Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 10 from a total of 10 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Set Treasury | 24501009 | 19 days ago | IN | 0 ETH | 0.00000251 | ||||
| Confirm Strategy... | 24501009 | 19 days ago | IN | 0 ETH | 0.00000161 | ||||
| Set Strategy Man... | 24501009 | 19 days ago | IN | 0 ETH | 0.00000247 | ||||
| Confirm Yield Cl... | 24501009 | 19 days ago | IN | 0 ETH | 0.00000159 | ||||
| Set Yield Claime... | 24501009 | 19 days ago | IN | 0 ETH | 0.00000251 | ||||
| Set Sub Yield Cl... | 24501008 | 19 days ago | IN | 0 ETH | 0.00000223 | ||||
| Set Circuit Brea... | 24501008 | 19 days ago | IN | 0 ETH | 0.00000222 | ||||
| Confirm Strategy | 24501008 | 19 days ago | IN | 0 ETH | 0.00000375 | ||||
| Set Strategy | 24501008 | 19 days ago | IN | 0 ETH | 0.00000363 | ||||
| Set Depositor | 24501008 | 19 days ago | IN | 0 ETH | 0.00000224 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60a08060 | 24501008 | 19 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
ftYieldWrapperV2
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Flying Tulip Inc. All rights reserved.
pragma solidity ^0.8.30;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {
IERC20Metadata,
IERC20
} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuardTransient} from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";
import {IStrategy, IStrategyWithQueue} from "@ft-common/interfaces/IStrategy.sol";
import {IftYieldWrapperV2} from "../interfaces/IftYieldWrapperV2.sol";
import {ProtectedContract} from "../cb/ProtectedContract.sol";
import {ICircuitBreakerV2} from "../cb/ICircuitBreakerV2.sol";
/// @title ftYieldWrapperV2
/// @notice Single-asset wrapper with CircuitBreakerV2 integration
/// @dev Replaces V1 CB integration with ProtectedContract mixin (fail-close pattern).
/// Fee-on-transfer (FOT) and rebasing tokens are NOT supported.
///
/// Key differences from V1:
/// - Uses ProtectedContract mixin for CB V2 integration
/// - Fail-close pattern: CB errors revert (no try/catch bypass)
/// - Deferred settlement: Large withdrawals may be queued in CB for later execution
/// - Withdrawals burn shares immediately; tokens go to user or CB (if queued)
contract ftYieldWrapperV2 is IftYieldWrapperV2, ERC20, ReentrancyGuardTransient, ProtectedContract {
using SafeERC20 for IERC20;
using SafeERC20 for IERC20Metadata;
uint256 public deployed; // default to 0
address public immutable token;
address public yieldClaimer;
address public pendingYieldClaimer;
address public strategyManager;
address public pendingStrategyManager;
address public treasury;
address public pendingTreasury;
address public subYieldClaimer;
address public putManager;
address public depositor;
IStrategy[] public strategies;
mapping(address strategy => uint256 deployed) public deployedToStrategy;
// must be submitted by strategyManager
address public pendingStrategy;
uint256 public delayStrategy;
/// @notice Timelock delay between setStrategy and confirmStrategy. Defaults to 0; set via setStrategyDelay.
uint256 public strategyDelayConfig;
/// @notice Minimum deposit amount. Defaults to 0 (no minimum). Set via setMinDepositAmount.
uint256 public minDepositAmount;
event Deployed(address strategy, uint256 allocation);
event YieldClaimed(address yieldClaimer, address token, uint256 amount);
event Deposit(address owner, uint256 amount);
event Withdraw(address owner, uint256 amount);
event WithdrawUnderlying(address owner, uint256 amount);
event QueuedToWrapper(address strategyManager, address strategy, uint256 amount);
event WithdrawToWrapper(address strategyManager, address strategy, uint256 amount);
event PendingYieldClaimer(address yieldClaimer, address pendingYieldClaimer);
event PendingStrategyManager(address strategyManager, address pendingStrategyManager);
event PendingTreasury(address treasury, address pendingTreasuy);
event PendingStrategy(address strategyManager, address pendingStrategy);
event UpdateYieldClaimer(address newYieldClaimer);
event UpdateStrategyManager(address newStrategyManager);
event UpdateTreasury(address newTreasury);
event UpdateSubYieldClaimer(address yieldClaimer, address subYieldClaimer);
event UpdatePutManager(address newPutManager);
event UpdateDepositor(address newDepositor);
event UpdateStrategyDelayConfig(uint256 newDelay);
event UpdateMinDepositAmount(uint256 newMinDepositAmount);
event AddedStrategy(address strategyManager, address strategy);
event RemovedStrategy(address strategyManager, address strategy);
event StrategiesReordered(address[] newOrder);
event YieldSwept(address caller, address token, uint256 amount);
/// @notice Emitted when a withdrawal is queued due to circuit breaker rate limiting
/// @param caller The address that initiated the withdrawal
/// @param recipient The address that will receive tokens after settlement
/// @param amount The amount queued
/// @param queueId The CB queue entry ID for claiming after settlement
event WithdrawQueued(
address indexed caller, address indexed recipient, uint256 amount, uint256 queueId
);
error ftYieldWrapperInsufficientLiquidity();
error ftYieldWrapperNotYieldClaimer();
error ftYieldWrapperNotYieldClaimers();
error ftYieldWrapperDelayNotExpired();
error ftYieldWrapperNotStrategyManager();
error ftYieldWrapperZeroAddress();
error ftYieldWrapperNotSetter();
error ftYieldWrapperNotConfirmer();
error ftYieldWrapperNotYieldClaimConfirmer();
error ftYieldWrapperSettingUnchanged();
error ftYieldWrapperNotStrategy();
error ftYieldWrapperNoYield();
error ftYieldWrapperInvalidStrategyIndex();
error ftYieldWrapperInvalidStrategiesOrder();
error ftYieldWrapperNotPutManagerOrDepositor();
error ftYieldWrapperFeeOnTransferUnsupported();
error ftYieldWrapperBelowMinDeposit(uint256 amount, uint256 minimum);
modifier onlyYieldClaimer() {
if (msg.sender != yieldClaimer) revert ftYieldWrapperNotYieldClaimer();
_;
}
modifier onlyStrategyManager() {
if (msg.sender != strategyManager) {
revert ftYieldWrapperNotStrategyManager();
}
_;
}
modifier onlyYieldClaimers() {
if (msg.sender != yieldClaimer && msg.sender != subYieldClaimer) {
revert ftYieldWrapperNotYieldClaimers();
}
_;
}
modifier onlyPutManagerOrDepositor() {
if (msg.sender != putManager && msg.sender != depositor) {
revert ftYieldWrapperNotPutManagerOrDepositor();
}
_;
}
/// @notice Creates a new yield wrapper for the given token
/// @dev Name and symbol default to "Flying Tulip {token.name()}" / "ft{token.symbol()}"
/// if empty strings are passed. Use custom names for engine-specific wrappers,
/// e.g., "FT_PmintUSDT" for PermissionedMint's USDT wrapper.
/// @param _token The underlying token address
/// @param _yieldClaimer Address that can claim yield and execute strategy calls
/// @param _strategyManager Address that can add/remove strategies and configure depositor
/// @param _treasury Address that receives claimed yield
/// @param _name Custom ERC20 name (empty string = auto-generate)
/// @param _symbol Custom ERC20 symbol (empty string = auto-generate)
constructor(
address _token,
address _yieldClaimer,
address _strategyManager,
address _treasury,
string memory _name,
string memory _symbol
)
ERC20(
bytes(_name).length > 0
? _name
: string.concat("Flying Tulip ", IERC20Metadata(_token).name()),
bytes(_symbol).length > 0
? _symbol
: string.concat("ft", IERC20Metadata(_token).symbol())
)
{
if (_token == address(0x0)) revert ftYieldWrapperZeroAddress();
if (_yieldClaimer == address(0x0)) revert ftYieldWrapperZeroAddress();
if (_strategyManager == address(0x0)) {
revert ftYieldWrapperZeroAddress();
}
if (_treasury == address(0x0)) revert ftYieldWrapperZeroAddress();
token = _token;
yieldClaimer = _yieldClaimer;
strategyManager = _strategyManager;
treasury = _treasury;
// putManager defaults to 0x0
// depositor defaults to 0x0
emit Transfer(address(0x0), address(this), 0);
}
// ===== ProtectedContract Overrides =====
function circuitBreaker()
public
view
override(IftYieldWrapperV2, ProtectedContract)
returns (address)
{
return _getCircuitBreaker();
}
/// @dev Returns the wrapper's TVL normalized to underlying decimals for CB rate limiting.
/// Assumes each strategy reports `valueOfCapital()` in its `positionToken()` units.
/// Converts each strategy leg from position-token decimals to underlying-token decimals
/// so CB preTvl and outflow accounting share a consistent unit basis.
/// For AaveStrategy this conversion is a no-op because token/aToken decimals are equal.
function _getTvl(address) internal view override returns (uint256 tvl) {
tvl = IERC20(token).balanceOf(address(this));
uint8 underlyingDec = IERC20Metadata(token).decimals();
uint256 len = strategies.length;
for (uint256 i = 0; i < len; i++) {
uint256 v = strategies[i].valueOfCapital();
uint8 posDec = IERC20Metadata(strategies[i].positionToken()).decimals();
if (posDec != underlyingDec) {
v = (v * 10 ** underlyingDec) / (10 ** posDec);
}
tvl += v;
}
}
// ===== Circuit Breaker Configuration =====
/// @notice Set or disable the circuit breaker (V2)
/// @dev Only callable by strategy manager. Set to address(0) to disable.
/// @param _circuitBreaker Address of circuit breaker V2, or address(0) to disable
function setCircuitBreaker(address _circuitBreaker) external onlyStrategyManager {
_setCircuitBreaker(_circuitBreaker);
}
// ===== Admin Functions =====
function setYieldClaimer(address _yieldClaimer) external onlyYieldClaimer {
if (_yieldClaimer == address(0x0)) revert ftYieldWrapperZeroAddress();
pendingYieldClaimer = _yieldClaimer;
emit PendingYieldClaimer(yieldClaimer, pendingYieldClaimer);
}
function setSubYieldClaimer(address _subYieldClaimer) external onlyYieldClaimer {
if (_subYieldClaimer == address(0x0)) {
revert ftYieldWrapperZeroAddress();
}
subYieldClaimer = _subYieldClaimer;
emit UpdateSubYieldClaimer(yieldClaimer, subYieldClaimer);
}
function confirmYieldClaimer() external {
if (msg.sender != treasury && msg.sender != strategyManager) {
revert ftYieldWrapperNotYieldClaimConfirmer();
}
if (pendingYieldClaimer == address(0x0)) {
revert ftYieldWrapperZeroAddress();
}
if (yieldClaimer == pendingYieldClaimer) {
revert ftYieldWrapperSettingUnchanged();
}
yieldClaimer = pendingYieldClaimer;
pendingYieldClaimer = address(0x0);
emit UpdateYieldClaimer(yieldClaimer);
}
function setStrategyManager(address _strategyManager) external onlyStrategyManager {
if (_strategyManager == address(0x0)) {
revert ftYieldWrapperZeroAddress();
}
pendingStrategyManager = _strategyManager;
emit PendingStrategyManager(strategyManager, pendingStrategyManager);
}
function confirmStrategyManager() external {
if (msg.sender != treasury && msg.sender != yieldClaimer) {
revert ftYieldWrapperNotConfirmer();
}
if (pendingStrategyManager == address(0x0)) {
revert ftYieldWrapperZeroAddress();
}
if (strategyManager == pendingStrategyManager) {
revert ftYieldWrapperSettingUnchanged();
}
strategyManager = pendingStrategyManager;
pendingStrategyManager = address(0x0);
emit UpdateStrategyManager(strategyManager);
}
function setTreasury(address _treasury) external {
if (msg.sender != treasury) revert ftYieldWrapperNotSetter();
if (_treasury == address(0x0)) revert ftYieldWrapperZeroAddress();
pendingTreasury = _treasury;
emit PendingTreasury(treasury, pendingTreasury);
}
function confirmTreasury() external {
if (msg.sender != strategyManager && msg.sender != yieldClaimer) {
revert ftYieldWrapperNotConfirmer();
}
if (pendingTreasury == address(0x0)) revert ftYieldWrapperZeroAddress();
if (treasury == pendingTreasury) {
revert ftYieldWrapperSettingUnchanged();
}
treasury = pendingTreasury;
pendingTreasury = address(0x0);
emit UpdateTreasury(treasury);
}
function setPutManager(address _putManager) external onlyStrategyManager {
if (_putManager == address(0x0)) revert ftYieldWrapperZeroAddress();
putManager = _putManager;
emit UpdatePutManager(_putManager);
}
function setDepositor(address _depositor) external onlyStrategyManager {
if (_depositor == address(0x0)) revert ftYieldWrapperZeroAddress();
depositor = _depositor;
emit UpdateDepositor(_depositor);
}
/// @notice Sets the minimum deposit amount. Use 0 to allow any amount.
/// @param _minDepositAmount The new minimum deposit amount in underlying token units
function setMinDepositAmount(uint256 _minDepositAmount) external onlyStrategyManager {
minDepositAmount = _minDepositAmount;
emit UpdateMinDepositAmount(_minDepositAmount);
}
function setStrategyDelay(uint256 _delay) external {
if (msg.sender != treasury) revert ftYieldWrapperNotConfirmer();
strategyDelayConfig = _delay;
emit UpdateStrategyDelayConfig(_delay);
}
// ===== Strategy Management =====
function setStrategy(address _strategy) external onlyStrategyManager {
if (_strategy == address(0x0)) revert ftYieldWrapperZeroAddress();
if (isStrategy(_strategy) || IStrategy(_strategy).token() != token) {
revert ftYieldWrapperNotStrategy();
}
uint256 effectiveTime = block.timestamp + strategyDelayConfig;
pendingStrategy = _strategy;
delayStrategy = effectiveTime;
emit PendingStrategy(strategyManager, pendingStrategy);
}
function confirmStrategy() external {
if (msg.sender != treasury) revert ftYieldWrapperNotConfirmer();
if (pendingStrategy == address(0x0)) revert ftYieldWrapperZeroAddress();
if (delayStrategy > block.timestamp) revert ftYieldWrapperDelayNotExpired();
strategies.push(IStrategy(pendingStrategy));
emit AddedStrategy(strategyManager, pendingStrategy);
// Ensure strategy wrapper pointer is set (no-op if already this wrapper)
try IStrategy(pendingStrategy).setftYieldWrapper(address(this)) {}
catch {
// strategies are expected to support this; ignore to avoid bricking
}
pendingStrategy = address(0x0);
delayStrategy = 0;
emit PendingStrategy(strategyManager, pendingStrategy);
}
/// @notice Remove a strategy whose wrapper share balance is zero.
function removeStrategy(uint256 index) external onlyStrategyManager {
uint256 len = strategies.length;
if (index >= len) {
revert ftYieldWrapperInvalidStrategyIndex();
}
IStrategy s = strategies[index];
if (deployedToStrategy[address(s)] != 0) {
revert ftYieldWrapperNotStrategy();
}
address removed = address(s);
// swap & pop
if (index != len - 1) {
strategies[index] = strategies[len - 1];
}
strategies.pop();
emit RemovedStrategy(msg.sender, removed);
}
function isStrategy(address _strategy) public view returns (bool) {
uint256 strategiesLength = strategies.length;
for (uint256 i = 0; i < strategiesLength; i++) {
if (address(strategies[i]) == _strategy) return true;
}
return false;
}
function setStrategiesOrder(address[] calldata _newOrder) external onlyStrategyManager {
uint256 currentLength = strategies.length;
if (_newOrder.length != currentLength) {
revert ftYieldWrapperInvalidStrategiesOrder();
}
for (uint256 i = 0; i < currentLength; i++) {
if (!isStrategy(_newOrder[i])) {
revert ftYieldWrapperInvalidStrategiesOrder();
}
for (uint256 j = i + 1; j < currentLength; j++) {
if (_newOrder[i] == _newOrder[j]) {
revert ftYieldWrapperInvalidStrategiesOrder();
}
}
}
for (uint256 i = 0; i < currentLength; i++) {
strategies[i] = IStrategy(_newOrder[i]);
}
emit StrategiesReordered(_newOrder);
}
// ===== Yield Management =====
function claimYield(address _strategy) external onlyYieldClaimers returns (uint256 _yield) {
if (!isStrategy(_strategy)) revert ftYieldWrapperNotStrategy();
_yield = IStrategy(_strategy).claimYield(treasury);
if (_yield == 0) revert ftYieldWrapperNoYield();
emit YieldClaimed(msg.sender, address(token), _yield);
}
function claimYields() external onlyYieldClaimers returns (uint256 _yield) {
uint256 strategiesLength = strategies.length;
address _treasury = treasury;
for (uint256 i = 0; i < strategiesLength; i++) {
_yield += IStrategy(strategies[i]).claimYield(_treasury);
}
if (_yield == 0) revert ftYieldWrapperNoYield();
emit YieldClaimed(msg.sender, address(token), _yield);
}
function sweepIdleYield() external nonReentrant onlyYieldClaimers returns (uint256 amount) {
uint256 idleBalance = IERC20(token).balanceOf(address(this));
uint256 liabilities = totalSupply();
if (idleBalance <= liabilities) revert ftYieldWrapperNoYield();
amount = idleBalance - liabilities;
IERC20(token).safeTransfer(treasury, amount);
emit YieldSwept(msg.sender, address(token), amount);
}
function execute(
address _strategy,
address to,
uint256 value,
bytes calldata data
)
external
onlyYieldClaimers
returns (bool success, bytes memory result)
{
if (!isStrategy(_strategy)) revert ftYieldWrapperNotStrategy();
return IStrategy(_strategy).execute(to, value, data);
}
// ===== View Functions =====
function numberOfStrategies() external view returns (uint256) {
return strategies.length;
}
function capital() external view returns (uint256) {
return totalSupply();
}
function valueOfCapital() public view returns (uint256 _capital) {
_capital = IERC20(token).balanceOf(address(this));
uint256 strategiesLength = strategies.length;
for (uint256 i = 0; i < strategiesLength; i++) {
_capital += strategies[i].valueOfCapital();
}
}
function yield() public view returns (uint256) {
uint256 _capital = valueOfCapital();
uint256 _totalSupply = totalSupply();
return (_capital > _totalSupply) ? (_capital - _totalSupply) : 0;
}
function availableToWithdraw(address strategy) public view returns (uint256 liquidity) {
if (!isStrategy(strategy)) return 0;
uint256 shares;
try IStrategy(strategy).balanceOf(address(this)) returns (uint256 sb) {
shares = sb;
} catch {
return 0;
}
try IStrategy(strategy).maxAbleToWithdraw(shares) returns (uint256 m) {
liquidity = m;
} catch {
return 0;
}
}
function availableToWithdraw() public view returns (uint256 liquidity) {
liquidity = IERC20(token).balanceOf(address(this));
uint256 strategiesLength = strategies.length;
for (uint256 i = 0; i < strategiesLength; i++) {
uint256 shares;
try strategies[i].balanceOf(address(this)) returns (uint256 sb) {
shares = sb;
} catch {
shares = 0;
}
if (shares != 0) {
try strategies[i].maxAbleToWithdraw(shares) returns (uint256 m) {
liquidity += m;
} catch {
// treat as zero
}
}
}
}
function canWithdraw(uint256 amount) external view returns (bool) {
return (availableToWithdraw() >= amount);
}
function maxAbleToWithdraw(uint256 amount) external view returns (uint256) {
uint256 _liquidity = availableToWithdraw();
return _liquidity > amount ? amount : _liquidity;
}
function availableToDeposit() external view returns (uint256) {
return IERC20(token).balanceOf(address(this));
}
// ===== Core Deposit/Withdraw Functions =====
/// @notice Deposit underlying tokens into the wrapper and receive wrapper tokens
/// @dev Records inflow with circuit breaker (fail-close pattern).
/// Fee-on-transfer tokens are NOT supported and will revert.
/// @param amount The amount of underlying tokens to deposit
function deposit(uint256 amount) external nonReentrant onlyPutManagerOrDepositor {
if (amount == 0) revert ftYieldWrapperInsufficientLiquidity();
uint256 _minDeposit = minDepositAmount;
if (_minDeposit != 0 && amount < _minDeposit) {
revert ftYieldWrapperBelowMinDeposit(amount, _minDeposit);
}
// Record inflow with circuit breaker (fail-close: will revert if CB errors)
// preTvl normalized to underlying decimals for consistent CB accounting
uint256 preTvl = _getTvl(token);
uint256 balBefore = IERC20(token).balanceOf(address(this));
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
if (IERC20(token).balanceOf(address(this)) != balBefore + amount) {
revert ftYieldWrapperFeeOnTransferUnsupported();
}
// Record inflow after successful transfer
_recordInflow(token, amount, preTvl);
_mint(msg.sender, amount);
emit Deposit(msg.sender, amount);
}
/// @notice Withdraw underlying tokens from the wrapper
/// @dev Uses protected transfer (fail-close pattern). Large withdrawals may be queued
/// in the circuit breaker for later settlement. Shares are burned immediately.
///
/// If withdrawal is queued:
/// - Shares are burned immediately
/// - Tokens are transferred to circuit breaker
/// - User must call CB.executeQueued(queueId) after settlement delay
///
/// @param amount The amount of underlying tokens to withdraw
/// @param to The recipient address (receives tokens immediately or after CB settlement)
/// @return queueId The CB queue entry ID if the withdrawal was queued, 0 if immediate
function withdraw(
uint256 amount,
address to
)
external
nonReentrant
onlyPutManagerOrDepositor
returns (uint256 queueId)
{
return _withdrawReturningQueueId(amount, to);
}
function _withdrawReturningQueueId(
uint256 amount,
address to
)
internal
returns (uint256 queueId)
{
if (to == address(0)) revert ftYieldWrapperZeroAddress();
if (amount == 0) revert ftYieldWrapperInsufficientLiquidity();
uint256 initialTarget = amount;
uint256 remaining = amount;
// 1) Use idle underlying on the wrapper first
uint256 idle = IERC20(token).balanceOf(address(this));
if (idle != 0) {
uint256 toTake = idle > remaining ? remaining : idle;
remaining -= toTake;
}
// 2) Drain strategies in order
uint256 _strategiesLength = strategies.length;
for (uint256 i = 0; i < _strategiesLength && remaining != 0; i++) {
uint256 shareBal;
try strategies[i].balanceOf(address(this)) returns (uint256 sb) {
shareBal = sb;
} catch {
continue;
}
if (shareBal == 0) {
continue;
}
uint256 avail;
try strategies[i].maxAbleToWithdraw(shareBal) returns (uint256 m) {
avail = m;
} catch {
continue;
}
if (avail == 0) {
continue;
}
uint256 toRequest = avail > remaining ? remaining : avail;
try strategies[i].withdraw(toRequest) returns (uint256 received) {
if (received != 0) {
uint256 currentDeployed = deployedToStrategy[address(strategies[i])];
uint256 toReduce = received > currentDeployed ? currentDeployed : received;
deployedToStrategy[address(strategies[i])] -= toReduce;
if (toReduce > deployed) {
deployed = 0;
} else {
deployed -= toReduce;
}
if (received > remaining) {
remaining = 0;
} else {
remaining -= received;
}
}
} catch {
// Skip failing strategies and continue
}
}
uint256 totalDelivered = initialTarget - remaining;
if (remaining != 0) {
revert ftYieldWrapperInsufficientLiquidity();
}
// Burn shares immediately (before transfer)
if (totalDelivered != 0) {
_burn(msg.sender, totalDelivered);
}
// Protected transfer: may be immediate or queued
// If CB not set: immediate transfer to `to`
// If CB set, within limit: immediate transfer to `to`
// If CB set, exceeds limit: transfer to CB, user claims later
queueId = _protectedTransfer(token, to, totalDelivered);
if (queueId > 0) {
emit WithdrawQueued(msg.sender, to, totalDelivered, queueId);
} else {
emit Withdraw(msg.sender, totalDelivered);
}
}
/// @dev Circuit breaker rate limits all position-token outflows against a single aggregate
/// bucket keyed by the wrapper's underlying `token`, preventing bypass via multiple
/// strategies. Large withdrawals may be queued in the CB for later settlement.
///
/// Assumes position tokens have an approximately 1:1 economic relationship with the
/// underlying token. The limiter consumes `limiterAmount` in underlying decimals, while
/// queue settlement transfers `got` in position-token decimals.
///
/// Strategy integration invariant:
/// - `valueOfCapital()` MUST be returned in `positionToken()` units.
/// - `withdrawUnderlying()` MUST return the actual `positionToken()` amount transferred.
///
/// Flow: Pull position tokens to wrapper -> protectedTransferWithLimiter to user/CB
///
/// Use case: When strategies cannot liquidate to underlying (e.g., paused).
///
/// @param amount The amount (in underlying/share terms) to withdraw
/// @param to The recipient address for position tokens
/// @return queueIds Compact array of CB queue entry IDs for withdrawals that were queued.
/// Only contains non-zero IDs; empty if all transfers were immediate.
function withdrawUnderlying(
uint256 amount,
address to
)
external
nonReentrant
onlyPutManagerOrDepositor
returns (uint256[] memory queueIds)
{
if (to == address(0)) revert ftYieldWrapperZeroAddress();
if (amount == 0) revert ftYieldWrapperInsufficientLiquidity();
// Capture preTvl BEFORE pulling position tokens from strategies.
// Normalized to underlying decimals for consistent CB rate-limit accounting.
uint256 preTvl = _getTvl(token);
uint256 initialTarget = amount;
uint256 remaining = amount;
uint256 _strategiesLength = strategies.length;
queueIds = new uint256[](_strategiesLength);
uint256 n; // write pointer for compact queueIds
for (uint256 i = 0; i < _strategiesLength && remaining != 0; ++i) {
uint256 shareBal = strategies[i].balanceOf(address(this));
if (shareBal == 0) continue;
// Track "asked" amount, not "got" - these are share/yield tokens
uint256 toExit = shareBal > remaining ? remaining : shareBal;
try strategies[i].withdrawUnderlying(toExit) returns (uint256 got) {
if (got != 0) {
// Update deployed tracking based on asked amount (share accounting)
uint256 currentDeployed = deployedToStrategy[address(strategies[i])];
uint256 toReduce = toExit > currentDeployed ? currentDeployed : toExit;
deployedToStrategy[address(strategies[i])] -= toReduce;
if (toReduce > deployed) {
deployed = 0;
} else {
deployed -= toReduce;
}
remaining -= toExit;
// Position tokens now in wrapper - send via protectedTransferWithTvl
// Use preTvl captured before internal token movements
address positionToken = strategies[i].positionToken();
// Normalize got to underlying decimals for rate-limit accounting.
// The actual transfer uses got (position-token decimals).
uint256 limiterAmount = got;
uint8 posDecimals = IERC20Metadata(positionToken).decimals();
uint8 underlyingDecimals = IERC20Metadata(token).decimals();
if (posDecimals != underlyingDecimals) {
limiterAmount = (got * 10 ** underlyingDecimals) / (10 ** posDecimals);
}
uint256 queueId = _protectedTransferWithTvlAndLimiter(
positionToken, to, got, preTvl, token, limiterAmount
);
if (queueId > 0) {
queueIds[n++] = queueId;
emit WithdrawQueued(msg.sender, to, got, queueId);
}
}
} catch {
// strategy doesn't support exit-in-position-token or failed; skip
}
}
// Truncate queueIds to only the `n` non-zero entries that were written.
// Memory arrays have fixed length; this overwrites the length slot in-place
// so the ABI encoder only returns the filled entries (empty array if n == 0).
assembly { mstore(queueIds, n) }
uint256 totalDelivered = initialTarget - remaining;
if (remaining != 0) {
revert ftYieldWrapperInsufficientLiquidity();
}
if (totalDelivered != 0) {
_burn(msg.sender, totalDelivered);
}
emit WithdrawUnderlying(msg.sender, totalDelivered);
}
// ===== Strategy Capital Management =====
function deploy(address strategy, uint256 amount) external nonReentrant onlyYieldClaimer {
if (!isStrategy(strategy)) revert ftYieldWrapperNotStrategy();
if (IERC20(token).balanceOf(address(this)) < amount) {
revert ftYieldWrapperInsufficientLiquidity();
}
IERC20(token).forceApprove(address(strategy), amount);
IStrategy(strategy).deposit(amount);
deployedToStrategy[strategy] += amount;
deployed += amount;
emit Deployed(strategy, amount);
}
function forceWithdrawToWrapper(
address strategy,
uint256 amount
)
external
nonReentrant
onlyYieldClaimer
{
if (!isStrategy(strategy)) revert ftYieldWrapperNotStrategy();
uint256 _withdrawn = IStrategy(strategy).withdraw(amount);
uint256 currentDeployed = deployedToStrategy[strategy];
uint256 toReduce = _withdrawn > currentDeployed ? currentDeployed : _withdrawn;
deployedToStrategy[strategy] -= toReduce;
if (toReduce > deployed) {
deployed = 0;
} else {
deployed -= toReduce;
}
emit WithdrawToWrapper(msg.sender, strategy, _withdrawn);
}
function withdrawQueued(
address strategy,
uint256 amount
)
external
nonReentrant
onlyYieldClaimer
returns (uint256 id)
{
if (!isStrategy(strategy)) revert ftYieldWrapperNotStrategy();
id = IStrategyWithQueue(strategy).withdrawQueued(amount);
emit QueuedToWrapper(msg.sender, strategy, amount);
}
function claimQueued(
address strategy,
uint256 id
)
external
nonReentrant
onlyYieldClaimer
returns (uint256 received)
{
if (!isStrategy(strategy)) revert ftYieldWrapperNotStrategy();
received = IStrategyWithQueue(strategy).claimQueued(id);
uint256 currentDeployed = deployedToStrategy[strategy];
uint256 toReduce = received > currentDeployed ? currentDeployed : received;
deployedToStrategy[strategy] -= toReduce;
if (toReduce > deployed) {
deployed = 0;
} else {
deployed -= toReduce;
}
emit WithdrawToWrapper(msg.sender, strategy, received);
}
function decimals() public view override(ERC20, IERC20Metadata) returns (uint8) {
return IERC20Metadata(token).decimals();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* Both values are immutable: they can only be set once during construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/// @inheritdoc IERC20
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/// @inheritdoc IERC20
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/// @inheritdoc IERC20
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation sets the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the `transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner`'s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity >=0.6.2;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
if (!_safeTransfer(token, to, value, true)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @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 {
if (!_safeTransferFrom(token, from, to, value, true)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _safeTransfer(token, to, value, false);
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _safeTransferFrom(token, from, to, value, false);
}
/**
* @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 {
if (!_safeApprove(token, spender, value, false)) {
if (!_safeApprove(token, spender, 0, true)) revert SafeERC20FailedOperation(address(token));
if (!_safeApprove(token, spender, value, true)) revert SafeERC20FailedOperation(address(token));
}
}
/**
* @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 relies 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 relies 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}.
* Oppositely, 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 `token.transfer(to, value)` call, 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 to The recipient of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeTransfer(IERC20 token, address to, uint256 value, bool bubble) private returns (bool success) {
bytes4 selector = IERC20.transfer.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(to, shr(96, not(0))))
mstore(0x24, value)
success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
}
}
/**
* @dev Imitates a Solidity `token.transferFrom(from, to, value)` call, 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 from The sender of the tokens
* @param to The recipient of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value,
bool bubble
) private returns (bool success) {
bytes4 selector = IERC20.transferFrom.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(from, shr(96, not(0))))
mstore(0x24, and(to, shr(96, not(0))))
mstore(0x44, value)
success := call(gas(), token, 0, 0x00, 0x64, 0x00, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
mstore(0x60, 0)
}
}
/**
* @dev Imitates a Solidity `token.approve(spender, value)` call, 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 spender The spender of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeApprove(IERC20 token, address spender, uint256 value, bool bubble) private returns (bool success) {
bytes4 selector = IERC20.approve.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(spender, shr(96, not(0))))
mstore(0x24, value)
success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (utils/ReentrancyGuardTransient.sol)
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._
*
* @custom:stateless
*/
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();
}
/**
* @dev A `view` only version of {nonReentrant}. Use to block view functions
* from being called, preventing reading from inconsistent contract state.
*
* CAUTION: This is a "view" modifier and does not change the reentrancy
* status. Use it only on view functions. For payable or non-payable functions,
* use the standard {nonReentrant} modifier instead.
*/
modifier nonReentrantView() {
_nonReentrantBeforeView();
_;
}
function _nonReentrantBeforeView() private view {
if (_reentrancyGuardEntered()) {
revert ReentrancyGuardReentrantCall();
}
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, REENTRANCY_GUARD_STORAGE.asBoolean().tload() will be false
_nonReentrantBeforeView();
// Any calls to nonReentrant after this point will fail
_reentrancyGuardStorageSlot().asBoolean().tstore(true);
}
function _nonReentrantAfter() private {
_reentrancyGuardStorageSlot().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 _reentrancyGuardStorageSlot().asBoolean().tload();
}
function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) {
return REENTRANCY_GUARD_STORAGE;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.30;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
interface IStrategy is IERC20Metadata {
event YieldClaimed(address yieldClaimer, address treasury, address token, uint256 amount);
event Deposit(address owner, uint256 amount);
event Withdraw(address owner, uint256 amount);
event WithdrawUnderlying(address owner, uint256 amount);
event UpdateftYieldWrapper(address newftYieldWrapper);
error StrategyNotYieldWrapper();
error StrategyZeroAddress();
error StrategyAmountZero();
error StrategyInsufficientLiquidity();
error StrategyCantInteractWithCoreAssets();
error StrategyCapitalMustNotChange();
function token() external view returns (address);
function valueOfCapital() external view returns (uint256);
function setftYieldWrapper(address _ftYieldWrapper) external;
function capital() external view returns (uint256);
function yield() external view returns (uint256);
function claimYield(address treasury) external returns (uint256);
function execute(
address to,
uint256 value,
bytes calldata data
)
external
returns (bool success, bytes memory result);
function availableToWithdraw() external view returns (uint256);
function maxAbleToWithdraw(uint256 amount) external view returns (uint256);
function withdraw(uint256 amount) external returns (uint256);
function deposit(uint256 amount) external;
/// @dev Address of the strategy's position token (e.g., aToken, stETH, etc)
function positionToken() external view returns (address);
/// @dev Burns wrapper's strategy shares and transfers `amount` of the position token to `to`.
/// Must return the actual amount sent (should be == amount for 1:1 strategies).
function withdrawUnderlying(uint256 amount) external returns (uint256 received);
}
interface IStrategyWithQueue is IStrategy {
event WithdrawQueued(address owner, uint256 amount, uint256 id);
event WithdrawClaimed(address owner, uint256 amount, uint256 id);
function withdrawQueued(uint256 amount) external returns (uint256 id);
function claimQueued(uint256 id) external returns (uint256 received);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Flying Tulip Inc. All rights reserved.
pragma solidity ^0.8.30;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
/// @title IftYieldWrapperV2
/// @notice Extended wrapper interface that surfaces queue IDs from withdrawals.
/// @dev Standalone interface — does NOT extend IftYieldWrapper because adding
/// return values to withdraw/withdrawUnderlying would create conflicting
/// selectors (Solidity treats return types as part of override resolution).
/// At the EVM level, function selectors are identical (return types don't
/// affect them), so existing callers using IftYieldWrapper-typed variables
/// continue to work at runtime.
interface IftYieldWrapperV2 is IERC20Metadata {
function token() external view returns (address);
function deposit(uint256 amount) external;
function canWithdraw(uint256 amount) external view returns (bool);
function maxAbleToWithdraw(uint256 amount) external view returns (uint256);
/// @notice Withdraw underlying tokens from the wrapper
/// @param amount The amount of underlying tokens to withdraw
/// @param to The recipient address
/// @return queueId The CB queue entry ID (0 if transfer was immediate)
function withdraw(uint256 amount, address to) external returns (uint256 queueId);
/// @notice Withdraw position tokens directly from strategies
/// @param amount The amount (in share terms) to withdraw
/// @param to The recipient address
/// @return queueIds Per-strategy CB queue entry IDs (0 if immediate or strategy skipped)
function withdrawUnderlying(
uint256 amount,
address to
)
external
returns (uint256[] memory queueIds);
function circuitBreaker() external view returns (address);
function setCircuitBreaker(address _circuitBreaker) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.30;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol";
import {ICircuitBreakerV2} from "./ICircuitBreakerV2.sol";
/// @title ProtectedContract
/// @notice Mixin for circuit breaker protected contracts (both upgradeable and non-upgradeable).
/// @dev Uses unstructured storage for the circuit breaker address, making it safe for:
/// - Non-upgradeable contracts (standard inheritance)
/// - Upgradeable contracts (no storage layout conflicts)
///
/// Fee-on-transfer (FOT) and rebasing tokens are NOT supported.
/// Using such tokens will result in incorrect accounting and potential loss of funds.
abstract contract ProtectedContract {
using SafeERC20 for IERC20;
// ===== Unstructured Storage =====
/// @dev keccak256("ft.circuitbreakerv2.storage.ProtectedContract.circuitBreaker") - 1
bytes32 internal constant _CIRCUIT_BREAKER_SLOT =
0x88194fc1127942401ead32f70ac4b1816d514a0e878b86370156febc2984ee85;
/// @notice Returns the circuit breaker address
/// @dev Virtual to allow overriding when inheriting from interfaces that declare circuitBreaker()
function circuitBreaker() public view virtual returns (address) {
return _getCircuitBreaker();
}
/// @dev Internal getter for circuit breaker address. Use in overrides to avoid duplicating storage access.
function _getCircuitBreaker() internal view returns (address) {
return StorageSlot.getAddressSlot(_CIRCUIT_BREAKER_SLOT).value;
}
// ===== Events =====
event CircuitBreakerUpdated(address indexed newCircuitBreaker);
event OutflowQueued(
uint256 indexed queueId, address indexed token, address indexed to, uint256 amount
);
// ===== Internal =====
function _setCircuitBreaker(address cb_) internal virtual {
StorageSlot.getAddressSlot(_CIRCUIT_BREAKER_SLOT).value = cb_;
emit CircuitBreakerUpdated(cb_);
}
function _recordInflow(address _token, uint256 _amount, uint256 _preTvl) internal {
address cb = circuitBreaker();
if (cb == address(0)) {
return;
}
ICircuitBreakerV2(cb).recordInflow(_token, _amount, _preTvl);
}
function _protectedTransfer(
address _token,
address _to,
uint256 _amount
)
internal
returns (uint256 queueId)
{
if (circuitBreaker() == address(0)) {
IERC20(_token).safeTransfer(_to, _amount);
return 0;
}
return _protectedTransferWithTvl(_token, _to, _amount, _getTvl(_token));
}
/// @notice Transfer with explicit preTvl for rate limiting
/// @dev Use when TVL must be captured before internal token movements
function _protectedTransferWithTvl(
address _token,
address _to,
uint256 _amount,
uint256 _preTvl
)
internal
returns (uint256 queueId)
{
address cb = circuitBreaker();
if (cb == address(0)) {
IERC20(_token).safeTransfer(_to, _amount);
return 0;
}
(bool immediate, uint256 _queueId) =
ICircuitBreakerV2(cb).attemptOutflow(_token, _to, _amount, _preTvl);
if (immediate) {
IERC20(_token).safeTransfer(_to, _amount);
return 0;
} else {
IERC20(_token).safeTransfer(cb, _amount);
emit OutflowQueued(_queueId, _token, _to, _amount);
return _queueId;
}
}
/// @notice Transfer with explicit preTvl and a separate limiter asset for rate limiting
/// @dev Use when the token being transferred differs from the rate-limit bucket key.
/// E.g., wrapper withdrawUnderlying uses the wrapper's `token` as limiter but
/// transfers individual strategy `positionToken`s.
/// @param _transferToken Token actually transferred
/// @param _to Recipient
/// @param _amount Amount of _transferToken to transfer
/// @param _preTvl TVL before the outflow (for capacity calculation)
/// @param _limiterAsset Asset used for rate-limit state (bucket key)
/// @param _limiterAmount Amount in _limiterAsset decimals for rate-limit accounting.
/// When position-token decimals differ from underlying, the caller must normalize.
function _protectedTransferWithTvlAndLimiter(
address _transferToken,
address _to,
uint256 _amount,
uint256 _preTvl,
address _limiterAsset,
uint256 _limiterAmount
)
internal
returns (uint256 queueId)
{
address cb = circuitBreaker();
if (cb == address(0)) {
IERC20(_transferToken).safeTransfer(_to, _amount);
return 0;
}
(bool immediate, uint256 _queueId) = ICircuitBreakerV2(cb)
.attemptOutflowWithLimiter(
_limiterAsset, _transferToken, _to, _limiterAmount, _amount, _preTvl
);
if (immediate) {
IERC20(_transferToken).safeTransfer(_to, _amount);
return 0;
} else {
IERC20(_transferToken).safeTransfer(cb, _amount);
emit OutflowQueued(_queueId, _transferToken, _to, _amount);
return _queueId;
}
}
// ===== Virtual =====
function _getTvl(address _token) internal view virtual returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.30;
import {IHealthcheck} from "./IHealthcheck.sol";
/// @title ICircuitBreakerV2
/// @notice Rate limiter with deferred settlement for protocol outflows
/// @dev Fee-on-transfer (FOT) and rebasing tokens are NOT supported.
/// Using such tokens will result in incorrect accounting and potential loss of funds.
interface ICircuitBreakerV2 {
// ===== Enums =====
enum QueueStatus {
Pending,
Paused
}
// ===== Structs =====
struct QueuedOutflow {
address token;
uint40 queuedAt;
uint40 settlesAt;
QueueStatus status;
address recipient;
uint64 recoveryEpoch;
uint256 amount;
}
struct LimiterConfig {
uint64 maxDrawRateWad;
uint48 mainWindow;
uint48 elasticWindow;
}
struct AssetHealth {
uint256 mainBuffer;
uint256 mainBufferCap;
uint256 elasticBuffer;
uint256 totalCapacity;
uint256 pendingOutflows;
uint256 utilizationBps;
bool isPaused;
}
// ===== Events =====
event Inflow(address indexed asset, uint256 amount, uint256 newTvl);
event OutflowImmediate(address indexed asset, address indexed to, uint256 amount);
event OutflowQueued(
uint256 indexed queueId,
address indexed asset,
address indexed recipient,
uint256 amount,
uint256 settlesAt
);
event OutflowLimiterContext(
uint256 indexed queueId, address indexed limiterAsset, address indexed transferToken
);
event OutflowExecuted(
uint256 indexed queueId, address indexed recipient, address indexed asset, uint256 amount
);
event OutflowPaused(uint256 indexed queueId, address indexed recipient);
event OutflowResumed(uint256 indexed queueId, address indexed recipient);
event OutflowSpedUp(uint256 indexed queueId, address indexed recipient, uint256 newSettlesAt);
event ConfigUpdated(address indexed asset, LimiterConfig config);
event DelayUpdated(uint256 newDelay);
event Paused(address indexed by);
event Unpaused();
event AssetPaused(address indexed asset, address indexed by);
event AssetUnpaused(address indexed asset);
event EmergencyRecovery(
address indexed asset, address indexed to, uint256 amount, uint64 newEpoch
);
event ProtectedContractUpdated(address indexed contract_, bool enabled);
event GuardianUpdated(address indexed newGuardian);
event EmergencyOverride(address indexed contract_, address indexed asset, uint256 amount);
event HealthcheckUpdated(address indexed newHealthcheck);
event OperatorUpdated(address indexed newOperator);
event RecipientWhitelistUpdated(address indexed recipient, bool enabled);
event ScopedLimiterUpdated(address indexed contract_, bool enabled);
event AssetTracked(address indexed asset);
// ===== Errors =====
error NotProtectedContract();
error NotScopedContract();
error NotAdmin();
error NotAdminOrGuardian();
error NotOperatorOrOwner();
error GloballyPaused();
error NotPaused();
error AssetPausedError(address asset);
error QueueNotFound(uint256 queueId);
error QueueNotSettled(uint256 queueId, uint256 settlesAt);
error QueueInvalidated(uint256 queueId, uint64 queueEpoch, uint64 currentEpoch);
error InvalidStatus(uint256 queueId, QueueStatus current, QueueStatus expected);
error InvalidConfig();
error ZeroAddress();
error ZeroAmount();
error HealthcheckFailed(uint256 queueId);
error InvalidSettleTime(uint256 queueId, uint256 currentSettlesAt, uint256 requestedSettlesAt);
error NoChange();
// ===== Core =====
function recordInflow(address asset, uint256 amount, uint256 preTvl) external;
function attemptOutflow(
address asset,
address to,
uint256 amount,
uint256 preTvl
)
external
returns (bool immediate, uint256 queueId);
/// @notice Rate-limit by `limiterAsset` but queue/transfer `transferToken`
/// @dev Use when the token being transferred differs from the rate-limit bucket key.
/// E.g., wrapper withdrawUnderlying uses the wrapper's `token` as limiter but
/// transfers individual strategy `positionToken`s.
/// `limiterAmount` is used for rate-limit accounting (in limiterAsset decimals).
/// `transferAmount` is stored in the queue and used for settlement transfers
/// (in transferToken decimals). They differ when decimals mismatch.
/// @param limiterAsset Asset address used for rate-limit state (bucket key)
/// @param transferToken Token actually transferred / queued
/// @param to Recipient
/// @param limiterAmount Amount in limiterAsset decimals for rate-limit accounting
/// @param transferAmount Amount in transferToken decimals for queue storage and settlement
/// @param preTvl TVL before the outflow (in limiterAsset decimals for consistent buffer sizing)
function attemptOutflowWithLimiter(
address limiterAsset,
address transferToken,
address to,
uint256 limiterAmount,
uint256 transferAmount,
uint256 preTvl
)
external
returns (bool immediate, uint256 queueId);
// ===== Settlement =====
function executeQueued(uint256 queueId) external;
function executeQueuedBatch(uint256[] calldata queueIds) external;
// ===== Views =====
function getQueueIdsByRecipient(address recipient)
external
view
returns (uint256[] memory queueIds);
function getQueuedByRecipient(
address recipient,
uint256 offset,
uint256 limit
)
external
view
returns (QueuedOutflow[] memory outflows);
function getQueued(uint256 queueId) external view returns (QueuedOutflow memory);
function isSettled(uint256 queueId) external view returns (bool);
function timeUntilSettled(uint256 queueId) external view returns (uint256 remaining);
function getAssetHealth(
address asset,
uint256 currentTvl
)
external
view
returns (AssetHealth memory health);
function withdrawalCapacity(address asset, uint256 currentTvl) external view returns (uint256);
/// @notice Check if an outflow would be immediate or queued (without recording)
/// @dev Use this for flows that cannot be queued (e.g., position token withdrawals)
/// @param asset The asset being withdrawn
/// @param amount The amount to check
/// @param currentTvl The current TVL for capacity calculation
/// @return wouldBeImmediate True if withdrawal would be immediate, false if would be queued
/// @return availableCapacity Current available withdrawal capacity
function checkOutflow(
address asset,
uint256 amount,
uint256 currentTvl
)
external
view
returns (bool wouldBeImmediate, uint256 availableCapacity);
/// @notice Get asset health for a specific contract's limiter state
/// @dev Falls back to shared state if contract is not scoped
function getAssetHealth(
address contract_,
address asset,
uint256 currentTvl
)
external
view
returns (AssetHealth memory health);
/// @notice Get withdrawal capacity for a specific contract's limiter state
/// @dev Falls back to shared state if contract is not scoped
function withdrawalCapacity(
address contract_,
address asset,
uint256 currentTvl
)
external
view
returns (uint256);
/// @notice Check if outflow would be immediate for a specific contract's limiter state
/// @dev Falls back to shared state if contract is not scoped
function checkOutflow(
address contract_,
address asset,
uint256 amount,
uint256 currentTvl
)
external
view
returns (bool wouldBeImmediate, uint256 availableCapacity);
function pendingOutflows(address asset) external view returns (uint256);
function settlementDelay() external view returns (uint256);
function paused() external view returns (bool);
function nextQueueId() external view returns (uint256);
function assetRecoveryEpoch(address asset) external view returns (uint64);
// ===== Enumeration Views =====
/// @notice Check if an address is a registered protected contract
function isProtectedContract(address contract_) external view returns (bool);
/// @notice Get all registered protected contracts
function getProtectedContracts() external view returns (address[] memory);
/// @notice Get count of registered protected contracts
function protectedContractCount() external view returns (uint256);
/// @notice Check if an asset has been tracked (had inflows/outflows)
function isTrackedAsset(address asset) external view returns (bool);
/// @notice Get all tracked assets
function getTrackedAssets() external view returns (address[] memory);
/// @notice Get count of tracked assets
function trackedAssetCount() external view returns (uint256);
/// @notice Check if a contract has scoped limiter state
function isScopedLimiter(address contract_) external view returns (bool);
/// @notice Check if an address is a whitelisted recipient
function isWhitelistedRecipient(address recipient) external view returns (bool);
/// @notice Get all whitelisted recipients
function getWhitelistedRecipients() external view returns (address[] memory);
/// @notice Get count of whitelisted recipients
function whitelistedRecipientCount() external view returns (uint256);
/// @notice Get comprehensive system status for monitoring dashboards
function getSystemStatus()
external
view
returns (
bool active,
address admin,
address guardianAddr,
uint256 delay,
uint256 numProtectedContracts,
uint256 numTrackedAssets,
uint256 numQueuedOutflows
);
// ===== Admin =====
function unpause() external;
function unpauseAsset(address asset) external;
/// @notice Speed up a queued outflow by reducing its settlement time
/// @param queueId The queue ID to speed up
/// @param newSettleDelay The new delay from now (0 = instant, settlesAt = block.timestamp)
/// @dev Reverts if newSettleDelay would result in a later settlesAt than current
function speedUp(uint256 queueId, uint256 newSettleDelay) external;
function resumeQueued(uint256 queueId) external;
function emergencyRecover(address asset, address to) external;
/// @notice Temporarily increase elastic buffer capacity to allow a specific withdrawal
/// @dev Writing lastUpdated discards pending main-buffer replenishment. Accepted: capacity
/// loss is conservative, `speedUp` is preferred for unblocking queued outflows, and
/// replenishment resumes on the next inflow/outflow.
/// @param asset The asset to increase capacity for
/// @param amount The amount to add to elastic buffer capacity
function emergencyOverride(address asset, uint256 amount) external;
/// @notice Temporarily increase elastic buffer capacity for a scoped contract's state
/// @dev Only works for contracts with scoped limiter enabled. Reverts if not registered or not scoped.
function emergencyOverrideScoped(address contract_, address asset, uint256 amount) external;
function setScopedLimiter(address contract_, bool enabled) external;
function setDelay(uint256 newDelay) external;
function setConfig(address asset, LimiterConfig calldata config) external;
function setDefaultConfig(LimiterConfig calldata config) external;
function setProtectedContract(address contract_, bool enabled) external;
function setGuardian(address newGuardian) external;
function setHealthcheck(address healthcheck_) external;
function setOperator(address operator_) external;
function setWhitelistedRecipient(address recipient, bool enabled) external;
// ===== Admin/Guardian =====
function pause() external;
function pauseAsset(address asset) external;
function pauseQueued(uint256 queueId) external;
// ===== Roles =====
function guardian() external view returns (address);
function healthcheck() external view returns (IHealthcheck);
function operator() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @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.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (interfaces/draft-IERC6093.sol)
pragma solidity >=0.8.4;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-721.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)
pragma solidity >=0.6.2;
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.3.0) (utils/TransientSlot.sol)
// This file was procedurally generated from scripts/generate/templates/TransientSlot.js.
pragma solidity ^0.8.24;
/**
* @dev Library for reading and writing value-types to specific transient storage slots.
*
* Transient slots are often used to store temporary values that are removed after the current transaction.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* * Example reading and writing values using transient storage:
* ```solidity
* contract Lock {
* using TransientSlot for *;
*
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542;
*
* modifier locked() {
* require(!_LOCK_SLOT.asBoolean().tload());
*
* _LOCK_SLOT.asBoolean().tstore(true);
* _;
* _LOCK_SLOT.asBoolean().tstore(false);
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library TransientSlot {
/**
* @dev UDVT that represents a slot holding an 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 represents 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 represents 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 represents 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 represents a slot holding a int256.
*/
type Int256Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Int256Slot.
*/
function asInt256(bytes32 slot) internal pure returns (Int256Slot) {
return Int256Slot.wrap(slot);
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(AddressSlot slot) internal view returns (address value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(AddressSlot slot, address value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(BooleanSlot slot) internal view returns (bool value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(BooleanSlot slot, bool value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Bytes32Slot slot) internal view returns (bytes32 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Bytes32Slot slot, bytes32 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Uint256Slot slot) internal view returns (uint256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Uint256Slot slot, uint256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Int256Slot slot) internal view returns (int256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Int256Slot slot, int256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC-1967 implementation slot:
* ```solidity
* contract ERC1967 {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct Int256Slot {
int256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Int256Slot` with member `value` located at `slot`.
*/
function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
/**
* @dev Returns a `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.30;
/// @title IHealthcheck
/// @notice Simple interface for system health verification at withdrawal execution
/// @dev Implementations define their own invariants specific to their system
interface IHealthcheck {
/// @notice Check if a queued withdrawal should be allowed to execute
/// @param queueId The queue entry ID being executed
/// @param token The token being withdrawn
/// @param recipient The withdrawal recipient
/// @param amount The withdrawal amount
/// @return healthy True if system invariants pass, false to block withdrawal
/// @dev MUST NOT revert under normal conditions - reverts block all withdrawals
function isHealthy(
uint256 queueId,
address token,
address recipient,
uint256 amount
)
external
view
returns (bool healthy);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)
pragma solidity >=0.4.16;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @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": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/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/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"@ft-common/=lib/ft-common-contracts/contracts/",
"murky/=lib/murky/src/",
"ds-test/=lib/ft-common-contracts/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
"ft-common-contracts/=lib/ft-common-contracts/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": false
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_yieldClaimer","type":"address"},{"internalType":"address","name":"_strategyManager","type":"address"},{"internalType":"address","name":"_treasury","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minimum","type":"uint256"}],"name":"ftYieldWrapperBelowMinDeposit","type":"error"},{"inputs":[],"name":"ftYieldWrapperDelayNotExpired","type":"error"},{"inputs":[],"name":"ftYieldWrapperFeeOnTransferUnsupported","type":"error"},{"inputs":[],"name":"ftYieldWrapperInsufficientLiquidity","type":"error"},{"inputs":[],"name":"ftYieldWrapperInvalidStrategiesOrder","type":"error"},{"inputs":[],"name":"ftYieldWrapperInvalidStrategyIndex","type":"error"},{"inputs":[],"name":"ftYieldWrapperNoYield","type":"error"},{"inputs":[],"name":"ftYieldWrapperNotConfirmer","type":"error"},{"inputs":[],"name":"ftYieldWrapperNotPutManagerOrDepositor","type":"error"},{"inputs":[],"name":"ftYieldWrapperNotSetter","type":"error"},{"inputs":[],"name":"ftYieldWrapperNotStrategy","type":"error"},{"inputs":[],"name":"ftYieldWrapperNotStrategyManager","type":"error"},{"inputs":[],"name":"ftYieldWrapperNotYieldClaimConfirmer","type":"error"},{"inputs":[],"name":"ftYieldWrapperNotYieldClaimer","type":"error"},{"inputs":[],"name":"ftYieldWrapperNotYieldClaimers","type":"error"},{"inputs":[],"name":"ftYieldWrapperSettingUnchanged","type":"error"},{"inputs":[],"name":"ftYieldWrapperZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"strategyManager","type":"address"},{"indexed":false,"internalType":"address","name":"strategy","type":"address"}],"name":"AddedStrategy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newCircuitBreaker","type":"address"}],"name":"CircuitBreakerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"allocation","type":"uint256"}],"name":"Deployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"queueId","type":"uint256"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"OutflowQueued","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"strategyManager","type":"address"},{"indexed":false,"internalType":"address","name":"pendingStrategy","type":"address"}],"name":"PendingStrategy","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"strategyManager","type":"address"},{"indexed":false,"internalType":"address","name":"pendingStrategyManager","type":"address"}],"name":"PendingStrategyManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"treasury","type":"address"},{"indexed":false,"internalType":"address","name":"pendingTreasuy","type":"address"}],"name":"PendingTreasury","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"yieldClaimer","type":"address"},{"indexed":false,"internalType":"address","name":"pendingYieldClaimer","type":"address"}],"name":"PendingYieldClaimer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"strategyManager","type":"address"},{"indexed":false,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"QueuedToWrapper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"strategyManager","type":"address"},{"indexed":false,"internalType":"address","name":"strategy","type":"address"}],"name":"RemovedStrategy","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"newOrder","type":"address[]"}],"name":"StrategiesReordered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newDepositor","type":"address"}],"name":"UpdateDepositor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMinDepositAmount","type":"uint256"}],"name":"UpdateMinDepositAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newPutManager","type":"address"}],"name":"UpdatePutManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newDelay","type":"uint256"}],"name":"UpdateStrategyDelayConfig","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newStrategyManager","type":"address"}],"name":"UpdateStrategyManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"yieldClaimer","type":"address"},{"indexed":false,"internalType":"address","name":"subYieldClaimer","type":"address"}],"name":"UpdateSubYieldClaimer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newTreasury","type":"address"}],"name":"UpdateTreasury","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newYieldClaimer","type":"address"}],"name":"UpdateYieldClaimer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"queueId","type":"uint256"}],"name":"WithdrawQueued","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"strategyManager","type":"address"},{"indexed":false,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawToWrapper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawUnderlying","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"yieldClaimer","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"YieldClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"YieldSwept","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"availableToDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"availableToWithdraw","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"availableToWithdraw","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"canWithdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"capital","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"circuitBreaker","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"claimQueued","outputs":[{"internalType":"uint256","name":"received","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"claimYield","outputs":[{"internalType":"uint256","name":"_yield","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimYields","outputs":[{"internalType":"uint256","name":"_yield","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"confirmStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"confirmStrategyManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"confirmTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"confirmYieldClaimer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delayStrategy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deploy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deployed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"deployedToStrategy","outputs":[{"internalType":"uint256","name":"deployed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"execute","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"forceWithdrawToWrapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"isStrategy","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"maxAbleToWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minDepositAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfStrategies","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingStrategy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingStrategyManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingYieldClaimer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"putManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"removeStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_circuitBreaker","type":"address"}],"name":"setCircuitBreaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_depositor","type":"address"}],"name":"setDepositor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minDepositAmount","type":"uint256"}],"name":"setMinDepositAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_putManager","type":"address"}],"name":"setPutManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_newOrder","type":"address[]"}],"name":"setStrategiesOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"setStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_delay","type":"uint256"}],"name":"setStrategyDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyManager","type":"address"}],"name":"setStrategyManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_subYieldClaimer","type":"address"}],"name":"setSubYieldClaimer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_treasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_yieldClaimer","type":"address"}],"name":"setYieldClaimer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"strategies","outputs":[{"internalType":"contract IStrategy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategyDelayConfig","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategyManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"subYieldClaimer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sweepIdleYield","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"valueOfCapital","outputs":[{"internalType":"uint256","name":"_capital","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"queueId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawQueued","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawUnderlying","outputs":[{"internalType":"uint256[]","name":"queueIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"yield","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"yieldClaimer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60a080604052346105ba57614797803803809161001c82856105be565b833981019060c0818303126105ba57610034816105e1565b91610041602083016105e1565b61004d604084016105e1565b9161005a606085016105e1565b60808501519094906001600160401b0381116105ba578261007c9183016105f5565b60a08201519092906001600160401b0381116105ba5761009c92016105f5565b81519091901561052957905b80511561049057905b8051906001600160401b0382116103935760035490600182811c92168015610486575b60208310146103755781601f849311610418575b50602090601f83116001146103b2575f926103a7575b50508160011b915f199060031b1c1916176003555b8051906001600160401b0382116103935760045490600182811c92168015610389575b60208310146103755781601f849311610307575b50602090601f83116001146102a1575f92610296575b50508160011b915f199060031b1c1916176004555b6001600160a01b03841615610287576001600160a01b0316908115610287576001600160a01b0316918215610287576001600160a01b03169283156102875760805260018060a01b0319600654161760065560018060a01b0319600854161760085560018060a01b0319600a541617600a556040515f81525f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203093a36040516141249081610673823960805181818161049401528181610eea01528181611055015281816111e2015281816112fc01528181611b4401528181611ce701528181611f4401528181612066015281816126cc01528181612e84015281816131de015281816135ec01526139ea0152f35b637695083160e11b5f5260045ffd5b015190505f80610160565b60045f9081528281209350601f198516905b8181106102ef57509084600195949392106102d7575b505050811b01600455610175565b01515f1960f88460031b161c191690555f80806102c9565b929360206001819287860151815501950193016102b3565b60045f529091507f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f840160051c8101916020851061036b575b90601f859493920160051c01905b81811061035d575061014a565b5f8155849350600101610350565b9091508190610342565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610136565b634e487b7160e01b5f52604160045260245ffd5b015190505f806100fe565b60035f9081528281209350601f198516905b81811061040057509084600195949392106103e8575b505050811b01600355610113565b01515f1960f88460031b161c191690555f80806103da565b929360206001819287860151815501950193016103c4565b60035f529091507fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f840160051c8101916020851061047c575b90601f859493920160051c01905b81811061046e57506100e8565b5f8155849350600101610461565b9091508190610453565b91607f16916100d4565b506040516395d89b4160e01b81525f816004816001600160a01b038a165afa801561051e5760226104f6916020935f916104fc575b5060405193849161199d60f21b828401528051918291018484015e81015f838201520301601f1981018352826105be565b906100b1565b61051891503d805f833e61051081836105be565b81019061064a565b5f6104c5565b6040513d5f823e3d90fd5b506040516306fdde0360e01b81525f816004816001600160a01b038a165afa801561051e57602d61059a916020935f916105a0575b506040519384916c023363cb4b733902a3ab634b81609d1b828401528051918291018484015e81015f838201520301601f1981018352826105be565b906100a8565b6105b491503d805f833e61051081836105be565b5f61055e565b5f80fd5b601f909101601f19168101906001600160401b0382119082101761039357604052565b51906001600160a01b03821682036105ba57565b81601f820112156105ba578051906001600160401b0382116103935760405192610629601f8401601f1916602001856105be565b828452602083830101116105ba57815f9260208093018386015e8301015290565b906020828203126105ba5781516001600160401b0381116105ba5761066f92016105f5565b9056fe60806040526004361015610011575f80fd5b5f3560e01c8062f714ce1461040e57806306fdde03146104095780630956e5a614610404578063095ea7b3146103ff5780630e845634146103fa57806316efd941146103f557806318160ddd1461030f5780631c30e682146103f057806323b872dd146103eb57806328593984146103e65780632a80cda3146103e15780632e8ebaae146103dc5780632ed6b75d146103d75780632f278fe8146103d25780633066bb27146103cd578063313ce567146103c857806333a100ca146103c357806339b70e38146103be578063465fc5d2146103b95780634956eaf0146103b45780634f5e8085146103af5780634f908e7f146103aa57806357c44f8e146103a557806358e75440146103a057806359746aab1461039b5780635985366f146103965780635c966646146103915780635d83f0931461038c5780635e1816551461038757806361d027b314610382578063645006ca1461037d57806370a082311461037857806375efcf771461037357806378e808d51461036e5780637a801f53146103695780637b9282b01461036457806382beee891461035f57806384afe9f01461035a57806389fe02cb146103555780638c05b47214610350578063958da9181461034b57806395d89b4114610346578063999927df1461034157806399f428cf1461033c5780639aefaff814610337578063a25db8ce14610332578063a9059cbb1461032d578063aec48f5014610328578063afa9294514610323578063b6b55f251461031e578063c0cbbca614610319578063c7c4ff4614610314578063d211fd181461030f578063d574ea3d1461030a578063d8b5138a14610305578063dd62ed3e14610300578063e322ad2b146102fb578063e357e92e146102f6578063edf1d2b3146102f1578063f0f44260146102ec578063f2c098b7146102e7578063f905c15a146102e2578063fbe85f06146102dd5763fc0c546a146102d8575f80fd5b612e6f565b612e47565b612e2a565b612dab565b612d15565b612c38565b612b2a565b612b10565b612ab1565b612a18565b6129be565b610b11565b612965565b612839565b61267e565b612648565b61256b565b612536565b61250f565b612487565b61200d565b611ea8565b611df3565b611db8565b611ca1565b611c87565b611c2f565b611baf565b611b19565b611af1565b611ac9565b611a30565b6119f6565b6119d9565b6119b1565b6118a0565b611883565b6117ea565b6117cd565b611770565b6116f1565b611591565b611469565b611441565b611295565b61126d565b611245565b6110cc565b611030565b611008565b610e8e565b610e66565b610e35565b610dce565b610d87565b610cdf565b610b2e565b610add565b610ab5565b6109f8565b6109db565b6108ec565b610428565b6001600160a01b0381160361042457565b5f80fd5b346104245760403660031901126104245760243560043561044882610413565b61045061371b565b600d546001600160a01b031633141580610895575b610886576001600160a01b0382168015610877578115610814576040516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000938392916020816024816001600160a01b038a165afa908115610872575f91610853575b508061082c575b50600f54935f5b85811080610823575b1561074e57610523610517610502836129a1565b905460039190911b1c6001600160a01b031690565b6001600160a01b031690565b6040516370a0823160e01b815230600482015290602090829060249082905afa5f918161072e575b5061055f575061055a906132d4565b6104e5565b801561060f57602061059a9161057a610517610502866129a1565b604051808095819463afa9294560e01b8352600483019190602083019252565b03915afa5f918161070e575b506105b5575061055a906132d4565b801561060f578581111561070257505f60206105fd875b6105db610517610502876129a1565b90604051948580948193632e1a7d4d60e01b8352600483019190602083019252565b03925af15f91816106d2575b50610619575b5061055a906132d4565b801561060f57909461064c610633610517610502896129a1565b6001600160a01b03165f90815260106020526040902090565b54808311156106cb575b6106686106336105176105028a6129a1565b610673828254612f2a565b9055600554808211156106b457505061068b5f600555565b808211156106a157505061055a5f945b9061060f565b61055a916106ae91612f2a565b9461069b565b6106c6916106c191612f2a565b600555565b61068b565b5081610656565b6106f491925060203d81116106fb575b6106ec8183612ec7565b810190612eee565b905f610609565b503d6106e2565b60206105fd5f926105cc565b61072791925060203d81116106fb576106ec8183612ec7565b905f6105a6565b61074791925060203d81116106fb576106ec8183612ec7565b905f61054b565b508594508361075c91612f2a565b926108145782610774916107c8958261080557613f5a565b9182156107cc57604080519182526020820184905233917f7f842c71573581ef84f3dc1e26292618e76438c9da374b4bceaa48eee30f2bd19190a35b6107b8613776565b6040519081529081906020820190565b0390f35b6040805133815260208101929092527f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364925090a16107b0565b61080f8333613d58565b613f5a565b638dc3160f60e01b5f5260045ffd5b508415156104ee565b9092508381111561084a5750610843835b84612f2a565b915f6104de565b6108439061083d565b61086c915060203d6020116106fb576106ec8183612ec7565b5f6104d7565b612efd565b637695083160e11b5f5260045ffd5b630a4c898360e31b5f5260045ffd5b50600e546001600160a01b0316331415610465565b5f91031261042457565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9060206108e99281815201906108b4565b90565b34610424575f366003190112610424576040515f6003548060011c90600181169081156109d1575b6020831082146109bd5782855260208501919081156109a45750600114610952575b6107c88461094681860382612ec7565b604051918291826108d8565b60035f9081529250907fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b8184106109905750500161094682610936565b80548484015260209093019260010161097d565b60ff191682525090151560051b01905061094682610936565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610914565b34610424575f366003190112610424576020600f54604051908152f35b3461042457604036600319011261042457600435610a1581610413565b6024353315610aa2576001600160a01b038216918215610a8f57610a558291335f52600160205260405f209060018060a01b03165f5260205260405f2090565b5560405190815233907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3602060405160018152f35b634a1406b160e11b5f525f60045260245ffd5b63e602df0560e01b5f525f60045260245ffd5b34610424575f366003190112610424576009546040516001600160a01b039091168152602090f35b34610424575f366003190112610424575f5160206141045f395f51905f52546040516001600160a01b039091168152602090f35b34610424575f366003190112610424576020600254604051908152f35b3461042457604036600319011261042457600435610b4b81610413565b60243590610b5761371b565b6006546001600160a01b03163303610cd057610b79610b7582612f37565b1590565b610cc15760405163336ecbed60e21b815260048101929092526020826024815f6001600160a01b0386165af1908115610872576107c8925f92610c7e575b506001600160a01b0381165f9081526010602052604090207f77a629eef1a2b0e39a9b09a71ddafbded84fb873712d56f5b2f6b1e1706e266b9183915480831115610c77575b6001600160a01b0382165f908152601060205260409020610c1f828254612f2a565b905560055480821115610c65575050610c375f600555565b604080513381526001600160a01b03909216602083015281019190915280606081015b0390a16107b8613776565b610c72916106c191612f2a565b610c37565b5081610bfd565b7f77a629eef1a2b0e39a9b09a71ddafbded84fb873712d56f5b2f6b1e1706e266b919250610cba9060203d6020116106fb576106ec8183612ec7565b9190610bb7565b63741f966f60e11b5f5260045ffd5b63bbab121360e01b5f5260045ffd5b3461042457606036600319011261042457600435610cfc81610413565b602435610d0881610413565b6001600160a01b0382165f9081526001602090815260408083203384529091529020549160443591905f198410610d50575b610d44935061379b565b60405160018152602090f35b828410610d6c57610d6783610d4495033383613fa1565b610d3a565b8284637dc7a0d960e11b5f523360045260245260445260645ffd5b34610424575f36600319011261042457610d9f6131bb565b60025480821115610dc4578103908111610dbf576020905b604051908152f35b612f08565b505060205f610db7565b3461042457602036600319011261042457600854600435906001600160a01b03163303610e26576020817fbe19bc97d2bca5ac16c999faf6d2fbde29b13fae7020e3a390febf030f8242d792601455604051908152a1005b632602fe1160e21b5f5260045ffd5b34610424576020366003190112610424576020610e5c600435610e5781610413565b612f37565b6040519015158152f35b34610424575f36600319011261042457600b546040516001600160a01b039091168152602090f35b34610424575f366003190112610424576006546001600160a01b031633141580610ff3575b610fe457600f54600a545f9182916001600160a01b03165b818310610f6457838015610f5557604080513381526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660208201529081018290526107c891907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd79080606081015b0390a16040519081529081906020820190565b631492cf1b60e21b5f5260045ffd5b909192610f76610517610502866129a1565b60405163999927df60e01b81526001600160a01b038416600482015290602090829060249082905f905af1801561087257600192610fbb925f92610fc4575b50612fa4565b93019190610ecb565b610fdd91925060203d81116106fb576106ec8183612ec7565b905f610fb5565b637158b7f360e01b5f5260045ffd5b50600c546001600160a01b0316331415610eb3565b34610424575f36600319011261042457600c546040516001600160a01b039091168152602090f35b34610424575f3660031901126104245760405163313ce56760e01b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610872575f9161109d575b5060405160ff9091168152602090f35b6110bf915060203d6020116110c5575b6110b78183612ec7565b810190612fb1565b5f61108d565b503d6110ad565b34610424576020366003190112610424576004356110e981610413565b6008546001600160a01b03163303610e26576001600160a01b03811680156108775761111482612f37565b9081156111b6575b50610cc1576111579061115261113460135442612fa4565b9160018060a01b03166001600160601b0360a01b6011541617601155565b601255565b6008547ffdcf3907978ffc608fc7f27e245c3d5d995700556bdc27685b2182a48b9b7e72906001600160a01b03166011546001600160a01b03165b604080516001600160a01b039384168152929091166020830152819081015b0390a1005b604051637e062a3560e11b81529150602090829060049082905afa908115610872575f91611216575b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03908116911614155f61111c565b611238915060203d60201161123e575b6112308183612ec7565b810190612fca565b5f6111df565b503d611226565b34610424575f366003190112610424576008546040516001600160a01b039091168152602090f35b34610424575f366003190112610424576011546040516001600160a01b039091168152602090f35b34610424576040366003190112610424576004356112b281610413565b6024356112bd61371b565b6006546001600160a01b03163303610cd0576112db610b7583612f37565b610cc1576040516370a0823160e01b81523060048201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690602081602481855afa80156108725783915f91611422575b501061081457818361134692613897565b6001600160a01b03821691823b156104245760405163b6b55f2560e01b815260048101839052925f908490602490829084905af1928315610872577fb03c53b28e78a88e31607a27e1fa48234dce28d5d9d9ec7b295aeb02e674a1e193611408575b506001600160a01b0381165f9081526010602052604090206113cb838254612fa4565b90556113dc6106c183600554612fa4565b604080516001600160a01b039092168252602082019290925290819081015b0390a1611406613776565b005b806114165f61141c93612ec7565b806108aa565b5f6113a8565b61143b915060203d6020116106fb576106ec8183612ec7565b5f611335565b34610424575f36600319011261042457600d546040516001600160a01b039091168152602090f35b34610424575f36600319011261042457600a5461148e906001600160a01b0316610517565b3303611582576011546001600160a01b03168015610877576012544210611573576114c1906001600160a01b0316612fdf565b600854601154604080516001600160a01b039384168152929091166020830181905261151a92610517927f5196e6e427329801067f2d2174593bfbda5a246c506762f55fc401ee6084845e91a16001600160a01b031690565b803b156104245760405162844ae160e71b8152306004820152905f908290602490829084905af161155f575b601180546001600160a01b03191690556111575f601255565b806114165f61156d93612ec7565b5f611546565b6330b068d360e21b5f5260045ffd5b631fe9dba560e21b5f5260045ffd5b34610424576040366003190112610424576004356115ae81610413565b602435906115ba61371b565b6006546001600160a01b03163303610cd0576115d8610b7582612f37565b610cc157604051632e1a7d4d60e01b815260048101929092526020826024815f6001600160a01b0386165af1908115610872577f77a629eef1a2b0e39a9b09a71ddafbded84fb873712d56f5b2f6b1e1706e266b925f926116d0575b506001600160a01b0381165f90815260106020526040902054808311156116c9575b6001600160a01b0382165f908152601060205260409020611678828254612f2a565b9055600554808211156116b75750506116905f600555565b604080513381526001600160a01b03909216602083015281019190915280606081016113fb565b6116c4916106c191612f2a565b611690565b5081611656565b6116ea91925060203d6020116106fb576106ec8183612ec7565b905f611634565b346104245760203660031901126104245760043561170e81610413565b6008546001600160a01b03163303610e26576001600160a01b03168015610877576020817fcac43e3f2c27e1b165ed9d6100079705fcc1d17d86c9588a65b3b21af19efb55926001600160601b0360a01b600d541617600d55604051908152a1005b346104245760203660031901126104245760043567ffffffffffffffff8111610424573660238201121561042457806004013567ffffffffffffffff8111610424573660248260051b84010111610424576024611406920161304c565b34610424575f366003190112610424576020601354604051908152f35b346104245760203660031901126104245760043561180781610413565b6008546001600160a01b03169033829003610e26576001600160a01b0316801561087757600980546001600160a01b03191682179055604080516001600160a01b0393841681529290911660208301527f229906f503462bb4f822a3a5c8a143331ba9ca0b25dd7de323cf57b842eb0c849190819081016111b1565b34610424575f366003190112610424576020601254604051908152f35b34610424575f36600319011261042457600a546118c5906001600160a01b0316610517565b33141580611993575b611984576007546001600160a01b03168015610877576006546001600160a01b0382811691161461197557600680546001600160a01b0319166001600160a01b03909216919091179055600780546001600160a01b03191690556006547f6c6266c4ca096dc11256cb3c5a2f481d4ebfc848b647e04ba395e5e02cde9a1d906111b1906001600160a01b03165b6040516001600160a01b0390911681529081906020820190565b63140d058560e11b5f5260045ffd5b637acfd59760e01b5f5260045ffd5b506008546119a9906001600160a01b0316610517565b3314156118ce565b34610424575f36600319011261042457600a546040516001600160a01b039091168152602090f35b34610424575f366003190112610424576020601454604051908152f35b3461042457602036600319011261042457600435611a1381610413565b60018060a01b03165f525f602052602060405f2054604051908152f35b3461042457602036600319011261042457600435611a4d81610413565b6006546001600160a01b03169033829003610cd0576001600160a01b0316801561087757600c80546001600160a01b03191682179055604080516001600160a01b0393841681529290911660208301527f68d1aa5e9bdbf0db52fe140cc136ad3a30d9010e7d2f048eaf1ab0dad87c59759190819081016111b1565b34610424575f366003190112610424576006546040516001600160a01b039091168152602090f35b34610424575f366003190112610424576007546040516001600160a01b039091168152602090f35b34610424575f366003190112610424576040516370a0823160e01b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa8015610872576107c8915f91611b90575b506040519081529081906020820190565b611ba9915060203d6020116106fb576106ec8183612ec7565b5f611b7f565b3461042457602036600319011261042457600435611bcc81610413565b6008546001600160a01b03163303610e26575f5160206141045f395f51905f5280546001600160a01b0319166001600160a01b039290921691821790557f576d39e65c913b7fedc16b28e8563e891fb6d44bcf0c1c29495375595cd79b655f80a2005b3461042457602036600319011261042457600a54600435906001600160a01b03163303611582576020817fcffacb86a783dc4940b8d1e3677131e283bc556cbd9978ba4ce78272df1550a692601355604051908152a1005b34610424575f366003190112610424576020610db76131bb565b34610424575f36600319011261042457611cb961371b565b6006546001600160a01b031633141580611da3575b610fe4576040516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b038216602082602481845afa918215610872575f92611d82575b506002549081831115610f55576107c893611d677fe33b3f29026207fc167007711d41e9edd78750b0051defa03f1e4ea002accd6a93610c3795612f2a565b93848093611d7c600a5460018060a01b031690565b90613950565b611d9c91925060203d6020116106fb576106ec8183612ec7565b905f611d28565b50600c546001600160a01b0316331415611cce565b3461042457602036600319011261042457600435611dd581610413565b60018060a01b03165f526010602052602060405f2054604051908152f35b34610424575f366003190112610424576040515f6004548060011c9060018116908115611e9e575b6020831082146109bd5782855260208501919081156109a45750600114611e4c576107c88461094681860382612ec7565b60045f9081529250907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b818410611e8a5750500161094682610936565b805484840152602090930192600101611e77565b91607f1691611e1b565b3461042457602036600319011261042457600435611ec581610413565b6006546001600160a01b031633141580611fbf575b610fe457611eea610b7582612f37565b610cc157600a5460405163999927df60e01b81526001600160a01b03918216600482015291602091839160249183915f91165af1908115610872575f91611fa0575b508015610f5557604080513381526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660208201529081018290526107c891907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd7908060608101610f42565b611fb9915060203d6020116106fb576106ec8183612ec7565b5f611f2c565b50600c546001600160a01b0316331415611eda565b60206040818301928281528451809452019201905f5b818110611ff75750505090565b8251845260209384019390920191600101611fea565b346104245760403660031901126104245760243560043561202d82610413565b61203561371b565b600d546001600160a01b031633141580612459575b610886576001600160a01b0382168015610877578115610814577f000000000000000000000000000000000000000000000000000000000000000061208d6139cd565b928091600f549461209d866132a2565b915f935f5b88811080612450575b156123d0576120bf610517610502836129a1565b6040516370a0823160e01b815230600482015290602090829060249082905afa908115610872575f916123b2575b5080156123a857878111156123a35750865b61210e610517610502846129a1565b6040516301071a2960e41b8152600481018390529190602090839060249082905f905af15f9281612383575b50612150575b505061214b906132d4565b6120a2565b8115612140576121b5908392996121716106336105176105026004986129a1565b548083111561237c575b61218d610633610517610502886129a1565b612198828254612f2a565b90556005548082111561236f5750506121b05f600555565b612f2a565b9760206121c7610517610502856129a1565b604051635037b7e960e01b815294859182905afa928315610872575f9361234f575b5060405163313ce56760e01b81529281906020856004816001600160a01b0385165afa948515610872575f9561232f575b5060405163313ce56760e01b8152926020846004816001600160a01b038c165afa958615610872578f848f9691612270958c9461214b9b8f955f91612311575b5060ff811660ff8316036122e2575b5050613c06565b8981612282575b505050819250612140565b817f7f842c71573581ef84f3dc1e26292618e76438c9da374b4bceaa48eee30f2bd1926122bc6122b56122d6949e6132d4565b9d8d613324565b526040805194855260208501919091523393918291820190565b0390a3885f8089612277565b6123099297506122fd6122f7612303926132e2565b866132f3565b916132e2565b90613306565b945f80612269565b612329915060203d81116110c5576110b78183612ec7565b5f61225a565b61234891955060203d81116110c5576110b78183612ec7565b935f61221a565b61236891935060203d811161123e576112308183612ec7565b915f6121e9565b6121b0916106c191612f2a565b508161217b565b61239c91935060203d81116106fb576106ec8183612ec7565b915f61213a565b6120ff565b5061214b906132d4565b6123ca915060203d81116106fb576106ec8183612ec7565b5f6120ed565b86856123df82858a8452612f2a565b91610814577fe66f2a5f859e75187ec8381862cf2edc4124d6ea89f5fa1a7b66035121bd446161242a836107c894612441575b60408051338152602081019290925290918291820190565b0390a1612435613776565b60405191829182611fd4565b61244b8133613d58565b612412565b508615156120ab565b50600e546001600160a01b031633141561204a565b6040906108e993921515815281602082015201906108b4565b34610424576080366003190112610424576004356124a481610413565b6024356124b081610413565b6064359160443567ffffffffffffffff841161042457366023850112156104245783600401359267ffffffffffffffff84116104245736602485870101116104245760246124ff9501926133c7565b906107c86040519283928361246e565b34610424576020366003190112610424576020610db760043561253181610413565b61349b565b346104245760403660031901126104245761256060043561255681610413565b602435903361379b565b602060405160018152f35b34610424575f36600319011261042457600a54612590906001600160a01b0316610517565b3314158061262a575b611582576009546001600160a01b03168015610877576008546001600160a01b0382811691161461197557600880546001600160a01b0319166001600160a01b03909216919091179055600980546001600160a01b03191690556008547f1d71beb956329ce4bda4b6a3cf409eae37dca995fe985253f39c49f5dd3bf3c7906111b1906001600160a01b031661195b565b50600654612640906001600160a01b0316610517565b331415612599565b346104245760203660031901126104245760206004356126666135c9565b818111156126775750604051908152f35b9050610db7565b346104245760203660031901126104245760043561269a61371b565b600d546001600160a01b031633141580612824575b610886578015610814576014548015158061281b575b61280357507f0000000000000000000000000000000000000000000000000000000000000000906126f46139cd565b6040516370a0823160e01b81523060048201529091906001600160a01b038416602082602481845afa918215610872575f926127e2575b5061273883303384613e03565b6040516370a0823160e01b815230600482015290602090829060249082905afa9081156108725783905f926127bf575b506127739192612fa4565b036127b1576127a76113fb92827fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c95613e6a565b6124128133613eeb565b626d480360e31b5f5260045ffd5b61277392506127dc9060203d6020116106fb576106ec8183612ec7565b91612768565b6127fc91925060203d6020116106fb576106ec8183612ec7565b905f61272b565b63be8c6f6360e01b5f5260049190915260245260445ffd5b508082106126c5565b50600e546001600160a01b03163314156126af565b34610424576020366003190112610424576004356008546001600160a01b03163303610e2657600f5490818110156129565761288e612877826129a1565b90546001600160a01b039260039290921b1c821690565b166001600160a01b0381165f90815260106020526040902054610cc157826111b1926128da7f206dcdb9e6d573bb19797ecdc31f426bc49808caf16879b86cee1ba682e3881f95612f1c565b810361290b575b50506128eb613562565b604080513381526001600160a01b03909216602083015290918291820190565b61292b61292561050261292061294f95612f1c565b6129a1565b916129a1565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b5f806128e1565b635979b5d360e01b5f5260045ffd5b34610424575f36600319011261042457600e546040516001600160a01b039091168152602090f35b634e487b7160e01b5f52603260045260245ffd5b600f548110156129b957600f5f5260205f2001905f90565b61298d565b3461042457602036600319011261042457600435600f5481101561042457600f5f527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac80201546040516001600160a01b039091168152602090f35b3461042457602036600319011261042457600435612a3581610413565b6006546001600160a01b03169033829003610cd0576001600160a01b0316801561087757600780546001600160a01b03191682179055604080516001600160a01b0393841681529290911660208301527f9199036abbe383a5a535e02e8f65421079bb3fa71d03495a107b8b0231b616f29190819081016111b1565b34610424576040366003190112610424576020612b07600435612ad381610413565b60243590612ae082610413565b60018060a01b03165f526001835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b34610424575f366003190112610424576020610db76135c9565b3461042457604036600319011261042457600435612b4781610413565b60243590612b5361371b565b6006546001600160a01b03163303610cd057612b71610b7582612f37565b610cc15760405163cb27acfd60e01b815260048101839052916020836024815f6001600160a01b0387165af1908115610872576107c8935f92612bf7575b50604080513381526001600160a01b039094166020850152830152907fb093bd21cacddf175c466449524962624f8edabfc8497212c45b6ce70c90bfe2908060608101610c5a565b7fb093bd21cacddf175c466449524962624f8edabfc8497212c45b6ce70c90bfe29250612c329060203d6020116106fb576106ec8183612ec7565b91612baf565b34610424575f36600319011261042457600854612c5d906001600160a01b0316610517565b33141580612cf7575b61158257600b546001600160a01b0316801561087757600a546001600160a01b0382811691161461197557600a80546001600160a01b0319166001600160a01b03909216919091179055600b80546001600160a01b0319169055600a547f1f54d231bb9d500b1923e4a1cb25e600f366a8368873d9af7c1c623814df19fc906111b1906001600160a01b031661195b565b50600654612d0d906001600160a01b0316610517565b331415612c66565b3461042457602036600319011261042457600435612d3281610413565b600a54906001600160a01b0382163303612d9c576001600160a01b031690811561087757600b80546001600160a01b031916831790557f74826a97c225ebfc6ae4d01aa55f9846d00bb141717acca3947f3778534be5a1916001600160a01b039182169116611192565b636b7bc45160e01b5f5260045ffd5b3461042457602036600319011261042457600435612dc881610413565b6008546001600160a01b03163303610e26576001600160a01b03168015610877576020817ff112f2fd3f9db4cbbd348d7d8948ad19988985c74b787d6f0fb8f62ba25f9e0e926001600160601b0360a01b600e541617600e55604051908152a1005b34610424575f366003190112610424576020600554604051908152f35b34610424576020366003190112610424576020600435612e656135c9565b1015604051908152f35b34610424575f366003190112610424576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff821117612ee957604052565b612eb3565b90816020910312610424575190565b6040513d5f823e3d90fd5b634e487b7160e01b5f52601160045260245ffd5b5f19810191908211610dbf57565b91908203918211610dbf57565b600f54905f5b82811015612f8f57600f5f527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac8028101546001600160a01b03838116911614612f8757600101612f3d565b505050600190565b5050505f90565b9060018201809211610dbf57565b91908201809211610dbf57565b90816020910312610424575160ff811681036104245790565b9081602091031261042457516108e981610413565b600f549068010000000000000000821015612ee95760018201600f55600f548210156129b957600f5f527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac80290910180546001600160a01b0319166001600160a01b03909216919091179055565b600854909291906001600160a01b03163303610e2657600f5480820361314b575f5b8181106130e357505f5b8181106130b85750507faaeeacfd3dbf7ee886e6df5ff129be2537f1886a93f3a104d6bba8b305fc52b191926130b360405192839283613174565b0390a1565b806130dd6130d46105176130cf600195888b61315a565b61316a565b61292b836129a1565b01613078565b6130f7610b75610e576130cf84878a61315a565b61314b5761310481612f96565b828110613114575060010161306e565b6131226130cf83868961315a565b6131336105176130cf84888b61315a565b6001600160a01b039091161461314b57600101613104565b637804bc3560e01b5f5260045ffd5b91908110156129b95760051b0190565b356108e981610413565b60208082528101839052604001915f5b8181106131915750505090565b90919260208060019286356131a581610413565b848060a01b031681520194019101919091613184565b6040516370a0823160e01b81523060048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115610872575f9161326b575b5090600f545f905b808210613221575050565b909260046020613236610517610502886129a1565b6040516389fe02cb60e01b815292839182905afa801561087257600192613263925f92610fc45750612fa4565b930190613216565b613284915060203d6020116106fb576106ec8183612ec7565b5f61320e565b67ffffffffffffffff8111612ee95760051b60200190565b906132ac8261328a565b6132b96040519182612ec7565b82815280926132ca601f199161328a565b0190602036910137565b5f198114610dbf5760010190565b60ff16604d8111610dbf57600a0a90565b81810292918115918404141715610dbf57565b8115613310570490565b634e487b7160e01b5f52601260045260245ffd5b80518210156129b95760209160051b010190565b5190811515820361042457565b91906040838203126104245761335a83613338565b9260208101519067ffffffffffffffff8211610424570181601f820112156104245780519067ffffffffffffffff8211612ee957604051926133a6601f8401601f191660200185612ec7565b8284526020838301011161042457815f9260208093018386015e8301015290565b90929194939460018060a01b036006541633141580613486575b610fe4576133ee82612f37565b15610cc1575f6084819588604051998a9788968794635b0e93fb60e11b865260018060a01b03166004860152602485015260606044850152816064850152848401378181018301849052601f01601f191681010301926001600160a01b03165af1918215610872575f905f9361346357509190565b90506134829192503d805f833e61347a8183612ec7565b810190613345565b9091565b50600c546001600160a01b03163314156133e1565b6134a7610b7582612f37565b61355d576040516370a0823160e01b81523060048201526001600160a01b039190911690602081602481855afa5f918161353c575b506134e75750505f90565b60405163afa9294560e01b8152600481019190915290602090829060249082905afa5f918161351b575b506108e957505f90565b61353591925060203d6020116106fb576106ec8183612ec7565b905f613511565b61355691925060203d6020116106fb576106ec8183612ec7565b905f6134dc565b505f90565b600f5480156135b5575f19810190600f548210156129b957600f5f8190527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac80190910180546001600160a01b031916905555565b634e487b7160e01b5f52603160045260245ffd5b6040516370a0823160e01b81523060048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115610872575f916136fc575b5090600f545f5b81811061362e575050565b61363d610517610502836129a1565b6040516370a0823160e01b815230600482015290602090829060249082905afa5f91816136dc575b506136d757505f5b8061367c575b50600101613623565b60206136919161057a610517610502866129a1565b03915afa5f91816136b7575b5015613673576136b09060019295612fa4565b9390613673565b6136d091925060203d81116106fb576106ec8183612ec7565b905f61369d565b61366d565b6136f591925060203d81116106fb576106ec8183612ec7565b905f613665565b613715915060203d6020116106fb576106ec8183612ec7565b5f61361c565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c6137675760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d565b633ee5aeb560e01b5f5260045ffd5b5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d565b916001600160a01b038316918215613884576001600160a01b038116938415613871576001600160a01b0381165f9081526020819052604090205483811061384c579161383a91613820857fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9695039160018060a01b03165f525f60205260405f2090565b556001600160a01b03165f90815260208190526040902090565b805482019055604051908152602090a3565b63391434e360e21b5f526001600160a01b03909116600452602452604482905260645ffd5b63ec442f0560e01b5f525f60045260245ffd5b634b637e8f60e11b5f525f60045260245ffd5b60405163095ea7b360e01b5f9081526001600160a01b03841660045260248590529193929160209060448180885af19060015f5114821615613941575b604052156138e157505050565b6138eb8184613fe7565b1561392357906138fb918361404c565b156139035750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b635274afe760e01b5f9081526001600160a01b038416600452602490fd5b90843b15153d151616906138d4565b916040519163a9059cbb60e01b5f5260018060a01b031660045260245260205f60448180865af160015f51148116156139ae575b604091909152155b6139935750565b635274afe760e01b5f526001600160a01b031660045260245ffd5b60018115166139c4573d15833b15151616613984565b503d5f823e3d90fd5b6040516370a0823160e01b81523060048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169190602081602481865afa908115610872576004916020915f91613bcc575b50936040519283809263313ce56760e01b82525afa908115610872575f91613bad575b50600f54905f915b808310613a6057505050565b9091936004906020613a77610517610502896129a1565b6040516389fe02cb60e01b815293849182905afa918215610872575f92613b8a575b5060049082906020613ab06105176105028b6129a1565b604051635037b7e960e01b815294859182905afa928315610872576004936020915f91613b6d575b5060405163313ce56760e01b815294859182906001600160a01b03165afa801561087257600194613b20945f92613b4d575b5060ff881660ff831603613b29575b5050612fa4565b94019190613a54565b613b459293506122fd61230391613b3f8a6132e2565b906132f3565b905f80613b19565b613b6691925060203d81116110c5576110b78183612ec7565b905f613b0a565b613b849150823d811161123e576112308183612ec7565b5f613ad8565b6004919250613ba69060203d81116106fb576106ec8183612ec7565b9190613a99565b613bc6915060203d6020116110c5576110b78183612ec7565b5f613a4c565b613be39150823d84116106fb576106ec8183612ec7565b5f613a29565b9190826040910312610424576020613c0083613338565b92015190565b5f5160206141045f395f51905f52549095929491936001600160a01b039091169182918215613d3f57604080516363f1eb4960e11b81526001600160a01b039384166004820152838a166024820152928716604484015260648301959095526084820187905260a482015292908390815f8160c481015b03925af1918215610872575f905f93613d0b575b5015613cb05750613cac936001600160a01b03169050613950565b5f90565b91613d057f4f37448b7f55138d464c73bb1822255f37ce365a656874c349039a0b1be2691791613ceb86859860018060a01b03169687613950565b6040519586526001600160a01b0316949081906020820190565b0390a490565b9050613d3091925060403d604011613d38575b613d288183612ec7565b810190613be9565b91905f613c91565b503d613d1e565b50613cac966001600160a01b0316935061395092505050565b6001600160a01b03811691908215613884576001600160a01b0381165f90815260208190526040902054828110613de057917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91613dca825f9695039160018060a01b03165f525f60205260405f2090565b55600280548290039055604051908152602090a3565b63391434e360e21b5f526001600160a01b0390911660045260245260445260645ffd5b6040516323b872dd60e01b5f9081526001600160a01b039384166004529290931660245260449390935260209060648180865af160015f5114811615613e54575b6040919091525f6060521561398c565b60018115166139c4573d15833b15151616613e44565b5f5160206141045f395f51905f52549091906001600160a01b03168015613ee557803b15610424575f9283606492604051968795869463a69fe86360e01b865260018060a01b03166004860152602485015260448401525af1801561087257613ed05750565b80613edc5f8093612ec7565b80031261042457565b50505050565b6001600160a01b038116919082156138715760025490828201809211610dbf576002919091556001600160a01b03165f90815260208181526040822091927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92818154019055604051908152a3565b5f5160206141045f395f51905f5254909291906001600160a01b031615613f8f57906108e99291613f896139cd565b92614087565b613cac926001600160a01b0316613950565b6001600160a01b0316908115610aa2576001600160a01b03811615610a8f57613fe4915f52600160205260405f209060018060a01b03165f5260205260405f2090565b55565b60405163095ea7b360e01b5f9081526001600160a01b03909316600452602483905290929160209060448180875af19260015f511484161561402a575b50604052565b60018492941516614043573b15153d151616915f614024565b833d5f823e3d90fd5b92916040519163095ea7b360e01b5f5260018060a01b031660045260245260205f60448180875af19260015f511484161561402a5750604052565b5f5160206141045f395f51905f52549093906001600160a01b03168080156140ec5760408051631b518cd160e21b81526001600160a01b03808916600483015286166024820152604481018790526064810194909452908390815f8160848101613c7d565b50613cac946001600160a01b03169150613950905056fe88194fc1127942401ead32f70ac4b1816d514a0e878b86370156febc2984ee85000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000044820497f8fe95a258a9522f0de2c04ab2bc3da300000000000000000000000044820497f8fe95a258a9522f0de2c04ab2bc3da300000000000000000000000044820497f8fe95a258a9522f0de2c04ab2bc3da300000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c8062f714ce1461040e57806306fdde03146104095780630956e5a614610404578063095ea7b3146103ff5780630e845634146103fa57806316efd941146103f557806318160ddd1461030f5780631c30e682146103f057806323b872dd146103eb57806328593984146103e65780632a80cda3146103e15780632e8ebaae146103dc5780632ed6b75d146103d75780632f278fe8146103d25780633066bb27146103cd578063313ce567146103c857806333a100ca146103c357806339b70e38146103be578063465fc5d2146103b95780634956eaf0146103b45780634f5e8085146103af5780634f908e7f146103aa57806357c44f8e146103a557806358e75440146103a057806359746aab1461039b5780635985366f146103965780635c966646146103915780635d83f0931461038c5780635e1816551461038757806361d027b314610382578063645006ca1461037d57806370a082311461037857806375efcf771461037357806378e808d51461036e5780637a801f53146103695780637b9282b01461036457806382beee891461035f57806384afe9f01461035a57806389fe02cb146103555780638c05b47214610350578063958da9181461034b57806395d89b4114610346578063999927df1461034157806399f428cf1461033c5780639aefaff814610337578063a25db8ce14610332578063a9059cbb1461032d578063aec48f5014610328578063afa9294514610323578063b6b55f251461031e578063c0cbbca614610319578063c7c4ff4614610314578063d211fd181461030f578063d574ea3d1461030a578063d8b5138a14610305578063dd62ed3e14610300578063e322ad2b146102fb578063e357e92e146102f6578063edf1d2b3146102f1578063f0f44260146102ec578063f2c098b7146102e7578063f905c15a146102e2578063fbe85f06146102dd5763fc0c546a146102d8575f80fd5b612e6f565b612e47565b612e2a565b612dab565b612d15565b612c38565b612b2a565b612b10565b612ab1565b612a18565b6129be565b610b11565b612965565b612839565b61267e565b612648565b61256b565b612536565b61250f565b612487565b61200d565b611ea8565b611df3565b611db8565b611ca1565b611c87565b611c2f565b611baf565b611b19565b611af1565b611ac9565b611a30565b6119f6565b6119d9565b6119b1565b6118a0565b611883565b6117ea565b6117cd565b611770565b6116f1565b611591565b611469565b611441565b611295565b61126d565b611245565b6110cc565b611030565b611008565b610e8e565b610e66565b610e35565b610dce565b610d87565b610cdf565b610b2e565b610add565b610ab5565b6109f8565b6109db565b6108ec565b610428565b6001600160a01b0381160361042457565b5f80fd5b346104245760403660031901126104245760243560043561044882610413565b61045061371b565b600d546001600160a01b031633141580610895575b610886576001600160a01b0382168015610877578115610814576040516370a0823160e01b81523060048201527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48938392916020816024816001600160a01b038a165afa908115610872575f91610853575b508061082c575b50600f54935f5b85811080610823575b1561074e57610523610517610502836129a1565b905460039190911b1c6001600160a01b031690565b6001600160a01b031690565b6040516370a0823160e01b815230600482015290602090829060249082905afa5f918161072e575b5061055f575061055a906132d4565b6104e5565b801561060f57602061059a9161057a610517610502866129a1565b604051808095819463afa9294560e01b8352600483019190602083019252565b03915afa5f918161070e575b506105b5575061055a906132d4565b801561060f578581111561070257505f60206105fd875b6105db610517610502876129a1565b90604051948580948193632e1a7d4d60e01b8352600483019190602083019252565b03925af15f91816106d2575b50610619575b5061055a906132d4565b801561060f57909461064c610633610517610502896129a1565b6001600160a01b03165f90815260106020526040902090565b54808311156106cb575b6106686106336105176105028a6129a1565b610673828254612f2a565b9055600554808211156106b457505061068b5f600555565b808211156106a157505061055a5f945b9061060f565b61055a916106ae91612f2a565b9461069b565b6106c6916106c191612f2a565b600555565b61068b565b5081610656565b6106f491925060203d81116106fb575b6106ec8183612ec7565b810190612eee565b905f610609565b503d6106e2565b60206105fd5f926105cc565b61072791925060203d81116106fb576106ec8183612ec7565b905f6105a6565b61074791925060203d81116106fb576106ec8183612ec7565b905f61054b565b508594508361075c91612f2a565b926108145782610774916107c8958261080557613f5a565b9182156107cc57604080519182526020820184905233917f7f842c71573581ef84f3dc1e26292618e76438c9da374b4bceaa48eee30f2bd19190a35b6107b8613776565b6040519081529081906020820190565b0390f35b6040805133815260208101929092527f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364925090a16107b0565b61080f8333613d58565b613f5a565b638dc3160f60e01b5f5260045ffd5b508415156104ee565b9092508381111561084a5750610843835b84612f2a565b915f6104de565b6108439061083d565b61086c915060203d6020116106fb576106ec8183612ec7565b5f6104d7565b612efd565b637695083160e11b5f5260045ffd5b630a4c898360e31b5f5260045ffd5b50600e546001600160a01b0316331415610465565b5f91031261042457565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9060206108e99281815201906108b4565b90565b34610424575f366003190112610424576040515f6003548060011c90600181169081156109d1575b6020831082146109bd5782855260208501919081156109a45750600114610952575b6107c88461094681860382612ec7565b604051918291826108d8565b60035f9081529250907fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b8184106109905750500161094682610936565b80548484015260209093019260010161097d565b60ff191682525090151560051b01905061094682610936565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610914565b34610424575f366003190112610424576020600f54604051908152f35b3461042457604036600319011261042457600435610a1581610413565b6024353315610aa2576001600160a01b038216918215610a8f57610a558291335f52600160205260405f209060018060a01b03165f5260205260405f2090565b5560405190815233907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3602060405160018152f35b634a1406b160e11b5f525f60045260245ffd5b63e602df0560e01b5f525f60045260245ffd5b34610424575f366003190112610424576009546040516001600160a01b039091168152602090f35b34610424575f366003190112610424575f5160206141045f395f51905f52546040516001600160a01b039091168152602090f35b34610424575f366003190112610424576020600254604051908152f35b3461042457604036600319011261042457600435610b4b81610413565b60243590610b5761371b565b6006546001600160a01b03163303610cd057610b79610b7582612f37565b1590565b610cc15760405163336ecbed60e21b815260048101929092526020826024815f6001600160a01b0386165af1908115610872576107c8925f92610c7e575b506001600160a01b0381165f9081526010602052604090207f77a629eef1a2b0e39a9b09a71ddafbded84fb873712d56f5b2f6b1e1706e266b9183915480831115610c77575b6001600160a01b0382165f908152601060205260409020610c1f828254612f2a565b905560055480821115610c65575050610c375f600555565b604080513381526001600160a01b03909216602083015281019190915280606081015b0390a16107b8613776565b610c72916106c191612f2a565b610c37565b5081610bfd565b7f77a629eef1a2b0e39a9b09a71ddafbded84fb873712d56f5b2f6b1e1706e266b919250610cba9060203d6020116106fb576106ec8183612ec7565b9190610bb7565b63741f966f60e11b5f5260045ffd5b63bbab121360e01b5f5260045ffd5b3461042457606036600319011261042457600435610cfc81610413565b602435610d0881610413565b6001600160a01b0382165f9081526001602090815260408083203384529091529020549160443591905f198410610d50575b610d44935061379b565b60405160018152602090f35b828410610d6c57610d6783610d4495033383613fa1565b610d3a565b8284637dc7a0d960e11b5f523360045260245260445260645ffd5b34610424575f36600319011261042457610d9f6131bb565b60025480821115610dc4578103908111610dbf576020905b604051908152f35b612f08565b505060205f610db7565b3461042457602036600319011261042457600854600435906001600160a01b03163303610e26576020817fbe19bc97d2bca5ac16c999faf6d2fbde29b13fae7020e3a390febf030f8242d792601455604051908152a1005b632602fe1160e21b5f5260045ffd5b34610424576020366003190112610424576020610e5c600435610e5781610413565b612f37565b6040519015158152f35b34610424575f36600319011261042457600b546040516001600160a01b039091168152602090f35b34610424575f366003190112610424576006546001600160a01b031633141580610ff3575b610fe457600f54600a545f9182916001600160a01b03165b818310610f6457838015610f5557604080513381526001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481660208201529081018290526107c891907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd79080606081015b0390a16040519081529081906020820190565b631492cf1b60e21b5f5260045ffd5b909192610f76610517610502866129a1565b60405163999927df60e01b81526001600160a01b038416600482015290602090829060249082905f905af1801561087257600192610fbb925f92610fc4575b50612fa4565b93019190610ecb565b610fdd91925060203d81116106fb576106ec8183612ec7565b905f610fb5565b637158b7f360e01b5f5260045ffd5b50600c546001600160a01b0316331415610eb3565b34610424575f36600319011261042457600c546040516001600160a01b039091168152602090f35b34610424575f3660031901126104245760405163313ce56760e01b81526020816004817f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03165afa908115610872575f9161109d575b5060405160ff9091168152602090f35b6110bf915060203d6020116110c5575b6110b78183612ec7565b810190612fb1565b5f61108d565b503d6110ad565b34610424576020366003190112610424576004356110e981610413565b6008546001600160a01b03163303610e26576001600160a01b03811680156108775761111482612f37565b9081156111b6575b50610cc1576111579061115261113460135442612fa4565b9160018060a01b03166001600160601b0360a01b6011541617601155565b601255565b6008547ffdcf3907978ffc608fc7f27e245c3d5d995700556bdc27685b2182a48b9b7e72906001600160a01b03166011546001600160a01b03165b604080516001600160a01b039384168152929091166020830152819081015b0390a1005b604051637e062a3560e11b81529150602090829060049082905afa908115610872575f91611216575b507f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03908116911614155f61111c565b611238915060203d60201161123e575b6112308183612ec7565b810190612fca565b5f6111df565b503d611226565b34610424575f366003190112610424576008546040516001600160a01b039091168152602090f35b34610424575f366003190112610424576011546040516001600160a01b039091168152602090f35b34610424576040366003190112610424576004356112b281610413565b6024356112bd61371b565b6006546001600160a01b03163303610cd0576112db610b7583612f37565b610cc1576040516370a0823160e01b81523060048201526001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481690602081602481855afa80156108725783915f91611422575b501061081457818361134692613897565b6001600160a01b03821691823b156104245760405163b6b55f2560e01b815260048101839052925f908490602490829084905af1928315610872577fb03c53b28e78a88e31607a27e1fa48234dce28d5d9d9ec7b295aeb02e674a1e193611408575b506001600160a01b0381165f9081526010602052604090206113cb838254612fa4565b90556113dc6106c183600554612fa4565b604080516001600160a01b039092168252602082019290925290819081015b0390a1611406613776565b005b806114165f61141c93612ec7565b806108aa565b5f6113a8565b61143b915060203d6020116106fb576106ec8183612ec7565b5f611335565b34610424575f36600319011261042457600d546040516001600160a01b039091168152602090f35b34610424575f36600319011261042457600a5461148e906001600160a01b0316610517565b3303611582576011546001600160a01b03168015610877576012544210611573576114c1906001600160a01b0316612fdf565b600854601154604080516001600160a01b039384168152929091166020830181905261151a92610517927f5196e6e427329801067f2d2174593bfbda5a246c506762f55fc401ee6084845e91a16001600160a01b031690565b803b156104245760405162844ae160e71b8152306004820152905f908290602490829084905af161155f575b601180546001600160a01b03191690556111575f601255565b806114165f61156d93612ec7565b5f611546565b6330b068d360e21b5f5260045ffd5b631fe9dba560e21b5f5260045ffd5b34610424576040366003190112610424576004356115ae81610413565b602435906115ba61371b565b6006546001600160a01b03163303610cd0576115d8610b7582612f37565b610cc157604051632e1a7d4d60e01b815260048101929092526020826024815f6001600160a01b0386165af1908115610872577f77a629eef1a2b0e39a9b09a71ddafbded84fb873712d56f5b2f6b1e1706e266b925f926116d0575b506001600160a01b0381165f90815260106020526040902054808311156116c9575b6001600160a01b0382165f908152601060205260409020611678828254612f2a565b9055600554808211156116b75750506116905f600555565b604080513381526001600160a01b03909216602083015281019190915280606081016113fb565b6116c4916106c191612f2a565b611690565b5081611656565b6116ea91925060203d6020116106fb576106ec8183612ec7565b905f611634565b346104245760203660031901126104245760043561170e81610413565b6008546001600160a01b03163303610e26576001600160a01b03168015610877576020817fcac43e3f2c27e1b165ed9d6100079705fcc1d17d86c9588a65b3b21af19efb55926001600160601b0360a01b600d541617600d55604051908152a1005b346104245760203660031901126104245760043567ffffffffffffffff8111610424573660238201121561042457806004013567ffffffffffffffff8111610424573660248260051b84010111610424576024611406920161304c565b34610424575f366003190112610424576020601354604051908152f35b346104245760203660031901126104245760043561180781610413565b6008546001600160a01b03169033829003610e26576001600160a01b0316801561087757600980546001600160a01b03191682179055604080516001600160a01b0393841681529290911660208301527f229906f503462bb4f822a3a5c8a143331ba9ca0b25dd7de323cf57b842eb0c849190819081016111b1565b34610424575f366003190112610424576020601254604051908152f35b34610424575f36600319011261042457600a546118c5906001600160a01b0316610517565b33141580611993575b611984576007546001600160a01b03168015610877576006546001600160a01b0382811691161461197557600680546001600160a01b0319166001600160a01b03909216919091179055600780546001600160a01b03191690556006547f6c6266c4ca096dc11256cb3c5a2f481d4ebfc848b647e04ba395e5e02cde9a1d906111b1906001600160a01b03165b6040516001600160a01b0390911681529081906020820190565b63140d058560e11b5f5260045ffd5b637acfd59760e01b5f5260045ffd5b506008546119a9906001600160a01b0316610517565b3314156118ce565b34610424575f36600319011261042457600a546040516001600160a01b039091168152602090f35b34610424575f366003190112610424576020601454604051908152f35b3461042457602036600319011261042457600435611a1381610413565b60018060a01b03165f525f602052602060405f2054604051908152f35b3461042457602036600319011261042457600435611a4d81610413565b6006546001600160a01b03169033829003610cd0576001600160a01b0316801561087757600c80546001600160a01b03191682179055604080516001600160a01b0393841681529290911660208301527f68d1aa5e9bdbf0db52fe140cc136ad3a30d9010e7d2f048eaf1ab0dad87c59759190819081016111b1565b34610424575f366003190112610424576006546040516001600160a01b039091168152602090f35b34610424575f366003190112610424576007546040516001600160a01b039091168152602090f35b34610424575f366003190112610424576040516370a0823160e01b81523060048201526020816024817f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03165afa8015610872576107c8915f91611b90575b506040519081529081906020820190565b611ba9915060203d6020116106fb576106ec8183612ec7565b5f611b7f565b3461042457602036600319011261042457600435611bcc81610413565b6008546001600160a01b03163303610e26575f5160206141045f395f51905f5280546001600160a01b0319166001600160a01b039290921691821790557f576d39e65c913b7fedc16b28e8563e891fb6d44bcf0c1c29495375595cd79b655f80a2005b3461042457602036600319011261042457600a54600435906001600160a01b03163303611582576020817fcffacb86a783dc4940b8d1e3677131e283bc556cbd9978ba4ce78272df1550a692601355604051908152a1005b34610424575f366003190112610424576020610db76131bb565b34610424575f36600319011261042457611cb961371b565b6006546001600160a01b031633141580611da3575b610fe4576040516370a0823160e01b81523060048201527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48906001600160a01b038216602082602481845afa918215610872575f92611d82575b506002549081831115610f55576107c893611d677fe33b3f29026207fc167007711d41e9edd78750b0051defa03f1e4ea002accd6a93610c3795612f2a565b93848093611d7c600a5460018060a01b031690565b90613950565b611d9c91925060203d6020116106fb576106ec8183612ec7565b905f611d28565b50600c546001600160a01b0316331415611cce565b3461042457602036600319011261042457600435611dd581610413565b60018060a01b03165f526010602052602060405f2054604051908152f35b34610424575f366003190112610424576040515f6004548060011c9060018116908115611e9e575b6020831082146109bd5782855260208501919081156109a45750600114611e4c576107c88461094681860382612ec7565b60045f9081529250907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b818410611e8a5750500161094682610936565b805484840152602090930192600101611e77565b91607f1691611e1b565b3461042457602036600319011261042457600435611ec581610413565b6006546001600160a01b031633141580611fbf575b610fe457611eea610b7582612f37565b610cc157600a5460405163999927df60e01b81526001600160a01b03918216600482015291602091839160249183915f91165af1908115610872575f91611fa0575b508015610f5557604080513381526001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481660208201529081018290526107c891907ff3055bc8d92d9c8d2f12b45d112dd345cd2cfd17292b8d65c5642ac6f912dfd7908060608101610f42565b611fb9915060203d6020116106fb576106ec8183612ec7565b5f611f2c565b50600c546001600160a01b0316331415611eda565b60206040818301928281528451809452019201905f5b818110611ff75750505090565b8251845260209384019390920191600101611fea565b346104245760403660031901126104245760243560043561202d82610413565b61203561371b565b600d546001600160a01b031633141580612459575b610886576001600160a01b0382168015610877578115610814577f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4861208d6139cd565b928091600f549461209d866132a2565b915f935f5b88811080612450575b156123d0576120bf610517610502836129a1565b6040516370a0823160e01b815230600482015290602090829060249082905afa908115610872575f916123b2575b5080156123a857878111156123a35750865b61210e610517610502846129a1565b6040516301071a2960e41b8152600481018390529190602090839060249082905f905af15f9281612383575b50612150575b505061214b906132d4565b6120a2565b8115612140576121b5908392996121716106336105176105026004986129a1565b548083111561237c575b61218d610633610517610502886129a1565b612198828254612f2a565b90556005548082111561236f5750506121b05f600555565b612f2a565b9760206121c7610517610502856129a1565b604051635037b7e960e01b815294859182905afa928315610872575f9361234f575b5060405163313ce56760e01b81529281906020856004816001600160a01b0385165afa948515610872575f9561232f575b5060405163313ce56760e01b8152926020846004816001600160a01b038c165afa958615610872578f848f9691612270958c9461214b9b8f955f91612311575b5060ff811660ff8316036122e2575b5050613c06565b8981612282575b505050819250612140565b817f7f842c71573581ef84f3dc1e26292618e76438c9da374b4bceaa48eee30f2bd1926122bc6122b56122d6949e6132d4565b9d8d613324565b526040805194855260208501919091523393918291820190565b0390a3885f8089612277565b6123099297506122fd6122f7612303926132e2565b866132f3565b916132e2565b90613306565b945f80612269565b612329915060203d81116110c5576110b78183612ec7565b5f61225a565b61234891955060203d81116110c5576110b78183612ec7565b935f61221a565b61236891935060203d811161123e576112308183612ec7565b915f6121e9565b6121b0916106c191612f2a565b508161217b565b61239c91935060203d81116106fb576106ec8183612ec7565b915f61213a565b6120ff565b5061214b906132d4565b6123ca915060203d81116106fb576106ec8183612ec7565b5f6120ed565b86856123df82858a8452612f2a565b91610814577fe66f2a5f859e75187ec8381862cf2edc4124d6ea89f5fa1a7b66035121bd446161242a836107c894612441575b60408051338152602081019290925290918291820190565b0390a1612435613776565b60405191829182611fd4565b61244b8133613d58565b612412565b508615156120ab565b50600e546001600160a01b031633141561204a565b6040906108e993921515815281602082015201906108b4565b34610424576080366003190112610424576004356124a481610413565b6024356124b081610413565b6064359160443567ffffffffffffffff841161042457366023850112156104245783600401359267ffffffffffffffff84116104245736602485870101116104245760246124ff9501926133c7565b906107c86040519283928361246e565b34610424576020366003190112610424576020610db760043561253181610413565b61349b565b346104245760403660031901126104245761256060043561255681610413565b602435903361379b565b602060405160018152f35b34610424575f36600319011261042457600a54612590906001600160a01b0316610517565b3314158061262a575b611582576009546001600160a01b03168015610877576008546001600160a01b0382811691161461197557600880546001600160a01b0319166001600160a01b03909216919091179055600980546001600160a01b03191690556008547f1d71beb956329ce4bda4b6a3cf409eae37dca995fe985253f39c49f5dd3bf3c7906111b1906001600160a01b031661195b565b50600654612640906001600160a01b0316610517565b331415612599565b346104245760203660031901126104245760206004356126666135c9565b818111156126775750604051908152f35b9050610db7565b346104245760203660031901126104245760043561269a61371b565b600d546001600160a01b031633141580612824575b610886578015610814576014548015158061281b575b61280357507f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48906126f46139cd565b6040516370a0823160e01b81523060048201529091906001600160a01b038416602082602481845afa918215610872575f926127e2575b5061273883303384613e03565b6040516370a0823160e01b815230600482015290602090829060249082905afa9081156108725783905f926127bf575b506127739192612fa4565b036127b1576127a76113fb92827fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c95613e6a565b6124128133613eeb565b626d480360e31b5f5260045ffd5b61277392506127dc9060203d6020116106fb576106ec8183612ec7565b91612768565b6127fc91925060203d6020116106fb576106ec8183612ec7565b905f61272b565b63be8c6f6360e01b5f5260049190915260245260445ffd5b508082106126c5565b50600e546001600160a01b03163314156126af565b34610424576020366003190112610424576004356008546001600160a01b03163303610e2657600f5490818110156129565761288e612877826129a1565b90546001600160a01b039260039290921b1c821690565b166001600160a01b0381165f90815260106020526040902054610cc157826111b1926128da7f206dcdb9e6d573bb19797ecdc31f426bc49808caf16879b86cee1ba682e3881f95612f1c565b810361290b575b50506128eb613562565b604080513381526001600160a01b03909216602083015290918291820190565b61292b61292561050261292061294f95612f1c565b6129a1565b916129a1565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b5f806128e1565b635979b5d360e01b5f5260045ffd5b34610424575f36600319011261042457600e546040516001600160a01b039091168152602090f35b634e487b7160e01b5f52603260045260245ffd5b600f548110156129b957600f5f5260205f2001905f90565b61298d565b3461042457602036600319011261042457600435600f5481101561042457600f5f527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac80201546040516001600160a01b039091168152602090f35b3461042457602036600319011261042457600435612a3581610413565b6006546001600160a01b03169033829003610cd0576001600160a01b0316801561087757600780546001600160a01b03191682179055604080516001600160a01b0393841681529290911660208301527f9199036abbe383a5a535e02e8f65421079bb3fa71d03495a107b8b0231b616f29190819081016111b1565b34610424576040366003190112610424576020612b07600435612ad381610413565b60243590612ae082610413565b60018060a01b03165f526001835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b34610424575f366003190112610424576020610db76135c9565b3461042457604036600319011261042457600435612b4781610413565b60243590612b5361371b565b6006546001600160a01b03163303610cd057612b71610b7582612f37565b610cc15760405163cb27acfd60e01b815260048101839052916020836024815f6001600160a01b0387165af1908115610872576107c8935f92612bf7575b50604080513381526001600160a01b039094166020850152830152907fb093bd21cacddf175c466449524962624f8edabfc8497212c45b6ce70c90bfe2908060608101610c5a565b7fb093bd21cacddf175c466449524962624f8edabfc8497212c45b6ce70c90bfe29250612c329060203d6020116106fb576106ec8183612ec7565b91612baf565b34610424575f36600319011261042457600854612c5d906001600160a01b0316610517565b33141580612cf7575b61158257600b546001600160a01b0316801561087757600a546001600160a01b0382811691161461197557600a80546001600160a01b0319166001600160a01b03909216919091179055600b80546001600160a01b0319169055600a547f1f54d231bb9d500b1923e4a1cb25e600f366a8368873d9af7c1c623814df19fc906111b1906001600160a01b031661195b565b50600654612d0d906001600160a01b0316610517565b331415612c66565b3461042457602036600319011261042457600435612d3281610413565b600a54906001600160a01b0382163303612d9c576001600160a01b031690811561087757600b80546001600160a01b031916831790557f74826a97c225ebfc6ae4d01aa55f9846d00bb141717acca3947f3778534be5a1916001600160a01b039182169116611192565b636b7bc45160e01b5f5260045ffd5b3461042457602036600319011261042457600435612dc881610413565b6008546001600160a01b03163303610e26576001600160a01b03168015610877576020817ff112f2fd3f9db4cbbd348d7d8948ad19988985c74b787d6f0fb8f62ba25f9e0e926001600160601b0360a01b600e541617600e55604051908152a1005b34610424575f366003190112610424576020600554604051908152f35b34610424576020366003190112610424576020600435612e656135c9565b1015604051908152f35b34610424575f366003190112610424576040517f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03168152602090f35b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff821117612ee957604052565b612eb3565b90816020910312610424575190565b6040513d5f823e3d90fd5b634e487b7160e01b5f52601160045260245ffd5b5f19810191908211610dbf57565b91908203918211610dbf57565b600f54905f5b82811015612f8f57600f5f527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac8028101546001600160a01b03838116911614612f8757600101612f3d565b505050600190565b5050505f90565b9060018201809211610dbf57565b91908201809211610dbf57565b90816020910312610424575160ff811681036104245790565b9081602091031261042457516108e981610413565b600f549068010000000000000000821015612ee95760018201600f55600f548210156129b957600f5f527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac80290910180546001600160a01b0319166001600160a01b03909216919091179055565b600854909291906001600160a01b03163303610e2657600f5480820361314b575f5b8181106130e357505f5b8181106130b85750507faaeeacfd3dbf7ee886e6df5ff129be2537f1886a93f3a104d6bba8b305fc52b191926130b360405192839283613174565b0390a1565b806130dd6130d46105176130cf600195888b61315a565b61316a565b61292b836129a1565b01613078565b6130f7610b75610e576130cf84878a61315a565b61314b5761310481612f96565b828110613114575060010161306e565b6131226130cf83868961315a565b6131336105176130cf84888b61315a565b6001600160a01b039091161461314b57600101613104565b637804bc3560e01b5f5260045ffd5b91908110156129b95760051b0190565b356108e981610413565b60208082528101839052604001915f5b8181106131915750505090565b90919260208060019286356131a581610413565b848060a01b031681520194019101919091613184565b6040516370a0823160e01b81523060048201526020816024816001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48165afa908115610872575f9161326b575b5090600f545f905b808210613221575050565b909260046020613236610517610502886129a1565b6040516389fe02cb60e01b815292839182905afa801561087257600192613263925f92610fc45750612fa4565b930190613216565b613284915060203d6020116106fb576106ec8183612ec7565b5f61320e565b67ffffffffffffffff8111612ee95760051b60200190565b906132ac8261328a565b6132b96040519182612ec7565b82815280926132ca601f199161328a565b0190602036910137565b5f198114610dbf5760010190565b60ff16604d8111610dbf57600a0a90565b81810292918115918404141715610dbf57565b8115613310570490565b634e487b7160e01b5f52601260045260245ffd5b80518210156129b95760209160051b010190565b5190811515820361042457565b91906040838203126104245761335a83613338565b9260208101519067ffffffffffffffff8211610424570181601f820112156104245780519067ffffffffffffffff8211612ee957604051926133a6601f8401601f191660200185612ec7565b8284526020838301011161042457815f9260208093018386015e8301015290565b90929194939460018060a01b036006541633141580613486575b610fe4576133ee82612f37565b15610cc1575f6084819588604051998a9788968794635b0e93fb60e11b865260018060a01b03166004860152602485015260606044850152816064850152848401378181018301849052601f01601f191681010301926001600160a01b03165af1918215610872575f905f9361346357509190565b90506134829192503d805f833e61347a8183612ec7565b810190613345565b9091565b50600c546001600160a01b03163314156133e1565b6134a7610b7582612f37565b61355d576040516370a0823160e01b81523060048201526001600160a01b039190911690602081602481855afa5f918161353c575b506134e75750505f90565b60405163afa9294560e01b8152600481019190915290602090829060249082905afa5f918161351b575b506108e957505f90565b61353591925060203d6020116106fb576106ec8183612ec7565b905f613511565b61355691925060203d6020116106fb576106ec8183612ec7565b905f6134dc565b505f90565b600f5480156135b5575f19810190600f548210156129b957600f5f8190527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac80190910180546001600160a01b031916905555565b634e487b7160e01b5f52603160045260245ffd5b6040516370a0823160e01b81523060048201526020816024816001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48165afa908115610872575f916136fc575b5090600f545f5b81811061362e575050565b61363d610517610502836129a1565b6040516370a0823160e01b815230600482015290602090829060249082905afa5f91816136dc575b506136d757505f5b8061367c575b50600101613623565b60206136919161057a610517610502866129a1565b03915afa5f91816136b7575b5015613673576136b09060019295612fa4565b9390613673565b6136d091925060203d81116106fb576106ec8183612ec7565b905f61369d565b61366d565b6136f591925060203d81116106fb576106ec8183612ec7565b905f613665565b613715915060203d6020116106fb576106ec8183612ec7565b5f61361c565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c6137675760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d565b633ee5aeb560e01b5f5260045ffd5b5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d565b916001600160a01b038316918215613884576001600160a01b038116938415613871576001600160a01b0381165f9081526020819052604090205483811061384c579161383a91613820857fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9695039160018060a01b03165f525f60205260405f2090565b556001600160a01b03165f90815260208190526040902090565b805482019055604051908152602090a3565b63391434e360e21b5f526001600160a01b03909116600452602452604482905260645ffd5b63ec442f0560e01b5f525f60045260245ffd5b634b637e8f60e11b5f525f60045260245ffd5b60405163095ea7b360e01b5f9081526001600160a01b03841660045260248590529193929160209060448180885af19060015f5114821615613941575b604052156138e157505050565b6138eb8184613fe7565b1561392357906138fb918361404c565b156139035750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b635274afe760e01b5f9081526001600160a01b038416600452602490fd5b90843b15153d151616906138d4565b916040519163a9059cbb60e01b5f5260018060a01b031660045260245260205f60448180865af160015f51148116156139ae575b604091909152155b6139935750565b635274afe760e01b5f526001600160a01b031660045260245ffd5b60018115166139c4573d15833b15151616613984565b503d5f823e3d90fd5b6040516370a0823160e01b81523060048201526001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169190602081602481865afa908115610872576004916020915f91613bcc575b50936040519283809263313ce56760e01b82525afa908115610872575f91613bad575b50600f54905f915b808310613a6057505050565b9091936004906020613a77610517610502896129a1565b6040516389fe02cb60e01b815293849182905afa918215610872575f92613b8a575b5060049082906020613ab06105176105028b6129a1565b604051635037b7e960e01b815294859182905afa928315610872576004936020915f91613b6d575b5060405163313ce56760e01b815294859182906001600160a01b03165afa801561087257600194613b20945f92613b4d575b5060ff881660ff831603613b29575b5050612fa4565b94019190613a54565b613b459293506122fd61230391613b3f8a6132e2565b906132f3565b905f80613b19565b613b6691925060203d81116110c5576110b78183612ec7565b905f613b0a565b613b849150823d811161123e576112308183612ec7565b5f613ad8565b6004919250613ba69060203d81116106fb576106ec8183612ec7565b9190613a99565b613bc6915060203d6020116110c5576110b78183612ec7565b5f613a4c565b613be39150823d84116106fb576106ec8183612ec7565b5f613a29565b9190826040910312610424576020613c0083613338565b92015190565b5f5160206141045f395f51905f52549095929491936001600160a01b039091169182918215613d3f57604080516363f1eb4960e11b81526001600160a01b039384166004820152838a166024820152928716604484015260648301959095526084820187905260a482015292908390815f8160c481015b03925af1918215610872575f905f93613d0b575b5015613cb05750613cac936001600160a01b03169050613950565b5f90565b91613d057f4f37448b7f55138d464c73bb1822255f37ce365a656874c349039a0b1be2691791613ceb86859860018060a01b03169687613950565b6040519586526001600160a01b0316949081906020820190565b0390a490565b9050613d3091925060403d604011613d38575b613d288183612ec7565b810190613be9565b91905f613c91565b503d613d1e565b50613cac966001600160a01b0316935061395092505050565b6001600160a01b03811691908215613884576001600160a01b0381165f90815260208190526040902054828110613de057917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91613dca825f9695039160018060a01b03165f525f60205260405f2090565b55600280548290039055604051908152602090a3565b63391434e360e21b5f526001600160a01b0390911660045260245260445260645ffd5b6040516323b872dd60e01b5f9081526001600160a01b039384166004529290931660245260449390935260209060648180865af160015f5114811615613e54575b6040919091525f6060521561398c565b60018115166139c4573d15833b15151616613e44565b5f5160206141045f395f51905f52549091906001600160a01b03168015613ee557803b15610424575f9283606492604051968795869463a69fe86360e01b865260018060a01b03166004860152602485015260448401525af1801561087257613ed05750565b80613edc5f8093612ec7565b80031261042457565b50505050565b6001600160a01b038116919082156138715760025490828201809211610dbf576002919091556001600160a01b03165f90815260208181526040822091927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92818154019055604051908152a3565b5f5160206141045f395f51905f5254909291906001600160a01b031615613f8f57906108e99291613f896139cd565b92614087565b613cac926001600160a01b0316613950565b6001600160a01b0316908115610aa2576001600160a01b03811615610a8f57613fe4915f52600160205260405f209060018060a01b03165f5260205260405f2090565b55565b60405163095ea7b360e01b5f9081526001600160a01b03909316600452602483905290929160209060448180875af19260015f511484161561402a575b50604052565b60018492941516614043573b15153d151616915f614024565b833d5f823e3d90fd5b92916040519163095ea7b360e01b5f5260018060a01b031660045260245260205f60448180875af19260015f511484161561402a5750604052565b5f5160206141045f395f51905f52549093906001600160a01b03168080156140ec5760408051631b518cd160e21b81526001600160a01b03808916600483015286166024820152604481018790526064810194909452908390815f8160848101613c7d565b50613cac946001600160a01b03169150613950905056fe88194fc1127942401ead32f70ac4b1816d514a0e878b86370156febc2984ee85
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000044820497f8fe95a258a9522f0de2c04ab2bc3da300000000000000000000000044820497f8fe95a258a9522f0de2c04ab2bc3da300000000000000000000000044820497f8fe95a258a9522f0de2c04ab2bc3da300000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _token (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [1] : _yieldClaimer (address): 0x44820497f8FE95A258A9522f0De2c04ab2bC3da3
Arg [2] : _strategyManager (address): 0x44820497f8FE95A258A9522f0De2c04ab2bC3da3
Arg [3] : _treasury (address): 0x44820497f8FE95A258A9522f0De2c04ab2bC3da3
Arg [4] : _name (string):
Arg [5] : _symbol (string):
-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [1] : 00000000000000000000000044820497f8fe95a258a9522f0de2c04ab2bc3da3
Arg [2] : 00000000000000000000000044820497f8fe95a258a9522f0de2c04ab2bc3da3
Arg [3] : 00000000000000000000000044820497f8fe95a258a9522f0de2c04ab2bc3da3
Arg [4] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [5] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
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.