POL Price: $0.503619 (+4.81%)
 

Overview

POL Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo0 POL

POL Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Multicall651250732024-12-05 19:37:1118 days ago1733427431IN
0x92fdb760...80DFa53E6
0 POL0.0070711536.23369534
Multicall650338942024-12-03 13:19:5520 days ago1733231995IN
0x92fdb760...80DFa53E6
0 POL0.02205947113.03625372
Multicall648325222024-11-28 12:13:5225 days ago1732796032IN
0x92fdb760...80DFa53E6
0 POL0.0060833532.98571997
Multicall647619712024-11-26 17:50:1227 days ago1732643412IN
0x92fdb760...80DFa53E6
0 POL0.0185826296.34091152
Multicall647283492024-11-25 21:05:3628 days ago1732568736IN
0x92fdb760...80DFa53E6
0 POL0.0078991836.1365101
Multicall645982612024-11-22 14:36:3631 days ago1732286196IN
0x92fdb760...80DFa53E6
0 POL0.01974438110.88986839
Multicall645676822024-11-21 20:20:4332 days ago1732220443IN
0x92fdb760...80DFa53E6
0 POL0.0064434333.07922459
Multicall645639872024-11-21 18:04:2232 days ago1732212262IN
0x92fdb760...80DFa53E6
0 POL0.0123462563.26418458
Multicall644785942024-11-19 15:01:1034 days ago1732028470IN
0x92fdb760...80DFa53E6
0 POL0.0159032581.54931253
Multicall642432792024-11-13 17:32:0440 days ago1731519124IN
0x92fdb760...80DFa53E6
0 POL0.0041625637.29799274
Multicall642415512024-11-13 16:30:3740 days ago1731515437IN
0x92fdb760...80DFa53E6
0 POL0.02138777112.35789395

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
DelayedWithdrawalManager

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 10000 runs

Other Settings:
cancun EvmVersion
File 1 of 18 : DelayedWithdrawalManager.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.22;

import { INFTPermissions } from "@balmy/nft-permissions/interfaces/INFTPermissions.sol";
import { IDelayedWithdrawalManager, IEarnVault } from "../interfaces/IDelayedWithdrawalManager.sol";
import { IDelayedWithdrawalAdapter } from "../interfaces/IDelayedWithdrawalAdapter.sol";
import { StrategyId, StrategyIdConstants } from "@balmy/earn-core/types/StrategyId.sol";
import { IEarnBalmyStrategy, IEarnStrategy } from "../interfaces/IEarnBalmyStrategy.sol";
// solhint-disable-next-line no-unused-import
import { RegisteredAdapter, RegisteredAdaptersLibrary, PositionIdTokenKey } from "./types/RegisteredAdapters.sol";
import { PayableMulticall } from "src/base/PayableMulticall.sol";

contract DelayedWithdrawalManager is IDelayedWithdrawalManager, PayableMulticall {
  using RegisteredAdaptersLibrary for mapping(uint256 => mapping(address => mapping(uint256 => RegisteredAdapter)));
  using RegisteredAdaptersLibrary for mapping(uint256 => RegisteredAdapter);

  // slither-disable-start naming-convention
  mapping(uint256 position => mapping(address token => mapping(uint256 index => RegisteredAdapter registeredAdapter)))
    internal _registeredAdapters;
  /// @inheritdoc IDelayedWithdrawalManager
  // solhint-disable-next-line var-name-mixedcase
  IEarnVault public immutable VAULT;
  // solhint-disable-next-line var-name-mixedcase
  INFTPermissions.Permission private immutable WITHDRAW_PERMISSION;

  // slither-disable-end naming-convention

  constructor(IEarnVault vault) {
    VAULT = vault;
    WITHDRAW_PERMISSION = vault.WITHDRAW_PERMISSION();
  }

  /// @inheritdoc IDelayedWithdrawalManager
  function estimatedPendingFunds(uint256 positionId, address token) public view returns (uint256 pendingFunds) {
    mapping(uint256 index => RegisteredAdapter registeredAdapter) storage registeredAdapters =
      _registeredAdapters.get(positionId, token);
    uint256 i = 0;

    bool shouldContinue = true;
    while (shouldContinue) {
      RegisteredAdapter memory adapter = registeredAdapters[i];
      if (address(adapter.adapter) != address(0)) {
        // slither-disable-next-line calls-loop
        pendingFunds += adapter.adapter.estimatedPendingFunds(positionId, token);
        unchecked {
          ++i;
        }
      }
      shouldContinue = adapter.isNextFilled;
    }
  }

  /// @inheritdoc IDelayedWithdrawalManager
  function withdrawableFunds(uint256 positionId, address token) public view returns (uint256 funds) {
    mapping(uint256 index => RegisteredAdapter registeredAdapter) storage registeredAdapters =
      _registeredAdapters.get(positionId, token);
    uint256 i = 0;
    bool shouldContinue = true;
    while (shouldContinue) {
      RegisteredAdapter memory adapter = registeredAdapters[i];
      if (address(adapter.adapter) != address(0)) {
        // slither-disable-next-line calls-loop
        funds += adapter.adapter.withdrawableFunds(positionId, token);
        unchecked {
          ++i;
        }
      }
      shouldContinue = adapter.isNextFilled;
    }
  }

  /// @inheritdoc IDelayedWithdrawalManager
  function allPositionFunds(uint256 positionId)
    external
    view
    returns (address[] memory tokens, uint256[] memory estimatedPending, uint256[] memory withdrawable)
  {
    // slither-disable-next-line unused-return
    (tokens,,,) = VAULT.position(positionId);
    uint256 tokensQuantity = tokens.length;
    estimatedPending = new uint256[](tokensQuantity);
    withdrawable = new uint256[](tokensQuantity);
    // slither-disable-start calls-loop
    for (uint256 i; i < tokensQuantity; ++i) {
      address token = tokens[i];
      mapping(uint256 index => RegisteredAdapter registeredAdapter) storage registeredAdapters =
        _registeredAdapters.get(positionId, token);
      uint256 j = 0;
      bool shouldContinue = true;
      while (shouldContinue) {
        RegisteredAdapter memory adapter = registeredAdapters[j];
        if (address(adapter.adapter) != address(0)) {
          withdrawable[i] += adapter.adapter.withdrawableFunds(positionId, token);
          estimatedPending[i] += adapter.adapter.estimatedPendingFunds(positionId, token);
          unchecked {
            ++j;
          }
        }
        shouldContinue = adapter.isNextFilled;
      }
      // slither-disable-end calls-loop
    }
  }

  /// @inheritdoc IDelayedWithdrawalManager
  function registerDelayedWithdraw(uint256 positionId, address token) external {
    _revertIfNotCurrentStrategyAdapter(positionId, token);

    mapping(uint256 index => RegisteredAdapter registeredAdapter) storage registeredAdapters =
      _registeredAdapters.get(positionId, token);

    (bool isRepeated, uint256 length) = registeredAdapters.isRepeated(IDelayedWithdrawalAdapter(msg.sender));
    if (isRepeated) {
      revert AdapterDuplicated();
    }
    registeredAdapters.set(length, IDelayedWithdrawalAdapter(msg.sender));
    // slither-disable-next-line reentrancy-events
    emit DelayedWithdrawalRegistered(positionId, token, msg.sender);
  }

  /// @inheritdoc IDelayedWithdrawalManager
  function withdraw(
    uint256 positionId,
    address token,
    address recipient
  )
    external
    returns (uint256 withdrawn, uint256 stillPending)
  {
    if (!VAULT.hasPermission(positionId, msg.sender, WITHDRAW_PERMISSION)) revert UnauthorizedWithdrawal();

    mapping(uint256 index => RegisteredAdapter registeredAdapter) storage registeredAdapters =
      _registeredAdapters.get(positionId, token);

    uint256 j = 0;
    uint256 i = 0;
    bool shouldContinue = true;
    while (shouldContinue) {
      RegisteredAdapter memory adapter = registeredAdapters[i];
      if (address(adapter.adapter) != address(0)) {
        // slither-disable-next-line calls-loop
        (uint256 _withdrawn, uint256 _stillPending) = adapter.adapter.withdraw(positionId, token, recipient);
        withdrawn += _withdrawn;
        stillPending += _stillPending;
        if (_stillPending != 0) {
          if (i != j) {
            registeredAdapters.set(j, adapter.adapter);
          }
          unchecked {
            ++j;
          }
        }
        unchecked {
          ++i;
        }
      }
      shouldContinue = adapter.isNextFilled;
    }
    registeredAdapters.pop({ start: j, end: i });
    // slither-disable-next-line reentrancy-events
    emit WithdrawnFunds(positionId, token, recipient, withdrawn);
  }

  function _revertIfNotCurrentStrategyAdapter(uint256 positionId, address token) internal view {
    (StrategyId strategyId, IEarnStrategy strategy) = VAULT.positionsStrategy(positionId);
    if (strategyId == StrategyIdConstants.NO_STRATEGY) revert AdapterMismatch();
    if (!strategy.supportsInterface(type(IEarnBalmyStrategy).interfaceId)) revert AdapterMismatch();
    IDelayedWithdrawalAdapter adapter = IEarnBalmyStrategy(address(strategy)).delayedWithdrawalAdapter(token);
    if (address(adapter) != msg.sender) revert AdapterMismatch();
  }
}

File 2 of 18 : INFTPermissions.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

interface INFTPermissions is IERC721 {
  type Permission is uint8;

  /// @notice A collection of permissions sets for a specific position
  struct PositionPermissions {
    // The id of the position
    uint256 positionId;
    // The permissions to assign to the position
    PermissionSet[] permissionSets;
  }

  /// @notice A set of permissions for a specific operator
  struct PermissionSet {
    // The address of the operator
    address operator;
    // The permissions given to the operator
    Permission[] permissions;
  }

  /**
   * @notice Emitted when permissions for a position are modified
   * @param positionId The id of the position
   * @param permissions The set of permissions that were updated
   */
  event ModifiedPermissions(uint256 positionId, PermissionSet[] permissions);

  /// @notice Thrown when a user tries to modify permissions for a position they do not own
  error NotOwner();

  /// @notice Thrown when a user tries to execute a permit with an expired deadline
  error ExpiredDeadline();

  /// @notice Thrown when a user tries to execute a permit with an invalid signature
  error InvalidSignature();

  /// @notice Thrown when a user tries perform an operation without permission
  error AccountWithoutPermission(uint256 positionId, address account, Permission permission);

  /**
   * @notice The domain separator used in the permit signature
   * @return The domain seperator used in encoding of permit signature
   */
  // solhint-disable-next-line func-name-mixedcase
  function DOMAIN_SEPARATOR() external view returns (bytes32);

  /**
   * @notice Returns the next nonce to use for a given user
   * @param account The address of the user
   * @return The next nonce to use
   */
  function nextNonce(address account) external view returns (uint256);

  /**
   * @notice Returns the block number where the ownership of the position last changed
   * @param positionId The position to check
   * @return The block number
   */
  function lastOwnershipChange(uint256 positionId) external view returns (uint256);

  /**
   * @notice Returns how many NFTs are currently tracked by this contract
   * @return How many of valid NFTs are tracked by this contract, where each one of
   *         them has an assigned and queryable owner not equal to the zero address
   */
  function totalSupply() external view returns (uint256);

  /**
   * @notice Returns whether the given account has the permission for the given position
   * @param positionId The id of the position to check
   * @param account The address of the user to check
   * @param permission The permission to check
   * @return Whether the user has the permission or not
   */
  function hasPermission(uint256 positionId, address account, Permission permission) external view returns (bool);

  /**
   * @notice Sets new permissions for the given positions
   * @dev Will revert if called by someone who is not the owner of all positions
   * @param positionPermissions A list of position permissions to set
   */
  function modifyPermissions(PositionPermissions[] calldata positionPermissions) external;

  /**
   * @notice Sets permissions via signature
   * @param permissions The permissions to set for the different positions
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param signature The signature
   */
  function permissionPermit(PositionPermissions[] calldata permissions, uint256 deadline, bytes calldata signature) external;
}

File 3 of 18 : IDelayedWithdrawalManager.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.8;

import { IEarnVault } from "@balmy/earn-core/interfaces/IEarnVault.sol";

/**
 * @title Delayed Withdrawal Manager Interface
 * @notice This contract will reference all delayed withdraws for all positions. When a delayed withdrawal is started,
 *         the Earn strategy will delegate the withdrawal to a delayed withdraw adapter. That adapter is the one that
 *         will start the withdraw, and then register itself to the manager. By doing so, we will be able to track all
 *         pending withdrawals for a specific position in one place (here).
 */
interface IDelayedWithdrawalManager {
  /**
   * @notice Thrown when trying to register a delayed withdraw from an address that doesn't correspond with the token
   *         adapter
   */
  error AdapterMismatch();

  /// @notice Thrown when trying to register a delayed withdraw for the same token and position twice
  error AdapterDuplicated();

  /// @notice Thrown when trying to withdraw funds for a position without withdrawal permission
  error UnauthorizedWithdrawal();

  /**
   * @notice Emitted when funds have been withdrawn
   * @param positionId The position to withdraw
   * @param token The token to withdraw
   * @param recipient The withdraw recipient
   * @param withdrawn How much was withdrawn
   */
  event WithdrawnFunds(uint256 positionId, address token, address recipient, uint256 withdrawn);

  /**
   * @notice Emitted when a delayed withdrawal is registered
   * @param positionId The position to associate the withdrawal to
   * @param token The token that is being withdrawn
   * @param adapter The delayed withdrawal adapter responsible for processing the withdrawal
   */
  event DelayedWithdrawalRegistered(uint256 positionId, address token, address adapter);

  /**
   * @notice Returns the address to Earn's vault
   * @return Earn's vault address
   */
  // slither-disable-next-line naming-convention
  function VAULT() external view returns (IEarnVault);

  /**
   * @notice Returns the estimated amount of funds that are pending for withdrawal. Note that this amount is estimated
   *         because the underlying farm might not be able to guarantee an exit amount when it is first started
   * @param positionId The position that executed the withdrawal
   * @param token The token that is being withdrawn
   * @return The estimated amount of funds that are pending for withdrawal
   */
  function estimatedPendingFunds(uint256 positionId, address token) external view returns (uint256);

  /**
   * @notice Returns the amount of funds that are available for withdrawal
   * @param positionId The position that executed the withdrawal
   * @param token The token that is being withdrawn
   * @return The amount of funds that are available for withdrawal
   */
  function withdrawableFunds(uint256 positionId, address token) external view returns (uint256);

  /**
   * @notice Returns the total amounts of funds that are pending or withdrawable, for a given position
   * @param positionId The position to check
   * @return tokens The position's tokens
   * @return estimatedPending The estimated amount of funds that are pending for withdrawal
   * @return withdrawable The amount of funds that are available for withdrawal
   */
  function allPositionFunds(uint256 positionId)
    external
    view
    returns (address[] memory tokens, uint256[] memory estimatedPending, uint256[] memory withdrawable);

  /**
   * @notice Registers a delayed withdrawal for the given position and token
   * @dev Must be called by a delayed withdrawal adapter that is referenced by the position's strategy
   * @param positionId The position to associate the withdrawal to
   * @param token The token that is being withdrawn
   */
  function registerDelayedWithdraw(uint256 positionId, address token) external;

  /**
   * @notice Completes a delayed withdrawal for a given position and token
   * @dev The caller must have withdraw permissions over the position
   *      If there are no withdrawable funds associated to the position, will just return 0
   * @param positionId The position that executed the withdrawal
   * @param token The token that is being withdrawn
   * @param recipient The account that will receive the funds
   * @return withdrawn How much was withdrawn
   * @return stillPending How much is still pending
   */
  function withdraw(
    uint256 positionId,
    address token,
    address recipient
  )
    external
    returns (uint256 withdrawn, uint256 stillPending);
}

File 4 of 18 : IDelayedWithdrawalAdapter.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.8;

import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { IEarnVault } from "@balmy/earn-core/interfaces/IEarnVault.sol";
import { IDelayedWithdrawalManager } from "./IDelayedWithdrawalManager.sol";

/**
 * @title Delayed Withdrawal Adapter Interface
 * @notice This contract will interact with one or more farms and handle the process of withdrawing funds that have a
 *         lock up period. When a withdrawal is initiated, adapters will register themselves to the manager, to
 *         help with discoverability. Only the manager can process a finished withdrawal
 */
interface IDelayedWithdrawalAdapter is IERC165 {
  /**
   * @notice Thrown when the sender is not the delayed withdrawal manager
   */
  error UnauthorizedDelayedWithdrawalManager();

  /**
   * @notice Thrown when the sender is not the position's strategy
   */
  error UnauthorizedPositionStrategy();

  /**
   * @notice Returns the address to Earn's vault
   * @return Earn's vault address
   */
  function vault() external view returns (IEarnVault);

  /**
   * @notice Returns the address to Earn's delayed withdrawal manager
   * @return Earn's delayed withdrawal manager's address
   */
  function manager() external view returns (IDelayedWithdrawalManager);

  /**
   * @notice Returns the estimated amount of funds that are pending for withdrawal. Note that this amount is estimated
   *         because the underlying farm might not be able to guarantee an exit amount when the withdraw is started
   * @param positionId The position that executed the withdrawal
   * @param token The token that is being withdrawn
   * @return The estimated amount of funds that are pending for withdrawal
   */
  function estimatedPendingFunds(uint256 positionId, address token) external view returns (uint256);

  /**
   * @notice Returns the amount of funds that are available for withdrawal
   * @param positionId The position that executed the withdrawal
   * @param token The token that is being withdrawn
   * @return The amount of funds that are available for withdrawal
   */
  function withdrawableFunds(uint256 positionId, address token) external view returns (uint256);

  /**
   * @notice Starts a delayed withdrawal, and associates it to the position
   * @dev Can only be called by the position's strategy.
   * @param positionId The position to associate to the withdrawal
   * @param token The token that is being withdrawn
   * @param amount The amount of input for the withdrawal to use. Each adapter might provide different meaning to this
   *               value
   */
  function initiateDelayedWithdrawal(uint256 positionId, address token, uint256 amount) external;

  /**
   * @notice Completes a delayed withdrawal for a given position and token
   * @dev Can only be called by the delayed withdrawal manager
   *      If there are no withdrawable funds associated to the given parameters, will just return 0
   * @param positionId The position that executed the withdrawal
   * @param token The token that is being withdrawn
   * @param recipient The account that will receive the funds
   * @return withdrawn How much was withdrawn
   * @return stillPending How much is still pending
   */
  function withdraw(
    uint256 positionId,
    address token,
    address recipient
  )
    external
    returns (uint256 withdrawn, uint256 stillPending);
}

File 5 of 18 : StrategyId.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.8;

type StrategyId is uint96;

using { increment, equals as ==, notEquals as != } for StrategyId global;

function increment(StrategyId id) pure returns (StrategyId) {
  return StrategyId.wrap(StrategyId.unwrap(id) + 1);
}

// slither-disable-next-line dead-code
function equals(StrategyId id1, StrategyId id2) pure returns (bool) {
  return StrategyId.unwrap(id1) == StrategyId.unwrap(id2);
}

function notEquals(StrategyId id1, StrategyId id2) pure returns (bool) {
  return StrategyId.unwrap(id1) != StrategyId.unwrap(id2);
}

library StrategyIdConstants {
  StrategyId internal constant NO_STRATEGY = StrategyId.wrap(0);
  StrategyId internal constant INITIAL_STRATEGY_ID = StrategyId.wrap(1);
}

File 6 of 18 : IEarnBalmyStrategy.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.8;

import { IEarnStrategy } from "@balmy/earn-core/interfaces/IEarnStrategy.sol";
import { IDelayedWithdrawalAdapter } from "./IDelayedWithdrawalAdapter.sol";

/// @notice Interface for Earn strategies that support delayed withdrawals
interface IEarnBalmyStrategy is IEarnStrategy {
  /**
   * @notice Returns the "delayed withdrawal" adapter that will be used for the given token
   * @param token The token to use the adapter for
   * @return The address of the "delayed withdrawal" adapter, or the zero address if none is configured
   */
  function delayedWithdrawalAdapter(address token) external view returns (IDelayedWithdrawalAdapter);

  /**
   * @notice Returns a yield coefficient that, when calculated over time, describes how much yield was
   *         generated by the strategy
   * @return coefficient The yield coefficient
   * @return multiplier The multiplier used for the coefficient
   */
  function assetYieldCoefficient() external view returns (uint256 coefficient, uint256 multiplier);

  /**
   * @notice Returns the rewards emitted per second for each reward token. This value represents how many
   *         reward tokens are emitted per deposited asset
   * @return emissions The rewards emitted per second for each reward token
   * @return multipliers The multiplier used for each emission value
   */
  function rewardEmissionsPerSecondPerAsset()
    external
    view
    returns (uint256[] memory emissions, uint256[] memory multipliers);
}

File 7 of 18 : RegisteredAdapters.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.22;

import { IDelayedWithdrawalAdapter } from "src/interfaces/IDelayedWithdrawalAdapter.sol";

struct RegisteredAdapter {
  IDelayedWithdrawalAdapter adapter;
  bool isNextFilled;
}

/// @notice A key composed of a position id and a token address
type PositionIdTokenKey is bytes32;

library RegisteredAdaptersLibrary {
  /// @notice Get all adapters for a position and token
  function get(
    mapping(uint256 => mapping(address => mapping(uint256 index => RegisteredAdapter registeredAdapter))) storage
      registeredAdapters,
    uint256 positionId,
    address token
  )
    internal
    view
    returns (mapping(uint256 index => RegisteredAdapter registeredAdapter) storage registeredAdapter)
  {
    return registeredAdapters[positionId][token];
  }

  /// @notice Checks if an adapter is repeated in the list of registered adapters for a position and token
  function isRepeated(
    mapping(uint256 index => RegisteredAdapter registeredAdapter) storage registeredAdapters,
    IDelayedWithdrawalAdapter adapter
  )
    internal
    view
    returns (bool, uint256)
  {
    uint256 length = 0;
    bool shouldContinue = true;
    while (shouldContinue) {
      RegisteredAdapter memory adapterToCompare = registeredAdapters[length];
      if (adapterToCompare.adapter == adapter) {
        // Since we won't be using the length, we can return any value
        return (true, 0);
      }
      if (address(adapterToCompare.adapter) != address(0)) {
        unchecked {
          ++length;
        }
      }
      shouldContinue = adapterToCompare.isNextFilled;
    }

    return (false, length);
  }

  function set(
    mapping(uint256 index => RegisteredAdapter registeredAdapter) storage registeredAdapters,
    uint256 index,
    IDelayedWithdrawalAdapter adapter
  )
    internal
  {
    if (index != 0) registeredAdapters[index - 1].isNextFilled = true;
    registeredAdapters[index] = RegisteredAdapter({ adapter: adapter, isNextFilled: false });
  }

  function pop(
    mapping(uint256 index => RegisteredAdapter registeredAdapter) storage registeredAdapters,
    uint256 start,
    uint256 end
  )
    internal
  {
    for (uint256 i = start; i < end; ++i) {
      delete registeredAdapters[i];
    }
  }
}

File 8 of 18 : PayableMulticall.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.22;

import { Address } from "@openzeppelin/contracts/utils/Address.sol";

/**
 * @dev Adding this contract will enable batching calls. This is basically the same as Open Zeppelin's
 *      Multicall contract, but we have made it payable. It supports both payable and non payable
 *      functions. However, if `msg.value` is not zero, then non payable functions cannot be called.
 *      Any contract that uses this Multicall version should be very careful when using msg.value.
 *      For more context, read: https://github.com/Uniswap/v3-periphery/issues/52
 */
abstract contract PayableMulticall {
  /**
   * @notice Receives and executes a batch of function calls on this contract.
   * @param data A list of different function calls to execute
   * @return results The result of executing each of those calls
   */
  function multicall(bytes[] calldata data) external payable returns (bytes[] memory results) {
    results = new bytes[](data.length);
    for (uint256 i = 0; i < data.length; ++i) {
      results[i] = Address.functionDelegateCall(address(this), data[i]);
    }
    return results;
  }
}

File 9 of 18 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC-721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC-721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 10 of 18 : IEarnVault.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.8;

import { INFTPermissions } from "@balmy/nft-permissions/interfaces/INFTPermissions.sol";
import { IEarnStrategyRegistry } from "../interfaces/IEarnStrategyRegistry.sol";
import { IEarnStrategy } from "./IEarnStrategy.sol";
import { IEarnNFTDescriptor } from "./IEarnNFTDescriptor.sol";
import { StrategyId } from "../types/StrategyId.sol";
import { SpecialWithdrawalCode } from "../types/SpecialWithdrawals.sol";

/**
 * @title Earn Vault Interface
 * @notice Earn's vault is the place where users deposit and withdraw their funds and rewards. Earn has a singleton
 *         vault contract, which will be the one that keeps track of who owns what. It is also the one that implements
 *         access control and makes sure that users can only access their own funds.
 */
interface IEarnVault is INFTPermissions {
  /// @notice Thrown when a user tries to make an empty deposit
  error ZeroAmountDeposit();

  /// @notice Thrown when a deposit earns the caller zero shares
  error ZeroSharesDeposit();

  /// @notice Thrown when the withdraw input is invalid
  error InvalidWithdrawInput();

  /// @notice Thrown when trying to withdraw amount exceeds balance
  error InsufficientFunds();

  /// @notice Emitted when a new position is created
  event PositionCreated(
    uint256 indexed positionId,
    address indexed owner,
    StrategyId strategyId,
    address depositedToken,
    uint256 depositedAmount,
    uint256 assetsDeposited,
    INFTPermissions.PermissionSet[] permissions,
    bytes misc
  );

  /// @notice Emitted when a new position is increased
  event PositionIncreased(
    uint256 indexed positionId, address depositedToken, uint256 depositedAmount, uint256 assetsDeposited
  );

  /// @notice Emitted when a new position is withdrawn
  event PositionWithdrawn(uint256 indexed positionId, address[] tokens, uint256[] withdrawn, address recipient);

  /// @notice Emitted when a new position is withdrawn specially
  event PositionWithdrawnSpecially(
    uint256 indexed positionId,
    address[] tokens,
    uint256[] balanceChanges,
    address[] actualWithdrawnTokens,
    uint256[] actualWithdrawnAmounts,
    bytes result,
    address recipient
  );

  /**
   * @notice Returns the role in charge of pausing/unpausing deposits
   * @return The role in charge of pausing/unpausing deposits
   */
  // slither-disable-next-line naming-convention
  function PAUSE_ROLE() external pure returns (bytes32);

  /**
   * @notice Returns the id of the "increase" permission
   * @return The id of the "increase" permission
   */
  // slither-disable-next-line naming-convention
  function INCREASE_PERMISSION() external pure returns (Permission);

  /**
   * @notice Returns the id of the "withdraw" permission
   * @return The id of the "withdraw" permission
   */
  // slither-disable-next-line naming-convention
  function WITHDRAW_PERMISSION() external pure returns (Permission);

  /**
   * @notice Returns the address of the strategy registry
   * @return The address of the strategy registry
   */
  // slither-disable-next-line naming-convention
  function STRATEGY_REGISTRY() external view returns (IEarnStrategyRegistry);

  /**
   * @notice Returns the NFT descriptor contract
   * @return The contract for the NFT descriptor
   */
  // slither-disable-next-line naming-convention
  function NFT_DESCRIPTOR() external view returns (IEarnNFTDescriptor);

  /**
   * @notice Returns the strategy chosen by the given position
   * @param positionId The position to check
   * @return strategyId The strategy chosen by the given position. Will return 0 if the position doesn't exist
   * @return strategy The strategy's address. Will return the zero address if the position doesn't exist
   */
  function positionsStrategy(uint256 positionId) external view returns (StrategyId strategyId, IEarnStrategy strategy);

  /**
   * @notice Returns a summary of the position's balances
   * @param positionId The position to check the balances for
   * @return tokens All of the position's tokens
   * @return balances Total balance of each token
   * @return strategyId The position's strategy id
   * @return strategy The position's strategy address
   */
  function position(uint256 positionId)
    external
    view
    returns (address[] memory tokens, uint256[] memory balances, StrategyId strategyId, IEarnStrategy strategy);

  /**
   * @notice Returns if deposits and paused
   * @return Whether deposits are paused or not
   */
  function paused() external view returns (bool);

  /**
   * @notice Creates a new position with the given strategy owner and permissions
   * @param strategyId The strategy to use for this position
   * @param depositToken The token to use for the initial deposit
   * @param depositAmount The amount to deposit. If it's max(uint256), then all balance will be taken from the caller
   *                      Using max(uint256) and the native token will end up in revert
   * @param owner The owner to set for the position
   * @param permissions The permissions to set for the position
   * @param strategyValidationData Data used by the strategy to determine if the position can be created
   * @param misc Miscellaneous bytes to emit, can work for referrals and more
   * @return positionId The id of the created position
   * @return assetsDeposited How much was actually deposited in terms of the asset
   */
  function createPosition(
    StrategyId strategyId,
    address depositToken,
    uint256 depositAmount,
    address owner,
    INFTPermissions.PermissionSet[] calldata permissions,
    bytes calldata strategyValidationData,
    bytes calldata misc
  )
    external
    payable
    returns (uint256 positionId, uint256 assetsDeposited);

  /**
   * @notice Deposits more funds into an existing position
   * @dev The caller must have permissions to increase the position
   * @param positionId The position to add funds to
   * @param depositToken The token to use for the initial deposit
   * @param depositAmount The amount to deposit. If it's max(uint256), then all balance will be taken from the caller
   *                      Using max(uint256) and the native token will end up in revert
   * @return assetsDeposited How much was actually deposited in terms of the asset
   */
  function increasePosition(
    uint256 positionId,
    address depositToken,
    uint256 depositAmount
  )
    external
    payable
    returns (uint256 assetsDeposited);

  /**
   * @notice Withdraws funds from an existing position
   * @dev The caller must have permissions to withdraw from the position
   * @param positionId The position to withdraw funds from
   * @param tokensToWithdraw All position's tokens, in the same order as returned on `position`
   * @param intendedWithdraw The amounts to withdraw from the position. You can set to max(uint256) to withdraw all
   *                         that's available. Note that if a token doesn't support and immediate withdrawal, then
   *                         a delayed withdrawal will be started
   * @param recipient The account that will receive the withdrawn funds
   * @return withdrawn How much was actually withdrawn from each token
   * @return withdrawalTypes The type of withdrawal for each token
   */
  function withdraw(
    uint256 positionId,
    address[] calldata tokensToWithdraw,
    uint256[] calldata intendedWithdraw,
    address recipient
  )
    external
    payable
    returns (uint256[] memory withdrawn, IEarnStrategy.WithdrawalType[] memory withdrawalTypes);

  /**
   * @notice Performs a special withdrawal against the strategy. This is meant to be used be used in special cases, like
   *         withdrawing the farm token directly, instead of the asset. The withdraw data and result can be different
   *         for each strategy
   * @dev The caller must have permissions to withdraw from the position
   * @param positionId The position to withdraw funds from
   * @param withdrawalCode The code that identifies the special withdrawal
   * @param toWithdraw Amounts to withdraw, based on the withdrawal code. Does not need to have the same
   *                   length or order as `tokens`
   * @param withdrawalData The data that defines the withdrawal
   * @param recipient The account that will receive the withdrawn funds
   * @return tokens All of the position's tokens
   * @return balanceChanges Changes in the position's balances, in the same order as `tokens`
   * @return actualWithdrawnTokens The tokens that were actually withdrawn
   * @return actualWithdrawnAmounts How much was withdrawn from each token
   * @return result The result of the withdrawal. Can be different for each strategy
   */
  function specialWithdraw(
    uint256 positionId,
    SpecialWithdrawalCode withdrawalCode,
    uint256[] calldata toWithdraw,
    bytes calldata withdrawalData,
    address recipient
  )
    external
    payable
    returns (
      address[] memory tokens,
      uint256[] memory balanceChanges,
      address[] memory actualWithdrawnTokens,
      uint256[] memory actualWithdrawnAmounts,
      bytes memory result
    );

  /**
   * @notice Pauses position creations and increases
   * @dev Can only be called by someone with the `PAUSE_ROLE` role
   */
  function pause() external payable;

  /**
   * @notice Unpauses position creations and increases
   * @dev Can only be called by someone with the `PAUSE_ROLE` role
   */
  function unpause() external payable;
}

File 11 of 18 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

File 12 of 18 : IEarnStrategy.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.8;

import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { IEarnStrategyRegistry } from "./IEarnStrategyRegistry.sol";
import { IEarnVault } from "./IEarnVault.sol";
import { StrategyId } from "../types/StrategyId.sol";
import { SpecialWithdrawalCode } from "../types/SpecialWithdrawals.sol";

/**
 * @title Earn Strategy Interface
 * @notice In Earn, a strategy will take an asset (could be ERC20 or native) and generate yield with it. The generated
 *         yield could be in the same asset, or in other tokens. One strategy could generate yield on multiple tokens
 *         at the same time
 * @dev For the proper functioning of the platform, there are some restrictions that strategy devs must consider:
 *      - The asset cannot change over time, it must always be the same
 *      - The strategy must report the asset as the first token
 *      - Tokens can never be removed from the list. New ones can be added, but they cannot be removed later
 *      - Functions like `withdrawalTypes` and `balances` must return the same amount of values as `tokens`, and
 *        in the same order too
 *      - Tokens with very high supplies or a high amount of decimals might not fit correctly into the Vault's
 *        accounting system. For more information about this, please refer to the [README](../vault/README.md).
 *      Take into account some strategies might not support an immediate withdraw of all tokens, since they
 *      might implement a lock up period. If that's the case, then executing a withdraw will start what we call a
 *      "delayed withdrawal". This is a process where the funds are sent to a "delayed withdrawal" adapter,
 *      which is in charge of handling this process. Users will be able to monitor their funds through the
 *      "delayed withdrawal" manager, who aggregates all available adapters. Finally, once the withdrawal can be
 *      executed, only those with withdraw permissions over the position will be able to retrieve the funds
 */
interface IEarnStrategy is IERC165 {
  /// @notice The type of withdrawal supported for a specific token
  enum WithdrawalType {
    IMMEDIATE,
    DELAYED
  }

  /// @notice The type of fee charged for a specific token
  enum FeeType {
    PERFORMANCE,
    DEPOSIT,
    WITHDRAW,
    RESCUE,
    OTHER
  }

  /**
   * @notice Returns the address to Earn's vault
   * @return Earn's vault address
   */
  function vault() external view returns (IEarnVault);

  /**
   * @notice Returns the address to Earn's strategy registry
   * @return Earn's strategy registry address
   */
  function registry() external view returns (IEarnStrategyRegistry);

  /**
   * @notice Returns the asset this strategy will use to generate yield
   * @return The asset this strategy will use to generate yield
   */
  function asset() external view returns (address);

  /**
   * @notice Returns the strategy's description
   * @return The strategy's description
   */
  function description() external view returns (string memory);

  /**
   * @notice Returns all tokens under the strategy's control
   * @dev The asset must be the first token returned
   * @return tokens All tokens under the strategy's control
   */
  function allTokens() external view returns (address[] memory tokens);

  /**
   * @notice Returns the types of withdrawals supported for each token
   * @dev In the same order returned as `tokens`
   * @return The types of withdrawals for each token
   */
  function supportedWithdrawals() external view returns (WithdrawalType[] memory);

  /**
   * @notice Returns whether a specific token can be used to deposit funds into the strategy
   * @param depositToken The token to check
   * @return Whether the given token can be used to deposit funds into the strategy
   */
  function isDepositTokenSupported(address depositToken) external view returns (bool);

  /**
   * @notice Returns all tokens that can be used  to deposit funds into the strategy
   * @return All tokens that can be used  to deposit funds into the strategy
   */
  function supportedDepositTokens() external view returns (address[] memory);

  /**
   * @notice Returns how much can be deposited from the given token
   * @dev Will return 0 if the token is not supported
   * @return How much can be deposited from the given token
   */
  function maxDeposit(address depositToken) external view returns (uint256);

  /**
   * @notice Returns how many tokens are currently under the strategy's control
   */
  function totalBalances() external view returns (address[] memory tokens, uint256[] memory balances);

  /**
   * @notice Returns whether a specific withdrawal method can be used
   * @param withdrawalCode The withdrawal method to check
   * @return Whether the given withdrawal method can be used to withdraw funds
   */
  function isSpecialWithdrawalSupported(SpecialWithdrawalCode withdrawalCode) external view returns (bool);

  /**
   * @notice Returns all withdrawal methods can be used  to withdraw funds
   * @return All withdrawal methods can be used  to withdraw funds
   */
  function supportedSpecialWithdrawals() external view returns (SpecialWithdrawalCode[] memory);

  /**
   * @notice Returns how much can be withdrawn at this moment
   * @return tokens All tokens under the strategy's control
   * @return withdrawable How much can be withdrawn for each one
   */
  function maxWithdraw() external view returns (address[] memory tokens, uint256[] memory withdrawable);

  /**
   * @notice Returns how much is charged in terms of fees, for each token
   * @return types The type of fee charged for each token
   * @return bps How much fee is being charged, in basis points
   */
  function fees() external view returns (FeeType[] memory types, uint16[] memory bps);

  /**
   * @notice Notifies the strategy that funds have been deposited into it
   * @dev Will revert if the given token is not supported
   * @param depositToken The token that was deposited
   * @param depositAmount The amount that was deposited
   * @return assetsDeposited How much was deposited, measured in asset
   */
  function deposited(address depositToken, uint256 depositAmount) external payable returns (uint256 assetsDeposited);

  /**
   * @notice Executes a withdraw, for the given tokens and amounts. If a token only supports delayed withdrawals,
   *         then one will be started and associated to the given position
   * @dev Can only be called by the vault
   * @param positionId The position that initiated the withdrawal
   * @param tokens All tokens supported by the strategy, in the same order as in `tokens`
   * @param toWithdraw How much to withdraw from each
   * @param recipient The account that will receive the tokens
   * @return The types of withdrawals for each token
   */
  function withdraw(
    uint256 positionId,
    address[] memory tokens,
    uint256[] memory toWithdraw,
    address recipient
  )
    external
    returns (WithdrawalType[] memory);

  /**
   * @notice Executes a special withdraw
   * @dev Can only be called by the vault
   * @param positionId The position that initiated the withdrawal
   * @param withdrawalCode The code that identifies the type of withdrawal
   * @param toWithdraw Amounts to withdraw, based on the withdrawal code. Does not need to have the same
   *                   length or order as `tokens`
   * @param withdrawalData Data necessary to execute the withdrawal
   * @param recipient The account that will receive the tokens
   * @return balanceChanges Changes in the position's balances
   * @return actualWithdrawnTokens The tokens that were actually withdrawn
   * @return actualWithdrawnAmounts How much was withdrawn from each token
   * @return result Some custom data related to the withdrawal
   */
  function specialWithdraw(
    uint256 positionId,
    SpecialWithdrawalCode withdrawalCode,
    uint256[] calldata toWithdraw,
    bytes calldata withdrawalData,
    address recipient
  )
    external
    returns (
      uint256[] memory balanceChanges,
      address[] memory actualWithdrawnTokens,
      uint256[] memory actualWithdrawnAmounts,
      bytes memory result
    );

  /**
   * @notice Migrates all tokens and data to a new strategy
   * @dev Can only be called by the strategy registry
   * @param newStrategy The strategy to migrate to
   * @param migrationData Data to be used as part of the migration
   * @return Data related to the result of the migration. Will be sent to the new strategy
   */
  function migrateToNewStrategy(
    IEarnStrategy newStrategy,
    bytes calldata migrationData
  )
    external
    returns (bytes memory);

  /**
   * @notice Performs any necessary preparations to be used by the vault
   * @dev Can only be called by the strategy registry
   * @param strategyId The id that this strategy was registered to
   * @param oldStrategy The previous strategy registered to the id. Will be the zero address if this is the first
   *                    strategy registered to the id
   * @param migrationResultData Data sent by the previous strategy registered to the id. Will be empty if this is the
   *                      first strategy registered to the id
   */
  function strategyRegistered(
    StrategyId strategyId,
    IEarnStrategy oldStrategy,
    bytes calldata migrationResultData
  )
    external;

  /**
   * @notice Validates if the position can be created for this strategy, and fails it it can't
   * @param sender The address to be checked
   * @param creationData The hash to check with the sender
   */
  function validatePositionCreation(address sender, bytes calldata creationData) external;
}

File 13 of 18 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)

pragma solidity ^0.8.20;

import {Errors} from "./Errors.sol";

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert Errors.InsufficientBalance(address(this).balance, amount);
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert Errors.FailedCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {Errors.FailedCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            assembly ("memory-safe") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}

File 14 of 18 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 15 of 18 : IEarnStrategyRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.8;

import { IEarnStrategy } from "./IEarnStrategy.sol";
import { StrategyId } from "../types/StrategyId.sol";

/**
 * @title Earn Strategy Registry Interface
 * @notice This contract will act as a registry, so that Earn strategies can be updated. It will force a delay before
 *         strategies can be updated
 */
interface IEarnStrategyRegistry {
  /// @notice Thrown when trying to register a strategy that is already registered
  error StrategyAlreadyRegistered();

  /// @notice Thrown when trying to propose a strategy update that has another pending proposal
  error StrategyAlreadyProposedUpdate();

  /// @notice Thrown when trying to propose a strategy update that doesn't have the same asset as the current strategy
  error AssetMismatch();

  /**
   * @notice Thrown when trying to propose a strategy update that doesn't support at least same tokens as the current
   *         strategy
   */
  error TokensSupportedMismatch();

  /**
   * @notice Thrown when the sender is not the owner of the strategy.
   */
  error UnauthorizedStrategyOwner();

  /**
   * @notice Thrown when the sender is not the strategy ownership receiver.
   */
  error UnauthorizedOwnershipReceiver();

  /**
   * @notice Thrown when trying to register an address that is not an strategy
   * @param notStrategy The address that was not a strategy
   */
  error AddressIsNotStrategy(IEarnStrategy notStrategy);

  /**
   * @notice Thrown when trying to register an strategy that does no report the asset as first token
   * @param invalidStrategy The object that was not a valid strategy
   */
  error AssetIsNotFirstToken(IEarnStrategy invalidStrategy);

  /**
   * @notice Thrown when trying to cancel a proposed update, but no new strategy has been proposed for the strategy id
   * @param strategyId The strategy id without a proposed update
   */
  error MissingStrategyProposedUpdate(StrategyId strategyId);

  /**
   * @notice Thrown when trying to confirm the proposed update before the delay has passed
   * @param strategyId The strategy id to update
   */
  error StrategyUpdateBeforeDelay(StrategyId strategyId);

  /**
   * @notice Thrown when the migration data doesn't match the expected one
   * @param strategyId The strategy id to update
   */
  error MigrationDataMismatch(StrategyId strategyId);

  /// @notice Thrown when trying to propose a strategy ownership transfer that has a pending proposal
  error StrategyOwnershipTransferAlreadyProposed();

  /// @notice Thrown when trying to cancel a proposed strategy ownership transfer without a pending proposal
  error StrategyOwnershipTransferWithoutPendingProposal();

  /// @notice Thrown when trying to confirm the proposed update with lower balances than the current one
  error ProposedStrategyBalancesAreLowerThanCurrentStrategy();

  /**
   * @notice Emitted when a new strategy is registered
   * @param owner The strategy's owner
   * @param strategyId The strategy id
   * @param strategy The strategy
   */
  event StrategyRegistered(address owner, StrategyId strategyId, IEarnStrategy strategy);

  /**
   * @notice Emitted when a new strategy is proposed
   * @param strategyId The strategy id
   * @param newStrategy The strategy
   * @param migrationData Data to be used as part of the migration
   */
  event StrategyUpdateProposed(StrategyId strategyId, IEarnStrategy newStrategy, bytes migrationData);

  /**
   * @notice Emitted when a new strategy is updated
   * @param strategyId The strategy id
   * @param newStrategy The strategy
   */
  event StrategyUpdated(StrategyId strategyId, IEarnStrategy newStrategy);

  /**
   * @notice Emitted when a proposed update is canceled
   * @param strategyId The strategy id
   * @param strategy The strategy we were going to update the id to
   */
  event StrategyUpdateCanceled(StrategyId strategyId, IEarnStrategy strategy);

  /**
   * @notice Emitted when a proposed strategy ownership transfer is proposed
   * @param strategyId The strategy id
   * @param newOwner The proposed new owner
   */
  event StrategyOwnershipTransferProposed(StrategyId strategyId, address newOwner);

  /**
   * @notice Emitted when a proposed strategy ownership transfer is canceled
   * @param strategyId The strategy id
   * @param receiver The canceled receiver
   */
  event StrategyOwnershipTransferCanceled(StrategyId strategyId, address receiver);

  /**
   * @notice Emitted when a strategy ownership is transferred
   * @param strategyId The strategy id
   * @param newOwner The new owner
   */
  event StrategyOwnershipTransferred(StrategyId strategyId, address newOwner);

  /**
   * @notice Returns the delay (in seconds) necessary to execute a proposed strategy update
   * @return The delay (in seconds) necessary to execute a proposed strategy update
   */
  // slither-disable-next-line naming-convention
  function STRATEGY_UPDATE_DELAY() external pure returns (uint256);

  /**
   * @notice Returns the strategy registered to the given id
   * @param strategyId The id to check
   * @return The registered strategy, or the zero address if none is registered
   */
  function getStrategy(StrategyId strategyId) external view returns (IEarnStrategy);

  /**
   * @notice Returns the id that is assigned to the strategy
   * @param strategy The strategy to check
   * @return The assigned if, or zero if it hasn't been assigned
   */
  function assignedId(IEarnStrategy strategy) external view returns (StrategyId);

  /**
   * @notice Returns any proposed update for the given strategy id
   * @param strategyId The id to check for proposed updates
   * @return newStrategy The new strategy
   * @return executableAt When the update will be executable
   */
  function proposedUpdate(StrategyId strategyId)
    external
    view
    returns (IEarnStrategy newStrategy, uint96 executableAt, bytes32 migrationDataHash);

  /**
   * @notice Returns any proposed ownership transfer for the given strategy id
   * @param strategyId The id to check for proposed ownership transfers
   * @return newOwner The new owner, or the zero address if no transfer was proposed
   */
  function proposedOwnershipTransfer(StrategyId strategyId) external view returns (address newOwner);

  /**
   * @notice Returns the number of registered strategies
   * @return The number of registered strategies
   */
  function totalRegistered() external view returns (uint256);

  /**
   * @notice Registers a new strategy
   * @dev The strategy must report the asset as the first token
   *      The strategy can't be associated to another id
   *      The new strategy must support the expected interface.
   * @param firstOwner The strategy's owner
   * @param strategy The strategy to register
   * @return The id assigned to the new strategy
   */
  function registerStrategy(address firstOwner, IEarnStrategy strategy) external returns (StrategyId);

  /**
   * @notice Returns the strategy's owner to the given id
   * @param strategyId The id to check
   * @return The owner of the strategy, or the zero address if none is registered
   */
  function owner(StrategyId strategyId) external view returns (address);

  /**
   * @notice Proposes an ownership transfer. Must be accepted by the new owner
   * @dev Can only be called by the strategy's owner
   * @param strategyId The id of the strategy to change ownership of
   * @param newOwner The new owner
   */
  function proposeOwnershipTransfer(StrategyId strategyId, address newOwner) external;

  /**
   * @notice Cancels an ownership transfer
   * @dev Can only be called by the strategy's owner
   * @param strategyId The id of the strategy that was being transferred
   */
  function cancelOwnershipTransfer(StrategyId strategyId) external;

  /**
   * @notice Accepts an ownership transfer, and updates the owner by doing so
   * @dev Can only be called by the strategy's new owner
   * @param strategyId The id of the strategy that was being transferred
   */
  function acceptOwnershipTransfer(StrategyId strategyId) external;

  /**
   * @notice Proposes a strategy update
   * @dev Can only be called by the strategy's owner.
   *      The strategy must report the asset as the first token
   *      The new strategy can't be associated to another id, neither can it be in the process of being associated to
   *      another id.
   *      The new strategy must support the expected interface.
   *      The new strategy must have the same asset as the strategy it's replacing.
   *      The new strategy must support the same tokens as the strategy it's replacing. It may also support new ones.
   * @param strategyId The strategy to update
   * @param newStrategy The new strategy to associate to the id
   * @param migrationData Data to be used as part of the migration
   */
  function proposeStrategyUpdate(
    StrategyId strategyId,
    IEarnStrategy newStrategy,
    bytes calldata migrationData
  )
    external;

  /**
   * @notice Cancels a strategy update
   * @dev Can only be called by the strategy's owner
   * @param strategyId The strategy that was being updated
   */
  function cancelStrategyUpdate(StrategyId strategyId) external;

  /**
   * @notice Updates a strategy, after the delay has passed
   * @dev The migration data must be the same as the ones passed during the proposal
   * @param strategyId The strategy to update
   * @param migrationData Data to be used as part of the migration
   */
  function updateStrategy(StrategyId strategyId, bytes calldata migrationData) external;
}

File 16 of 18 : IEarnNFTDescriptor.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.8;

import { IEarnVault } from "./IEarnVault.sol";

/**
 * @title The interface for generating a description for a position in a Earn Vault
 * @notice Contracts that implement this interface must return a base64 JSON with the entire description
 */
interface IEarnNFTDescriptor {
  /**
   * @notice Generates a positions's description, both the JSON and the image inside
   * @param vault The address of the Earn Vault
   * @param positionId The position id
   * @return description The position's description
   */
  function tokenURI(IEarnVault vault, uint256 positionId) external view returns (string memory description);
}

File 17 of 18 : SpecialWithdrawals.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.8;

type SpecialWithdrawalCode is uint256;

using { equals as ==, notEquals as != } for SpecialWithdrawalCode global;

// slither-disable-next-line dead-code
function equals(SpecialWithdrawalCode code1, SpecialWithdrawalCode code2) pure returns (bool) {
  return SpecialWithdrawalCode.unwrap(code1) == SpecialWithdrawalCode.unwrap(code2);
}

// slither-disable-next-line dead-code
function notEquals(SpecialWithdrawalCode code1, SpecialWithdrawalCode code2) pure returns (bool) {
  return SpecialWithdrawalCode.unwrap(code1) != SpecialWithdrawalCode.unwrap(code2);
}

/**
 * @title Special withdrawals
 * @notice There are some cases where we might want to perform a special withdrawal. For example, if a
 *         token only supports a delayed withdrawal, we might want to withdraw the farm token directly and
 *         sell it on the market, instead of waiting for the normal process.
 *         Since each strategy could support different types of withdrawals, we need to define a "protocol"
 *         on how to execute and interpret each of them. Input and output are encoded as bytes, in here we'll
 *         specify how to encode/decode them.
 */
library SpecialWithdrawal {
  /*
   * Withdraws the asset's farm token directly, by specifying the amount of farm tokens to withdraw
   */
  SpecialWithdrawalCode internal constant WITHDRAW_ASSET_FARM_TOKEN_BY_AMOUNT = SpecialWithdrawalCode.wrap(0);

  /*
   * Withdraws the asset's farm token directly, by specifying the equivalent in terms of the asset
   */
  SpecialWithdrawalCode internal constant WITHDRAW_ASSET_FARM_TOKEN_BY_ASSET_AMOUNT = SpecialWithdrawalCode.wrap(1);
}

File 18 of 18 : Errors.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}

Settings
{
  "remappings": [
    "@prb/test/=node_modules/@prb/test/src/",
    "ds-test/=node_modules/ds-test/src/",
    "forge-std/=node_modules/forge-std/src/",
    "@clones/=node_modules/clones/src/",
    "src/=src/",
    "@openzeppelin/=node_modules/@openzeppelin/",
    "@balmy/nft-permissions/=node_modules/@balmy/nft-permissions/src/",
    "@balmy/nft-permissions-test/=node_modules/@balmy/nft-permissions/test/",
    "@balmy/earn-core/=node_modules/@balmy/earn-core/src/",
    "@balmy/earn-core-test/=node_modules/@balmy/earn-core/test/",
    "@forta/firewall/=node_modules/@forta/firewall/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {
    "node_modules/@forta/firewall/src/Quantization.sol": {
      "Quantization": "0x7A9FE945B4eaC7693E10fcD4F510f97a295307A4"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IEarnVault","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AdapterDuplicated","type":"error"},{"inputs":[],"name":"AdapterMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[],"name":"UnauthorizedWithdrawal","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"adapter","type":"address"}],"name":"DelayedWithdrawalRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawn","type":"uint256"}],"name":"WithdrawnFunds","type":"event"},{"inputs":[],"name":"VAULT","outputs":[{"internalType":"contract IEarnVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"allPositionFunds","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"estimatedPending","type":"uint256[]"},{"internalType":"uint256[]","name":"withdrawable","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"address","name":"token","type":"address"}],"name":"estimatedPendingFunds","outputs":[{"internalType":"uint256","name":"pendingFunds","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"address","name":"token","type":"address"}],"name":"registerDelayedWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"withdrawn","type":"uint256"},{"internalType":"uint256","name":"stillPending","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"address","name":"token","type":"address"}],"name":"withdrawableFunds","outputs":[{"internalType":"uint256","name":"funds","type":"uint256"}],"stateMutability":"view","type":"function"}]

60c080604052346100d557602081611628803803809161001f82856100ec565b8339810103126100d557516001600160a01b038116908181036100d557608052604051635faf45bd60e01b815290602090829060049082905afa9081156100e1575f916100a1575b5060a0526040516115049081610124823960805181818161027b015281816105a901528181610c7a0152610f27015260a051816102390152f35b90506020813d6020116100d9575b816100bc602093836100ec565b810103126100d5575160ff811681036100d5575f610067565b5f80fd5b3d91506100af565b6040513d5f823e3d90fd5b601f909101601f19168101906001600160401b0382119082101761010f57604052565b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f3560e01c80632cedc68a14610f4b578063411557d114610efb5780635452666714610c13578063ac9650d8146109d9578063b263b7cc1461054e578063b460af94146101c55763b57f84ac14610066575f80fd5b346101af5760406003193601126101af5760043561008261109b565b905f90805f526020925f845260405f209173ffffffffffffffffffffffffffffffffffffffff928383165f52855260405f20915f9160019360015b6100cb578787604051908152f35b835f5280885260405f20604051906100e2826110f1565b549060ff8a89841692838152019260a01c161515825280610107575b505115156100bd565b6040517fb57f84ac0000000000000000000000000000000000000000000000000000000081526004810185905273ffffffffffffffffffffffffffffffffffffffff8616602482015295989195908a90829060449082905afa80156101ba5787915f91610183575b5061017a919261117b565b9701935f6100fe565b8092508b8092503d83116101b3575b61019c818361113a565b810103126101af5751869061017a61016f565b5f80fd5b503d610192565b6040513d5f823e3d90fd5b346101af5760606003193601126101af576101de61109b565b73ffffffffffffffffffffffffffffffffffffffff60443516604435036101af575f806040517f823abfd9000000000000000000000000000000000000000000000000000000008152600435600482015233602482015260ff7f000000000000000000000000000000000000000000000000000000000000000016604482015260208160648173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9081156101ba575f9161051f575b50156104f5576004355f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff84165f5260205260405f205f905f9160015b610383575b82811061036d575050507f5d59f11b58458fbcad377ef1f5dd5c53b158cacf770d418be4a655f3a1e625a2608060409473ffffffffffffffffffffffffffffffffffffffff865191600435835216602082015273ffffffffffffffffffffffffffffffffffffffff6044351686820152846060820152a182519182526020820152f35b806001915f52826020525f6040812055016102ea565b825f528160205260405f206040519061039b826110f1565b5473ffffffffffffffffffffffffffffffffffffffff811680835260a082901c60ff16151560208401526103d6575b506020015115156102e5565b604080517fb460af94000000000000000000000000000000000000000000000000000000008152600480359082015273ffffffffffffffffffffffffffffffffffffffff8a81166024830152604480358216908301529798979396939092839160649183915f91165af19586156101ba575f915f976104b6575b5061045f61046592889261117b565b9761117b565b94610476575b6001019260206103ca565b9060018082602093850361048f575b019291505061046b565b6104b173ffffffffffffffffffffffffffffffffffffffff8851168288611363565b610485565b965090506040863d6040116104ed575b816104d36040938361113a565b810103126101af578551602090960151959061045f610450565b3d91506104c6565b60046040517f60b39bc5000000000000000000000000000000000000000000000000000000008152fd5b610541915060203d602011610547575b610539818361113a565b8101906112d1565b846102ab565b503d61052f565b346101af5760206003193601126101af576040517ff7a95a9e00000000000000000000000000000000000000000000000000000000815260043560048201525f8160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9081156101ba575f916108a7575b5080516105e581611282565b906105ef81611282565b905f5b81811061067a575050604051928392606084016060855282518091526020608086019301905f5b81811061064b5750505081610639918561064795940360208701526110be565b9083820360408501526110be565b0390f35b825173ffffffffffffffffffffffffffffffffffffffff16855287965060209485019490920191600101610619565b73ffffffffffffffffffffffffffffffffffffffff61069c8287959697611207565b51166004355f525f60205260405f20815f5260205260405f205f9060015b6106cd57505050600101939291936105f2565b815f528060205260405f20604051906106e5826110f1565b5460ff73ffffffffffffffffffffffffffffffffffffffff82169182845260a01c161515602083015280610720575b506020015115156106ba565b6040517f2cedc68a000000000000000000000000000000000000000000000000000000008152600480359082015273ffffffffffffffffffffffffffffffffffffffff8616602482015291939190602090829060449082905afa80156101ba575f90610873575b61079c9150610796878b611207565b5161117b565b6107a6868a611207565b5282516040517fb57f84ac000000000000000000000000000000000000000000000000000000008152600480359082015273ffffffffffffffffffffffffffffffffffffffff8681166024830152909160209183916044918391165afa9081156101ba578a87915f9361083b575b506001926107966020959361082893611207565b610832888d611207565b52019290610714565b925050506020813d60201161086b575b816108586020938361113a565b810103126101af5751858a610828610814565b3d915061084b565b506020813d60201161089f575b8161088d6020938361113a565b810103126101af5761079c9051610787565b3d9150610880565b90503d805f833e6108b8818361113a565b8101906080818303126101af5780519167ffffffffffffffff928381116101af5782019281601f850112156101af5783516108f2816111b5565b94610900604051968761113a565b81865260208087019260051b820101908482116101af57602001915b8183106109ac5750505060208301519081116101af57820181601f820112156101af5780519060208061094e846111b5565b61095b604051918261113a565b848152019260051b8201019283116101af57602001905b82821061099c5750505060608161098e60406109959401611248565b5001611261565b50816105d9565b8151815260209182019101610972565b825173ffffffffffffffffffffffffffffffffffffffff811681036101af5781526020928301920161091c565b6020806003193601126101af576004359067ffffffffffffffff8083116101af57366023840112156101af578260040135908082116101af57600560243684831b87018201116101af57610a2e8492946111b5565b93610a3c604051958661113a565b828552610a48836111b5565b967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0809801875f5b828110610c03575050505f917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbd82360301925b858110610b3f5789898960405191808301818452825180915260408401918060408360051b8701019401925f965b838810610ade5786860387f35b9091929394838080837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08b6001960301875285601f838c518051918291828752018686015e5f85828601015201160101970193019701969093929193610ad1565b8481831b84010135848112156101af5783019085820135918883116101af576044908181019380360385136101af57610b77816111cd565b91610b85604051938461113a565b8183528d8301938236920101116101af575f838e93828585829660019b610bde9a37830101525190305af43d15610bfa573d610bc0816111cd565b90610bce604051928361113a565b81525f81933d92013e5b30611464565b610be8828b611207565b52610bf3818a611207565b5001610aa3565b60609150610bd8565b6060898201830152899101610a70565b346101af5760406003193601126101af57600435610c2f61109b565b9073ffffffffffffffffffffffffffffffffffffffff6040517f95c0465a000000000000000000000000000000000000000000000000000000008152826004820152604081602481857f0000000000000000000000000000000000000000000000000000000000000000165afa9081156101ba575f905f92610ea6575b506bffffffffffffffffffffffff1615610e2757811690604051907f01ffc9a70000000000000000000000000000000000000000000000000000000082527ff2670b730000000000000000000000000000000000000000000000000000000060048301526020918281602481875afa9081156101ba575f91610e89575b5015610e2757604051907f034db2350000000000000000000000000000000000000000000000000000000082528282602481848a16978860048301525afa9182156101ba575f92610e51575b5033911603610e2757825f525f815260405f20915f525260405f2091610d9b33846112e9565b90610dfd577fd4ad436bd7ca727f4928e74a79db1cf6060d563e6a7b27b9703c1348756a781c93610dcd913391611363565b6040805192835273ffffffffffffffffffffffffffffffffffffffff9190911660208301523390820152606090a1005b60046040517fd176fd8c000000000000000000000000000000000000000000000000000000008152fd5b60046040517fa9cd67ae000000000000000000000000000000000000000000000000000000008152fd5b9091508281813d8311610e82575b610e69818361113a565b810103126101af575181811681036101af579086610d75565b503d610e5f565b610ea09150833d851161054757610539818361113a565b86610d29565b9150506040813d604011610ef3575b81610ec26040938361113a565b810103126101af576bffffffffffffffffffffffff610eec6020610ee584611248565b9301611261565b9190610cac565b3d9150610eb5565b346101af575f6003193601126101af57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101af5760406003193601126101af57600435610f6761109b565b905f90805f526020925f845260405f209173ffffffffffffffffffffffffffffffffffffffff928383165f52855260405f20915f9160019360015b610fb0578787604051908152f35b835f5280885260405f2060405190610fc7826110f1565b549060ff8a89841692838152019260a01c161515825280610fec575b50511515610fa2565b6040517f2cedc68a0000000000000000000000000000000000000000000000000000000081526004810185905273ffffffffffffffffffffffffffffffffffffffff8616602482015295989195908a90829060449082905afa80156101ba5787915f91611068575b5061105f919261117b565b97019389610fe3565b8092508b8092503d8311611094575b611081818361113a565b810103126101af5751869061105f611054565b503d611077565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101af57565b9081518082526020808093019301915f5b8281106110dd575050505090565b8351855293810193928101926001016110cf565b6040810190811067ffffffffffffffff82111761110d57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761110d57604052565b9190820180921161118857565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b67ffffffffffffffff811161110d5760051b60200190565b67ffffffffffffffff811161110d57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b805182101561121b5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b51906bffffffffffffffffffffffff821682036101af57565b519073ffffffffffffffffffffffffffffffffffffffff821682036101af57565b9061128c826111b5565b611299604051918261113a565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06112c782946111b5565b0190602036910137565b908160209103126101af575180151581036101af5790565b5f929160015b6112fa5750505f9190565b835f5260208281526040805f20905190611313826110f1565b549073ffffffffffffffffffffffffffffffffffffffff9060ff8284169384835260a01c16151593849101528316811461135657156112ef5793600101936112ef565b5050505090506001905f90565b91816113ee575b60405192611377846110f1565b73ffffffffffffffffffffffffffffffffffffffff809216845260208401925f84525f5260205260405f209251167fffffffffffffffffffffff00000000000000000000000000000000000000000074ff000000000000000000000000000000000000000084549351151560a01b16921617179055565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201828111611188575f528260205260405f20740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff82541617905561136a565b906114a3575080511561147957805190602001fd5b60046040517fd6bda275000000000000000000000000000000000000000000000000000000008152fd5b815115806114fb575b6114b4575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b156114ac5600000000000000000000000058e5d76fbbd7e1b51f0fc0f66b7734e108be0461

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c80632cedc68a14610f4b578063411557d114610efb5780635452666714610c13578063ac9650d8146109d9578063b263b7cc1461054e578063b460af94146101c55763b57f84ac14610066575f80fd5b346101af5760406003193601126101af5760043561008261109b565b905f90805f526020925f845260405f209173ffffffffffffffffffffffffffffffffffffffff928383165f52855260405f20915f9160019360015b6100cb578787604051908152f35b835f5280885260405f20604051906100e2826110f1565b549060ff8a89841692838152019260a01c161515825280610107575b505115156100bd565b6040517fb57f84ac0000000000000000000000000000000000000000000000000000000081526004810185905273ffffffffffffffffffffffffffffffffffffffff8616602482015295989195908a90829060449082905afa80156101ba5787915f91610183575b5061017a919261117b565b9701935f6100fe565b8092508b8092503d83116101b3575b61019c818361113a565b810103126101af5751869061017a61016f565b5f80fd5b503d610192565b6040513d5f823e3d90fd5b346101af5760606003193601126101af576101de61109b565b73ffffffffffffffffffffffffffffffffffffffff60443516604435036101af575f806040517f823abfd9000000000000000000000000000000000000000000000000000000008152600435600482015233602482015260ff7f000000000000000000000000000000000000000000000000000000000000000116604482015260208160648173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000058e5d76fbbd7e1b51f0fc0f66b7734e108be0461165afa9081156101ba575f9161051f575b50156104f5576004355f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff84165f5260205260405f205f905f9160015b610383575b82811061036d575050507f5d59f11b58458fbcad377ef1f5dd5c53b158cacf770d418be4a655f3a1e625a2608060409473ffffffffffffffffffffffffffffffffffffffff865191600435835216602082015273ffffffffffffffffffffffffffffffffffffffff6044351686820152846060820152a182519182526020820152f35b806001915f52826020525f6040812055016102ea565b825f528160205260405f206040519061039b826110f1565b5473ffffffffffffffffffffffffffffffffffffffff811680835260a082901c60ff16151560208401526103d6575b506020015115156102e5565b604080517fb460af94000000000000000000000000000000000000000000000000000000008152600480359082015273ffffffffffffffffffffffffffffffffffffffff8a81166024830152604480358216908301529798979396939092839160649183915f91165af19586156101ba575f915f976104b6575b5061045f61046592889261117b565b9761117b565b94610476575b6001019260206103ca565b9060018082602093850361048f575b019291505061046b565b6104b173ffffffffffffffffffffffffffffffffffffffff8851168288611363565b610485565b965090506040863d6040116104ed575b816104d36040938361113a565b810103126101af578551602090960151959061045f610450565b3d91506104c6565b60046040517f60b39bc5000000000000000000000000000000000000000000000000000000008152fd5b610541915060203d602011610547575b610539818361113a565b8101906112d1565b846102ab565b503d61052f565b346101af5760206003193601126101af576040517ff7a95a9e00000000000000000000000000000000000000000000000000000000815260043560048201525f8160248173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000058e5d76fbbd7e1b51f0fc0f66b7734e108be0461165afa9081156101ba575f916108a7575b5080516105e581611282565b906105ef81611282565b905f5b81811061067a575050604051928392606084016060855282518091526020608086019301905f5b81811061064b5750505081610639918561064795940360208701526110be565b9083820360408501526110be565b0390f35b825173ffffffffffffffffffffffffffffffffffffffff16855287965060209485019490920191600101610619565b73ffffffffffffffffffffffffffffffffffffffff61069c8287959697611207565b51166004355f525f60205260405f20815f5260205260405f205f9060015b6106cd57505050600101939291936105f2565b815f528060205260405f20604051906106e5826110f1565b5460ff73ffffffffffffffffffffffffffffffffffffffff82169182845260a01c161515602083015280610720575b506020015115156106ba565b6040517f2cedc68a000000000000000000000000000000000000000000000000000000008152600480359082015273ffffffffffffffffffffffffffffffffffffffff8616602482015291939190602090829060449082905afa80156101ba575f90610873575b61079c9150610796878b611207565b5161117b565b6107a6868a611207565b5282516040517fb57f84ac000000000000000000000000000000000000000000000000000000008152600480359082015273ffffffffffffffffffffffffffffffffffffffff8681166024830152909160209183916044918391165afa9081156101ba578a87915f9361083b575b506001926107966020959361082893611207565b610832888d611207565b52019290610714565b925050506020813d60201161086b575b816108586020938361113a565b810103126101af5751858a610828610814565b3d915061084b565b506020813d60201161089f575b8161088d6020938361113a565b810103126101af5761079c9051610787565b3d9150610880565b90503d805f833e6108b8818361113a565b8101906080818303126101af5780519167ffffffffffffffff928381116101af5782019281601f850112156101af5783516108f2816111b5565b94610900604051968761113a565b81865260208087019260051b820101908482116101af57602001915b8183106109ac5750505060208301519081116101af57820181601f820112156101af5780519060208061094e846111b5565b61095b604051918261113a565b848152019260051b8201019283116101af57602001905b82821061099c5750505060608161098e60406109959401611248565b5001611261565b50816105d9565b8151815260209182019101610972565b825173ffffffffffffffffffffffffffffffffffffffff811681036101af5781526020928301920161091c565b6020806003193601126101af576004359067ffffffffffffffff8083116101af57366023840112156101af578260040135908082116101af57600560243684831b87018201116101af57610a2e8492946111b5565b93610a3c604051958661113a565b828552610a48836111b5565b967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0809801875f5b828110610c03575050505f917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbd82360301925b858110610b3f5789898960405191808301818452825180915260408401918060408360051b8701019401925f965b838810610ade5786860387f35b9091929394838080837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08b6001960301875285601f838c518051918291828752018686015e5f85828601015201160101970193019701969093929193610ad1565b8481831b84010135848112156101af5783019085820135918883116101af576044908181019380360385136101af57610b77816111cd565b91610b85604051938461113a565b8183528d8301938236920101116101af575f838e93828585829660019b610bde9a37830101525190305af43d15610bfa573d610bc0816111cd565b90610bce604051928361113a565b81525f81933d92013e5b30611464565b610be8828b611207565b52610bf3818a611207565b5001610aa3565b60609150610bd8565b6060898201830152899101610a70565b346101af5760406003193601126101af57600435610c2f61109b565b9073ffffffffffffffffffffffffffffffffffffffff6040517f95c0465a000000000000000000000000000000000000000000000000000000008152826004820152604081602481857f00000000000000000000000058e5d76fbbd7e1b51f0fc0f66b7734e108be0461165afa9081156101ba575f905f92610ea6575b506bffffffffffffffffffffffff1615610e2757811690604051907f01ffc9a70000000000000000000000000000000000000000000000000000000082527ff2670b730000000000000000000000000000000000000000000000000000000060048301526020918281602481875afa9081156101ba575f91610e89575b5015610e2757604051907f034db2350000000000000000000000000000000000000000000000000000000082528282602481848a16978860048301525afa9182156101ba575f92610e51575b5033911603610e2757825f525f815260405f20915f525260405f2091610d9b33846112e9565b90610dfd577fd4ad436bd7ca727f4928e74a79db1cf6060d563e6a7b27b9703c1348756a781c93610dcd913391611363565b6040805192835273ffffffffffffffffffffffffffffffffffffffff9190911660208301523390820152606090a1005b60046040517fd176fd8c000000000000000000000000000000000000000000000000000000008152fd5b60046040517fa9cd67ae000000000000000000000000000000000000000000000000000000008152fd5b9091508281813d8311610e82575b610e69818361113a565b810103126101af575181811681036101af579086610d75565b503d610e5f565b610ea09150833d851161054757610539818361113a565b86610d29565b9150506040813d604011610ef3575b81610ec26040938361113a565b810103126101af576bffffffffffffffffffffffff610eec6020610ee584611248565b9301611261565b9190610cac565b3d9150610eb5565b346101af575f6003193601126101af57602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000058e5d76fbbd7e1b51f0fc0f66b7734e108be0461168152f35b346101af5760406003193601126101af57600435610f6761109b565b905f90805f526020925f845260405f209173ffffffffffffffffffffffffffffffffffffffff928383165f52855260405f20915f9160019360015b610fb0578787604051908152f35b835f5280885260405f2060405190610fc7826110f1565b549060ff8a89841692838152019260a01c161515825280610fec575b50511515610fa2565b6040517f2cedc68a0000000000000000000000000000000000000000000000000000000081526004810185905273ffffffffffffffffffffffffffffffffffffffff8616602482015295989195908a90829060449082905afa80156101ba5787915f91611068575b5061105f919261117b565b97019389610fe3565b8092508b8092503d8311611094575b611081818361113a565b810103126101af5751869061105f611054565b503d611077565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101af57565b9081518082526020808093019301915f5b8281106110dd575050505090565b8351855293810193928101926001016110cf565b6040810190811067ffffffffffffffff82111761110d57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761110d57604052565b9190820180921161118857565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b67ffffffffffffffff811161110d5760051b60200190565b67ffffffffffffffff811161110d57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b805182101561121b5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b51906bffffffffffffffffffffffff821682036101af57565b519073ffffffffffffffffffffffffffffffffffffffff821682036101af57565b9061128c826111b5565b611299604051918261113a565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06112c782946111b5565b0190602036910137565b908160209103126101af575180151581036101af5790565b5f929160015b6112fa5750505f9190565b835f5260208281526040805f20905190611313826110f1565b549073ffffffffffffffffffffffffffffffffffffffff9060ff8284169384835260a01c16151593849101528316811461135657156112ef5793600101936112ef565b5050505090506001905f90565b91816113ee575b60405192611377846110f1565b73ffffffffffffffffffffffffffffffffffffffff809216845260208401925f84525f5260205260405f209251167fffffffffffffffffffffff00000000000000000000000000000000000000000074ff000000000000000000000000000000000000000084549351151560a01b16921617179055565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201828111611188575f528260205260405f20740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff82541617905561136a565b906114a3575080511561147957805190602001fd5b60046040517fd6bda275000000000000000000000000000000000000000000000000000000008152fd5b815115806114fb575b6114b4575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b156114ac56

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000058e5d76fbbd7e1b51f0fc0f66b7734e108be0461

-----Decoded View---------------
Arg [0] : vault (address): 0x58E5d76Fbbd7E1b51F0fC0F66B7734E108be0461

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000058e5d76fbbd7e1b51f0fc0f66b7734e108be0461


Block Transaction Gas Used Reward
view all blocks produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.