ETH Price: $3,416.10 (+3.64%)

Contract

0xb835f5351D32ba0eaD26b78a391306d5C55b20Eb
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60a06040167013462023-02-24 23:08:59637 days ago1677280139IN
 Create: CapitalLedger
0 ETH0.0896641837.54229789

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CapitalLedger

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 100 runs

Other Settings:
default evmVersion, MIT license
File 1 of 46 : CapitalLedger.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.16;

import "@openzeppelin/contracts-upgradeable/interfaces/IERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/interfaces/IERC721ReceiverUpgradeable.sol";

import {Context} from "../../../cake/Context.sol";
import {Base} from "../../../cake/Base.sol";
import "../../../cake/Routing.sol" as Routing;

import {Arrays} from "../../../library/Arrays.sol";
import {CapitalAssets} from "./assets/CapitalAssets.sol";
import {UserEpochTotals, UserEpochTotal} from "./UserEpochTotals.sol";

import {ICapitalLedger, CapitalAssetType} from "../../../interfaces/ICapitalLedger.sol";

using Routing.Context for Context;
using UserEpochTotals for UserEpochTotal;
using Arrays for uint256[];

/**
 * @title CapitalLedger
 * @notice Track Capital held by owners and ensure the Capital has been accounted for.
 * @author Goldfinch
 */
contract CapitalLedger is ICapitalLedger, Base, IERC721ReceiverUpgradeable {
  /// Thrown when attempting to deposit nothing
  error ZeroDeposit();
  /// Thrown when withdrawing an invalid amount for a position
  error InvalidWithdrawAmount(uint256 requested, uint256 max);
  /// Thrown when depositing from address(0)
  error InvalidOwnerIndex();
  /// Thrown when querying token supply with an index greater than the supply
  error IndexGreaterThanTokenSupply();

  struct Position {
    // Owner of the position
    address owner;
    // Index of the position in the ownership array
    uint256 ownedIndex;
    // Address of the underlying asset represented by the position
    address assetAddress;
    // USDC equivalent value of the position. This is first written
    // on position deposit but may be updated on harvesting or kicking
    uint256 usdcEquivalent;
    // When the position was deposited
    uint256 depositTimestamp;
  }

  struct ERC721Data {
    // Id of the ERC721 assetAddress' token
    uint256 assetTokenId;
  }

  /// Data for positions in the vault. Always has a corresponding
  /// entry at the same index in ERC20Data or ERC721 data, but never
  /// both.
  mapping(uint256 => Position) public positions;

  // Which positions an address owns
  mapping(address => uint256[]) private owners;

  /// Total held by each user, while being aware of the deposit epoch
  mapping(address => UserEpochTotal) private totals;

  // The current position index
  uint256 private positionCounter;

  /// ERC721 data corresponding to positions, data has the same index
  /// as its corresponding position.
  mapping(uint256 => ERC721Data) private erc721Datas;

  /// @notice Construct the contract
  constructor(Context _context) Base(_context) {}

  /// @inheritdoc ICapitalLedger
  function depositERC721(
    address owner,
    address assetAddress,
    uint256 assetTokenId
  ) external onlyOperator(Routing.Keys.MembershipOrchestrator) returns (uint256) {
    if (CapitalAssets.getSupportedType(context, assetAddress) != CapitalAssetType.ERC721) {
      revert CapitalAssets.InvalidAsset(assetAddress);
    }
    if (!CapitalAssets.isValid(context, assetAddress, assetTokenId)) {
      revert CapitalAssets.InvalidAssetWithId(assetAddress, assetTokenId);
    }

    IERC721Upgradeable asset = IERC721Upgradeable(assetAddress);
    uint256 usdcEquivalent = CapitalAssets.getUsdcEquivalent(context, asset, assetTokenId);
    uint256 positionId = _mintPosition(owner, assetAddress, usdcEquivalent);

    erc721Datas[positionId] = ERC721Data({assetTokenId: assetTokenId});

    totals[owner].recordIncrease(usdcEquivalent);

    asset.safeTransferFrom(address(context.membershipOrchestrator()), address(this), assetTokenId);

    emit CapitalERC721Deposit({
      owner: owner,
      assetAddress: assetAddress,
      positionId: positionId,
      assetTokenId: assetTokenId,
      usdcEquivalent: usdcEquivalent
    });

    return positionId;
  }

  /// @inheritdoc ICapitalLedger
  function erc721IdOf(uint256 positionId) public view returns (uint256) {
    return erc721Datas[positionId].assetTokenId;
  }

  /// @inheritdoc ICapitalLedger
  function withdraw(uint256 positionId) external onlyOperator(Routing.Keys.MembershipOrchestrator) {
    Position memory position = positions[positionId];
    delete positions[positionId];

    CapitalAssetType assetType = CapitalAssets.getSupportedType(context, position.assetAddress);

    totals[position.owner].recordDecrease(position.usdcEquivalent, position.depositTimestamp);

    uint256[] storage ownersList = owners[position.owner];
    (, bool replaced) = ownersList.reorderingRemove(position.ownedIndex);
    if (replaced) {
      positions[ownersList[position.ownedIndex]].ownedIndex = position.ownedIndex;
    }

    if (assetType == CapitalAssetType.ERC721) {
      uint256 assetTokenId = erc721Datas[positionId].assetTokenId;
      delete erc721Datas[positionId];

      IERC721Upgradeable(position.assetAddress).safeTransferFrom(
        address(this),
        position.owner,
        assetTokenId
      );

      emit CapitalERC721Withdrawal(
        position.owner,
        positionId,
        position.assetAddress,
        position.depositTimestamp
      );
    } else {
      revert InvalidAssetType(assetType);
    }
  }

  /// @inheritdoc ICapitalLedger
  function harvest(uint256 positionId) external onlyOperator(Routing.Keys.MembershipOrchestrator) {
    Position memory position = positions[positionId];
    CapitalAssetType assetType = CapitalAssets.getSupportedType(context, position.assetAddress);

    if (assetType != CapitalAssetType.ERC721) revert InvalidAssetType(assetType);

    CapitalAssets.harvest(
      context,
      position.owner,
      IERC721Upgradeable(position.assetAddress),
      erc721Datas[positionId].assetTokenId
    );

    emit CapitalERC721Harvest({positionId: positionId, assetAddress: position.assetAddress});

    _kick(positionId);
  }

  /// @inheritdoc ICapitalLedger
  function assetAddressOf(uint256 positionId) public view returns (address) {
    return positions[positionId].assetAddress;
  }

  /// @inheritdoc ICapitalLedger
  function ownerOf(uint256 positionId) public view returns (address) {
    return positions[positionId].owner;
  }

  /// @inheritdoc ICapitalLedger
  function totalsOf(
    address addr
  ) external view returns (uint256 eligibleAmount, uint256 totalAmount) {
    return totals[addr].getTotals();
  }

  /// @inheritdoc ICapitalLedger
  function totalSupply() public view returns (uint256) {
    return positionCounter;
  }

  /// @inheritdoc ICapitalLedger
  function balanceOf(address addr) external view returns (uint256) {
    return owners[addr].length;
  }

  /// @inheritdoc ICapitalLedger
  function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256) {
    if (index >= owners[owner].length) revert InvalidOwnerIndex();

    return owners[owner][index];
  }

  /// @inheritdoc ICapitalLedger
  function tokenByIndex(uint256 index) external view returns (uint256) {
    if (index >= totalSupply()) revert IndexGreaterThanTokenSupply();

    return index + 1;
  }

  /// @inheritdoc IERC721ReceiverUpgradeable
  function onERC721Received(
    address,
    address,
    uint256,
    bytes calldata
  ) external pure returns (bytes4) {
    return IERC721ReceiverUpgradeable.onERC721Received.selector;
  }

  //////////////////////////////////////////////////////////////////
  // Private

  function _mintPosition(
    address owner,
    address assetAddress,
    uint256 usdcEquivalent
  ) private returns (uint256 positionId) {
    positionCounter++;

    positionId = positionCounter;
    positions[positionId] = Position({
      owner: owner,
      ownedIndex: owners[owner].length,
      assetAddress: assetAddress,
      usdcEquivalent: usdcEquivalent,
      depositTimestamp: block.timestamp
    });

    owners[owner].push(positionId);
  }

  /**
   * @notice Update the USDC equivalent value of the position, based on the current,
   *  point-in-time valuation of the underlying asset.
   * @param positionId id of the position
   */
  function _kick(uint256 positionId) internal {
    Position memory position = positions[positionId];
    CapitalAssetType assetType = CapitalAssets.getSupportedType(context, position.assetAddress);

    if (assetType != CapitalAssetType.ERC721) revert InvalidAssetType(assetType);

    // Remove the original USDC equivalent value from the owner's total
    totals[position.owner].recordDecrease(position.usdcEquivalent, position.depositTimestamp);

    uint256 usdcEquivalent = CapitalAssets.getUsdcEquivalent(
      context,
      IERC721Upgradeable(position.assetAddress),
      erc721Datas[positionId].assetTokenId
    );

    //  Set the new value & add the new USDC equivalent value back to the owner's total
    positions[positionId].usdcEquivalent = usdcEquivalent;
    totals[position.owner].recordInstantIncrease(usdcEquivalent, position.depositTimestamp);

    emit CapitalPositionAdjustment({
      positionId: positionId,
      assetAddress: position.assetAddress,
      usdcEquivalent: usdcEquivalent
    });
  }
}

File 2 of 46 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20Upgradeable.sol";

File 3 of 46 : IERC721ReceiverUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../token/ERC721/IERC721ReceiverUpgradeable.sol";

File 4 of 46 : IERC721Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../token/ERC721/IERC721Upgradeable.sol";

File 5 of 46 : Initializable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }
}

File 6 of 46 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 7 of 46 : SafeERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";
import "../../../utils/AddressUpgradeable.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20Upgradeable {
    using AddressUpgradeable for address;

    function safeTransfer(
        IERC20Upgradeable token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20Upgradeable token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 8 of 46 : IERC721ReceiverUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721ReceiverUpgradeable {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 9 of 46 : IERC721Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165Upgradeable.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721Upgradeable is IERC165Upgradeable {
    /**
     * @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`, checking first that contract recipients
     * are aware of the ERC721 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 be 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: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * 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 Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @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 caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @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);

    /**
     * @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;
}

File 10 of 46 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @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, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * 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.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @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`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // 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 {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 11 of 46 : IERC165Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

File 12 of 46 : AccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../interfaces/IAccessControl.sol";

/// @title Cake access control
/// @author landakram
/// @notice This contact centralizes contract-to-contract access control using a simple
/// access-control list. There are two types of actors: operators and admins. Operators
/// are callers involved in a regular end-user tx. This would likely be another Goldfinch
/// contract for which the current contract is a dependency. Admins are callers allowed
/// for specific admin actions (like changing parameters, topping up funds, etc.).
contract AccessControl is Initializable, IAccessControl {
  /// @dev Mapping from contract address to contract admin;
  mapping(address => address) public admins;

  function initialize(address admin) public initializer {
    admins[address(this)] = admin;
    emit AdminSet(address(this), admin);
  }

  /// @inheritdoc IAccessControl
  function setAdmin(address resource, address admin) external {
    requireSuperAdmin(msg.sender);
    admins[resource] = admin;
    emit AdminSet(resource, admin);
  }

  /// @inheritdoc IAccessControl
  function requireAdmin(address resource, address accessor) public view {
    if (accessor == address(0)) revert ZeroAddress();
    bool isAdmin = admins[resource] == accessor;
    if (!isAdmin) revert RequiresAdmin(resource, accessor);
  }

  /// @inheritdoc IAccessControl
  function requireSuperAdmin(address accessor) public view {
    // The super admin is the admin of this AccessControl contract
    requireAdmin({resource: address(this), accessor: accessor});
  }
}

File 13 of 46 : Base.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import {Context} from "./Context.sol";
import "./Routing.sol" as Routing;

using Routing.Context for Context;

/// @title Base contract for application-layer
/// @author landakram
/// @notice This base contract is what all application-layer contracts should inherit from.
///  It provides `Context`, as well as some convenience functions for working with it and
///  using access control. All public methods on the inheriting contract should likely
///  use one of the modifiers to assert valid callers.
abstract contract Base {
  error RequiresOperator(address resource, address accessor);
  error ZeroAddress();

  /// @dev this is safe for proxies as immutable causes the context to be written to
  ///  bytecode on deployment. The proxy then treats this as a constant.
  Context immutable context;

  constructor(Context _context) {
    context = _context;
  }

  modifier onlyOperator(bytes4 operatorId) {
    requireOperator(operatorId, msg.sender);
    _;
  }

  modifier onlyOperators(bytes4[2] memory operatorIds) {
    requireAnyOperator(operatorIds, msg.sender);
    _;
  }

  modifier onlyAdmin() {
    context.accessControl().requireAdmin(address(this), msg.sender);
    _;
  }

  function requireAnyOperator(bytes4[2] memory operatorIds, address accessor) private view {
    if (accessor == address(0)) revert ZeroAddress();

    bool validOperator = isOperator(operatorIds[0], accessor) ||
      isOperator(operatorIds[1], accessor);

    if (!validOperator) revert RequiresOperator(address(this), accessor);
  }

  function requireOperator(bytes4 operatorId, address accessor) private view {
    if (accessor == address(0)) revert ZeroAddress();
    if (!isOperator(operatorId, accessor)) revert RequiresOperator(address(this), accessor);
  }

  function isOperator(bytes4 operatorId, address accessor) private view returns (bool) {
    return context.router().contracts(operatorId) == accessor;
  }
}

File 14 of 46 : Context.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import {AccessControl} from "./AccessControl.sol";
import {Router} from "./Router.sol";
import "./Routing.sol" as Routing;

using Routing.Context for Context;

/// @title Entry-point for all application-layer contracts.
/// @author landakram
/// @notice This contract provides an interface for retrieving other contract addresses and doing access
///  control.
contract Context {
  /// @notice Used for retrieving other contract addresses.
  /// @dev This variable is immutable. This is done to save gas, as it is expected to be referenced
  /// in every end-user call with a call-chain length > 0. Note that it is written into the contract
  /// bytecode at contract creation time, so if the contract is deployed as the implementation for proxies,
  /// every proxy will share the same Router address.
  Router public immutable router;

  constructor(Router _router) {
    router = _router;
  }
}

File 15 of 46 : Router.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {AccessControl} from "./AccessControl.sol";
import {IRouter} from "../interfaces/IRouter.sol";

import "./Routing.sol" as Routing;

/// @title Router
/// @author landakram
/// @notice This contract provides service discovery for contracts using the cake framework.
///   It can be used in conjunction with the convenience methods defined in the `Routing.Context`
///   and `Routing.Keys` libraries.
contract Router is Initializable, IRouter {
  /// @notice Mapping of keys to contract addresses. Keys are the first 4 bytes of the keccak of
  ///   the contract's name. See Routing.sol for all options.
  mapping(bytes4 => address) public contracts;

  function initialize(AccessControl accessControl) public initializer {
    contracts[Routing.Keys.AccessControl] = address(accessControl);
  }

  /// @notice Associate a routing key to a contract address
  /// @dev This function is only callable by the Router admin
  /// @param key A routing key (defined in the `Routing.Keys` libary)
  /// @param addr A contract address
  function setContract(bytes4 key, address addr) public {
    AccessControl accessControl = AccessControl(contracts[Routing.Keys.AccessControl]);
    accessControl.requireAdmin(address(this), msg.sender);
    contracts[key] = addr;
    emit SetContract(key, addr);
  }
}

File 16 of 46 : Routing.sol
// SPDX-License-Identifier: MIT
// solhint-disable const-name-snakecase

pragma solidity ^0.8.16;

import "@openzeppelin/contracts-upgradeable/interfaces/IERC20Upgradeable.sol";

import {IMembershipVault} from "../interfaces/IMembershipVault.sol";
import {IGFILedger} from "../interfaces/IGFILedger.sol";
import {ICapitalLedger} from "../interfaces/ICapitalLedger.sol";
import {IMembershipDirector} from "../interfaces/IMembershipDirector.sol";
import {IMembershipOrchestrator} from "../interfaces/IMembershipOrchestrator.sol";
import {IMembershipLedger} from "../interfaces/IMembershipLedger.sol";
import {IMembershipCollector} from "../interfaces/IMembershipCollector.sol";
import {IBackerRewards} from "../interfaces/IBackerRewards.sol";

import {ISeniorPool} from "../interfaces/ISeniorPool.sol";
import {IPoolTokens} from "../interfaces/IPoolTokens.sol";
import {IStakingRewards} from "../interfaces/IStakingRewards.sol";
import {IGo} from "../interfaces/IGo.sol";

import {IERC20Splitter} from "../interfaces/IERC20Splitter.sol";
import {Context as ContextContract} from "./Context.sol";
import {IAccessControl} from "../interfaces/IAccessControl.sol";

/// @title Routing.Keys
/// @notice This library is used to define routing keys used by `Router`.
/// @dev We use uints instead of enums for several reasons. First, keys can be re-ordered
///   or removed. This is useful when routing keys are deprecated; they can be moved to a
///   different section of the file. Second, other libraries or contracts can define their
///   own routing keys independent of this global mapping. This is useful for test contracts.
library Keys {
  // Membership
  bytes4 internal constant MembershipOrchestrator = bytes4(keccak256("MembershipOrchestrator"));
  bytes4 internal constant MembershipDirector = bytes4(keccak256("MembershipDirector"));
  bytes4 internal constant GFILedger = bytes4(keccak256("GFILedger"));
  bytes4 internal constant CapitalLedger = bytes4(keccak256("CapitalLedger"));
  bytes4 internal constant MembershipCollector = bytes4(keccak256("MembershipCollector"));
  bytes4 internal constant MembershipLedger = bytes4(keccak256("MembershipLedger"));
  bytes4 internal constant MembershipVault = bytes4(keccak256("MembershipVault"));

  // Tokens
  bytes4 internal constant GFI = bytes4(keccak256("GFI"));
  bytes4 internal constant FIDU = bytes4(keccak256("FIDU"));
  bytes4 internal constant USDC = bytes4(keccak256("USDC"));

  // Cake
  bytes4 internal constant AccessControl = bytes4(keccak256("AccessControl"));
  bytes4 internal constant Router = bytes4(keccak256("Router"));

  // Core
  bytes4 internal constant ReserveSplitter = bytes4(keccak256("ReserveSplitter"));
  bytes4 internal constant PoolTokens = bytes4(keccak256("PoolTokens"));
  bytes4 internal constant SeniorPool = bytes4(keccak256("SeniorPool"));
  bytes4 internal constant StakingRewards = bytes4(keccak256("StakingRewards"));
  bytes4 internal constant ProtocolAdmin = bytes4(keccak256("ProtocolAdmin"));
  bytes4 internal constant PauserAdmin = bytes4(keccak256("PauserAdmin"));
  bytes4 internal constant BackerRewards = bytes4(keccak256("BackerRewards"));
  bytes4 internal constant Go = bytes4(keccak256("Go"));
}

/// @title Routing.Context
/// @notice This library provides convenience functions for getting contracts from `Router`.
library Context {
  function accessControl(ContextContract context) internal view returns (IAccessControl) {
    return IAccessControl(context.router().contracts(Keys.AccessControl));
  }

  function membershipVault(ContextContract context) internal view returns (IMembershipVault) {
    return IMembershipVault(context.router().contracts(Keys.MembershipVault));
  }

  function capitalLedger(ContextContract context) internal view returns (ICapitalLedger) {
    return ICapitalLedger(context.router().contracts(Keys.CapitalLedger));
  }

  function gfiLedger(ContextContract context) internal view returns (IGFILedger) {
    return IGFILedger(context.router().contracts(Keys.GFILedger));
  }

  function gfi(ContextContract context) internal view returns (IERC20Upgradeable) {
    return IERC20Upgradeable(context.router().contracts(Keys.GFI));
  }

  function membershipDirector(ContextContract context) internal view returns (IMembershipDirector) {
    return IMembershipDirector(context.router().contracts(Keys.MembershipDirector));
  }

  function membershipOrchestrator(
    ContextContract context
  ) internal view returns (IMembershipOrchestrator) {
    return IMembershipOrchestrator(context.router().contracts(Keys.MembershipOrchestrator));
  }

  function stakingRewards(ContextContract context) internal view returns (IStakingRewards) {
    return IStakingRewards(context.router().contracts(Keys.StakingRewards));
  }

  function poolTokens(ContextContract context) internal view returns (IPoolTokens) {
    return IPoolTokens(context.router().contracts(Keys.PoolTokens));
  }

  function seniorPool(ContextContract context) internal view returns (ISeniorPool) {
    return ISeniorPool(context.router().contracts(Keys.SeniorPool));
  }

  function fidu(ContextContract context) internal view returns (IERC20Upgradeable) {
    return IERC20Upgradeable(context.router().contracts(Keys.FIDU));
  }

  function usdc(ContextContract context) internal view returns (IERC20Upgradeable) {
    return IERC20Upgradeable(context.router().contracts(Keys.USDC));
  }

  function reserveSplitter(ContextContract context) internal view returns (IERC20Splitter) {
    return IERC20Splitter(context.router().contracts(Keys.ReserveSplitter));
  }

  function membershipLedger(ContextContract context) internal view returns (IMembershipLedger) {
    return IMembershipLedger(context.router().contracts(Keys.MembershipLedger));
  }

  function membershipCollector(
    ContextContract context
  ) internal view returns (IMembershipCollector) {
    return IMembershipCollector(context.router().contracts(Keys.MembershipCollector));
  }

  function protocolAdmin(ContextContract context) internal view returns (address) {
    return context.router().contracts(Keys.ProtocolAdmin);
  }

  function pauserAdmin(ContextContract context) internal view returns (address) {
    return context.router().contracts(Keys.PauserAdmin);
  }

  function backerRewards(ContextContract context) internal view returns (IBackerRewards) {
    return IBackerRewards(context.router().contracts(Keys.BackerRewards));
  }

  function go(ContextContract context) internal view returns (IGo) {
    return IGo(context.router().contracts(Keys.Go));
  }
}

File 17 of 46 : IAccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

/// @title Cake access control
/// @author landakram
/// @notice This contact centralizes contract-to-contract access control using a simple
/// access-control list. There are two types of actors: operators and admins. Operators
/// are callers involved in a regular end-user tx. This would likely be another Goldfinch
/// contract for which the current contract is a dependency. Admins are callers allowed
/// for specific admin actions (like changing parameters, topping up funds, etc.).
interface IAccessControl {
  error RequiresAdmin(address resource, address accessor);
  error ZeroAddress();

  event AdminSet(address indexed resource, address indexed admin);

  /// @notice Set an admin for a given resource
  /// @param resource An address which with `admin` should be allowed to administer
  /// @param admin An address which should be allowed to administer `resource`
  /// @dev This method is only callable by the super-admin (the admin of this AccessControl
  ///   contract)
  function setAdmin(address resource, address admin) external;

  /// @notice Require a valid admin for a given resource
  /// @param resource An address that `accessor` is attempting to access
  /// @param accessor An address on which to assert access control checks
  /// @dev This method reverts when `accessor` is not a valid admin
  function requireAdmin(address resource, address accessor) external view;

  /// @notice Require a super-admin. A super-admin is an admin of this AccessControl contract.
  /// @param accessor An address on which to assert access control checks
  /// @dev This method reverts when `accessor` is not a valid super-admin
  function requireSuperAdmin(address accessor) external view;
}

File 18 of 46 : IBackerRewards.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;

pragma experimental ABIEncoderV2;

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

interface IBackerRewards {
  struct BackerRewardsTokenInfo {
    uint256 rewardsClaimed; // gfi claimed
    uint256 accRewardsPerPrincipalDollarAtMint; // Pool's accRewardsPerPrincipalDollar at PoolToken mint()
  }

  struct BackerRewardsInfo {
    uint256 accRewardsPerPrincipalDollar; // accumulator gfi per interest dollar
  }

  /// @notice Staking rewards parameters relevant to a TranchedPool
  struct StakingRewardsPoolInfo {
    // @notice the value `StakingRewards.accumulatedRewardsPerToken()` at the last checkpoint
    uint256 accumulatedRewardsPerTokenAtLastCheckpoint;
    // @notice last time the rewards info was updated
    //
    // we need this in order to know how much to pro rate rewards after the term is over.
    uint256 lastUpdateTime;
    // @notice staking rewards parameters for each slice of the tranched pool
    StakingRewardsSliceInfo[] slicesInfo;
  }

  /// @notice Staking rewards paramters relevant to a TranchedPool slice
  struct StakingRewardsSliceInfo {
    // @notice fidu share price when the slice is first drawn down
    //
    // we need to save this to calculate what an equivalent position in
    // the senior pool would be at the time the slice is downdown
    uint256 fiduSharePriceAtDrawdown;
    // @notice the amount of principal deployed at the last checkpoint
    //
    // we use this to calculate the amount of principal that should
    // acctually accrue rewards during between the last checkpoint and
    // and subsequent updates
    uint256 principalDeployedAtLastCheckpoint;
    // @notice the value of StakingRewards.accumulatedRewardsPerToken() at time of drawdown
    //
    // we need to keep track of this to use this as a base value to accumulate rewards
    // for tokens. If the token has never claimed staking rewards, we use this value
    // and the current staking rewards accumulator
    uint256 accumulatedRewardsPerTokenAtDrawdown;
    // @notice amount of rewards per token accumulated over the lifetime of the slice that a backer
    //          can claim
    uint256 accumulatedRewardsPerTokenAtLastCheckpoint;
    // @notice the amount of rewards per token accumulated over the lifetime of the slice
    //
    // this value is "unrealized" because backers will be unable to claim against this value.
    // we keep this value so that we can always accumulate rewards for the amount of capital
    // deployed at any point in time, but not allow backers to withdraw them until a payment
    // is made. For example: we want to accumulate rewards when a backer does a drawdown. but
    // a backer shouldn't be allowed to claim rewards until a payment is made.
    //
    // this value is scaled depending on the current proportion of capital currently deployed
    // in the slice. For example, if the staking rewards contract accrued 10 rewards per token
    // between the current checkpoint and a new update, and only 20% of the capital was deployed
    // during that period, we would accumulate 2 (10 * 20%) rewards.
    uint256 unrealizedAccumulatedRewardsPerTokenAtLastCheckpoint;
  }

  /// @notice Staking rewards parameters relevant to a PoolToken
  struct StakingRewardsTokenInfo {
    // @notice the amount of rewards accumulated the last time a token's rewards were withdrawn
    uint256 accumulatedRewardsPerTokenAtLastWithdraw;
  }

  /// @notice total amount of GFI rewards available, times 1e18
  function totalRewards() external view returns (uint256);

  /// @notice interest $ eligible for gfi rewards, times 1e18
  function maxInterestDollarsEligible() external view returns (uint256);

  /// @notice counter of total interest repayments, times 1e6
  function totalInterestReceived() external view returns (uint256);

  /// @notice totalRewards/totalGFISupply * 100, times 1e18
  function totalRewardPercentOfTotalGFI() external view returns (uint256);

  /// @notice Get backer rewards metadata for a pool token
  function getTokenInfo(uint256 poolTokenId) external view returns (BackerRewardsTokenInfo memory);

  /// @notice Get backer staking rewards metadata for a pool token
  function getStakingRewardsTokenInfo(
    uint256 poolTokenId
  ) external view returns (StakingRewardsTokenInfo memory);

  /// @notice Get backer staking rewards for a pool
  function getBackerStakingRewardsPoolInfo(
    ITranchedPool pool
  ) external view returns (StakingRewardsPoolInfo memory);

  /// @notice Calculates the accRewardsPerPrincipalDollar for a given pool,
  ///   when a interest payment is received by the protocol
  /// @param _interestPaymentAmount Atomic usdc amount of the interest payment
  function allocateRewards(uint256 _interestPaymentAmount) external;

  /// @notice callback for TranchedPools when they drawdown
  /// @param sliceIndex index of the tranched pool slice
  /// @dev initializes rewards info for the calling TranchedPool if it's the first
  ///  drawdown for the given slice
  function onTranchedPoolDrawdown(uint256 sliceIndex) external;

  /// @notice When a pool token is minted for multiple drawdowns,
  ///   set accRewardsPerPrincipalDollarAtMint to the current accRewardsPerPrincipalDollar price
  /// @param poolAddress Address of the pool associated with the pool token
  /// @param tokenId Pool token id
  function setPoolTokenAccRewardsPerPrincipalDollarAtMint(
    address poolAddress,
    uint256 tokenId
  ) external;

  /// @notice PoolToken request to withdraw all allocated rewards
  /// @param tokenId Pool token id
  /// @return amount of rewards withdrawn
  function withdraw(uint256 tokenId) external returns (uint256);

  /**
   * @notice Set BackerRewards and BackerStakingRewards metadata for tokens created by a pool token split.
   * @param originalBackerRewardsTokenInfo backer rewards info for the pool token that was split
   * @param originalStakingRewardsTokenInfo backer staking rewards info for the pool token that was split
   * @param newTokenId id of one of the tokens in the split
   * @param newRewardsClaimed rewardsClaimed value for the new token.
   */
  function setBackerAndStakingRewardsTokenInfoOnSplit(
    BackerRewardsTokenInfo memory originalBackerRewardsTokenInfo,
    StakingRewardsTokenInfo memory originalStakingRewardsTokenInfo,
    uint256 newTokenId,
    uint256 newRewardsClaimed
  ) external;

  /**
   * @notice Calculate the gross available gfi rewards for a PoolToken
   * @param tokenId Pool token id
   * @return The amount of GFI claimable
   */
  function poolTokenClaimableRewards(uint256 tokenId) external view returns (uint256);

  /// @notice Clear all BackerRewards and StakingRewards associated data for `tokenId`
  function clearTokenInfo(uint256 tokenId) external;
}

File 19 of 46 : ICapitalLedger.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;

enum CapitalAssetType {
  INVALID,
  ERC721
}

interface ICapitalLedger {
  /**
   * @notice Emitted when a new capital erc721 deposit has been made
   * @param owner address owning the deposit
   * @param assetAddress address of the deposited ERC721
   * @param positionId id for the deposit
   * @param assetTokenId id of the token from the ERC721 `assetAddress`
   * @param usdcEquivalent usdc equivalent value at the time of deposit
   */
  event CapitalERC721Deposit(
    address indexed owner,
    address indexed assetAddress,
    uint256 positionId,
    uint256 assetTokenId,
    uint256 usdcEquivalent
  );

  /**
   * @notice Emitted when a new ERC721 capital withdrawal has been made
   * @param owner address owning the deposit
   * @param positionId id for the capital position
   * @param assetAddress address of the underlying ERC721
   * @param depositTimestamp block.timestamp of the original deposit
   */
  event CapitalERC721Withdrawal(
    address indexed owner,
    uint256 positionId,
    address assetAddress,
    uint256 depositTimestamp
  );

  /**
   * @notice Emitted when an ERC721 capital asset has been harvested
   * @param positionId id for the capital position
   * @param assetAddress address of the underlying ERC721
   */
  event CapitalERC721Harvest(uint256 indexed positionId, address assetAddress);

  /**
   * @notice Emitted when an ERC721 capital asset has been "kicked", which may cause the underlying
   *  usdc equivalent value to change.
   * @param positionId id for the capital position
   * @param assetAddress address of the underlying ERC721
   * @param usdcEquivalent new usdc equivalent value of the position
   */
  event CapitalPositionAdjustment(
    uint256 indexed positionId,
    address assetAddress,
    uint256 usdcEquivalent
  );

  /// Thrown when called with an invalid asset type for the function. Valid
  /// types are defined under CapitalAssetType
  error InvalidAssetType(CapitalAssetType);

  /**
   * @notice Account for a deposit of `id` for the ERC721 asset at `assetAddress`.
   * @dev reverts with InvalidAssetType if `assetAddress` is not an ERC721
   * @param owner address that owns the position
   * @param assetAddress address of the ERC20 address
   * @param assetTokenId id of the ERC721 asset to add
   * @return id of the newly created position
   */
  function depositERC721(
    address owner,
    address assetAddress,
    uint256 assetTokenId
  ) external returns (uint256);

  /**
   * @notice Get the id of the ERC721 asset held by position `id`. Pair this with
   *  `assetAddressOf` to get the address & id of the nft.
   * @dev reverts with InvalidAssetType if `assetAddress` is not an ERC721
   * @param positionId id of the position
   * @return id of the underlying ERC721 asset
   */
  function erc721IdOf(uint256 positionId) external view returns (uint256);

  /**
   * @notice Completely withdraw a position
   * @param positionId id of the position
   */
  function withdraw(uint256 positionId) external;

  /**
   * @notice Harvests the associated rewards, interest, and other accrued assets
   *  associated with the asset token. For example, if given a PoolToken asset,
   *  this will collect the GFI rewards (if available), redeemable interest, and
   *  redeemable principal, and send that to the `owner`.
   * @param positionId id of the position
   */
  function harvest(uint256 positionId) external;

  /**
   * @notice Get the asset address of the position. Example: For an ERC721 position, this
   *  returns the address of that ERC721 contract.
   * @param positionId id of the position
   * @return asset address of the position
   */
  function assetAddressOf(uint256 positionId) external view returns (address);

  /**
   * @notice Get the owner of a given position.
   * @param positionId id of the position
   * @return owner of the position
   */
  function ownerOf(uint256 positionId) external view returns (address);

  /**
   * @notice Total number of positions in the ledger
   * @return number of positions in the ledger
   */
  function totalSupply() external view returns (uint256);

  /**
   * @notice Get the number of capital positions held by an address
   * @param addr address
   * @return positions held by address
   */
  function balanceOf(address addr) external view returns (uint256);

  /**
   * @notice Returns a position ID owned by `owner` at a given `index` of its position list
   * @param owner owner of the positions
   * @param index index of the owner's balance to get the position ID of
   * @return position id
   *
   * @dev use with {balanceOf} to enumerate all of `owner`'s positions
   */
  function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

  /**
   * @dev Returns a position ID at a given `index` of all the positions stored by the contract.
   * @param index index to get the position ID at
   * @return position id
   *
   * @dev use with {totalSupply} to enumerate all positions
   */
  function tokenByIndex(uint256 index) external view returns (uint256);

  /**
   * @notice Get the USDC value of `owner`s positions, reporting what is currently
   *  eligible and the total amount.
   * @param owner address owning the positions
   * @return eligibleAmount USDC value of positions eligible for rewards
   * @return totalAmount total USDC value of positions
   *
   * @dev this is used by Membership to determine how much is eligible in
   *  the current epoch vs the next epoch.
   */
  function totalsOf(
    address owner
  ) external view returns (uint256 eligibleAmount, uint256 totalAmount);
}

File 20 of 46 : ICreditLine.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

interface ICreditLine {
  function borrower() external view returns (address);

  function limit() external view returns (uint256);

  function maxLimit() external view returns (uint256);

  function interestApr() external view returns (uint256);

  function paymentPeriodInDays() external view returns (uint256);

  function principalGracePeriodInDays() external view returns (uint256);

  function termInDays() external view returns (uint256);

  function lateFeeApr() external view returns (uint256);

  function isLate() external view returns (bool);

  function withinPrincipalGracePeriod() external view returns (bool);

  // Accounting variables
  function balance() external view returns (uint256);

  function interestOwed() external view returns (uint256);

  function principalOwed() external view returns (uint256);

  function termEndTime() external view returns (uint256);

  function nextDueTime() external view returns (uint256);

  function interestAccruedAsOf() external view returns (uint256);

  function lastFullPaymentTime() external view returns (uint256);
}

File 21 of 46 : IERC20Splitter.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;

interface IERC20Splitter {
  function lastDistributionAt() external view returns (uint256);

  function distribute() external;

  function replacePayees(address[] calldata _payees, uint256[] calldata _shares) external;

  function pendingDistributionFor(address payee) external view returns (uint256);
}

File 22 of 46 : IGFILedger.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;

interface IGFILedger {
  struct Position {
    // Owner of the position
    address owner;
    // Index of the position in the ownership array
    uint256 ownedIndex;
    // Amount of GFI held in the position
    uint256 amount;
    // When the position was deposited
    uint256 depositTimestamp;
  }

  /**
   * @notice Emitted when a new GFI deposit has been made
   * @param owner address owning the deposit
   * @param positionId id for the deposit
   * @param amount how much GFI was deposited
   */
  event GFIDeposit(address indexed owner, uint256 indexed positionId, uint256 amount);

  /**
   * @notice Emitted when a new GFI withdrawal has been made. If the remaining amount is 0, the position has bee removed
   * @param owner address owning the withdrawn position
   * @param positionId id for the position
   * @param remainingAmount how much GFI is remaining in the position
   * @param depositTimestamp block.timestamp of the original deposit
   */
  event GFIWithdrawal(
    address indexed owner,
    uint256 indexed positionId,
    uint256 withdrawnAmount,
    uint256 remainingAmount,
    uint256 depositTimestamp
  );

  /**
   * @notice Account for a new deposit by the owner.
   * @param owner address to account for the deposit
   * @param amount how much was deposited
   * @return how much was deposited
   */
  function deposit(address owner, uint256 amount) external returns (uint256);

  /**
   * @notice Account for a new withdraw by the owner.
   * @param positionId id of the position
   * @return how much was withdrawn
   */
  function withdraw(uint256 positionId) external returns (uint256);

  /**
   * @notice Account for a new withdraw by the owner.
   * @param positionId id of the position
   * @param amount how much to withdraw
   * @return how much was withdrawn
   */
  function withdraw(uint256 positionId, uint256 amount) external returns (uint256);

  /**
   * @notice Get the number of GFI positions held by an address
   * @param addr address
   * @return positions held by address
   */
  function balanceOf(address addr) external view returns (uint256);

  /**
   * @notice Get the owner of a given position.
   * @param positionId id of the position
   * @return owner of the position
   */
  function ownerOf(uint256 positionId) external view returns (address);

  /**
   * @notice Total number of positions in the ledger
   * @return number of positions in the ledger
   */
  function totalSupply() external view returns (uint256);

  /**
   * @notice Returns a position ID owned by `owner` at a given `index` of its position list
   * @param owner owner of the positions
   * @param index index of the owner's balance to get the position ID of
   * @return position id
   *
   * @dev use with {balanceOf} to enumerate all of `owner`'s positions
   */
  function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

  /**
   * @dev Returns a position ID at a given `index` of all the positions stored by the contract.
   * @param index index to get the position ID at
   * @return token id
   *
   * @dev use with {totalSupply} to enumerate all positions
   */
  function tokenByIndex(uint256 index) external view returns (uint256);

  /**
   * @notice Get amount of GFI of `owner`s positions, reporting what is currently
   *  eligible and the total amount.
   * @return eligibleAmount GFI amount of positions eligible for rewards
   * @return totalAmount total GFI amount of positions
   *
   * @dev this is used by Membership to determine how much is eligible in
   *  the current epoch vs the next epoch.
   */
  function totalsOf(
    address owner
  ) external view returns (uint256 eligibleAmount, uint256 totalAmount);
}

File 23 of 46 : IGo.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

abstract contract IGo {
  uint256 public constant ID_TYPE_0 = 0;
  uint256 public constant ID_TYPE_1 = 1;
  uint256 public constant ID_TYPE_2 = 2;
  uint256 public constant ID_TYPE_3 = 3;
  uint256 public constant ID_TYPE_4 = 4;
  uint256 public constant ID_TYPE_5 = 5;
  uint256 public constant ID_TYPE_6 = 6;
  uint256 public constant ID_TYPE_7 = 7;
  uint256 public constant ID_TYPE_8 = 8;
  uint256 public constant ID_TYPE_9 = 9;
  uint256 public constant ID_TYPE_10 = 10;

  /// @notice Returns the address of the UniqueIdentity contract.
  function uniqueIdentity() external virtual returns (address);

  function go(address account) public view virtual returns (bool);

  function goOnlyIdTypes(
    address account,
    uint256[] calldata onlyIdTypes
  ) public view virtual returns (bool);

  /**
   * @notice Returns whether the provided account is go-listed for use of the SeniorPool on the Goldfinch protocol.
   * @param account The account whose go status to obtain
   * @return true if `account` is go listed
   */
  function goSeniorPool(address account) public view virtual returns (bool);
}

File 24 of 46 : IMembershipCollector.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;

interface IMembershipCollector {
  /// @notice Have the collector distribute `amount` of Fidu to `addr`
  /// @param addr address to distribute to
  /// @param amount amount to distribute
  function distributeFiduTo(address addr, uint256 amount) external;

  /// @notice Get the last epoch finalized by the collector. This means the
  ///  collector will no longer add rewards to the epoch.
  /// @return the last finalized epoch
  function lastFinalizedEpoch() external view returns (uint256);

  /// @notice Get the rewards associated with `epoch`. This amount may change
  ///  until `epoch` has been finalized (is less than or equal to getLastFinalizedEpoch)
  /// @return rewards associated with `epoch`
  function rewardsForEpoch(uint256 epoch) external view returns (uint256);

  /// @notice Estimate rewards for a given epoch. For epochs at or before lastFinalizedEpoch
  ///  this will be the fixed, accurate reward for the epoch. For the current and other
  ///  non-finalized epochs, this will be the value as if the epoch were finalized in that
  ///  moment.
  /// @param epoch epoch to estimate the rewards of
  /// @return rewards associated with `epoch`
  function estimateRewardsFor(uint256 epoch) external view returns (uint256);

  /// @notice Finalize all unfinalized epochs. Causes the reserve splitter to distribute
  ///  if there are unfinalized epochs so all possible rewards are distributed.
  function finalizeEpochs() external;
}

File 25 of 46 : IMembershipDirector.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;

interface IMembershipDirector {
  /**
   * @notice Adjust an `owner`s membership score and position due to the change
   *  in their GFI and Capital holdings
   * @param owner address who's holdings changed
   * @return id of membership position
   */
  function consumeHoldingsAdjustment(address owner) external returns (uint256);

  /**
   * @notice Collect all membership yield enhancements for the owner.
   * @param owner address to claim rewards for
   * @return amount of yield enhancements collected
   */
  function collectRewards(address owner) external returns (uint256);

  /**
   * @notice Check how many rewards are claimable for the owner. The return
   *  value here is how much would be retrieved by calling `collectRewards`.
   * @param owner address to calculate claimable rewards for
   * @return the amount of rewards that could be claimed by the owner
   */
  function claimableRewards(address owner) external view returns (uint256);

  /**
   * @notice Calculate the membership score
   * @param gfi Amount of gfi
   * @param capital Amount of capital in USDC
   * @return membership score
   */
  function calculateMembershipScore(uint256 gfi, uint256 capital) external view returns (uint256);

  /**
   * @notice Get the current score of `owner`
   * @param owner address to check the score of
   * @return eligibleScore score that is currently eligible for rewards
   * @return totalScore score that will be elgible for rewards next epoch
   */
  function currentScore(
    address owner
  ) external view returns (uint256 eligibleScore, uint256 totalScore);

  /**
   * @notice Get the sum of all member scores that are currently eligible and that will be eligible next epoch
   * @return eligibleTotal sum of all member scores that are currently eligible
   * @return nextEpochTotal sum of all member scores that will be eligible next epoch
   */
  function totalMemberScores()
    external
    view
    returns (uint256 eligibleTotal, uint256 nextEpochTotal);

  /**
   * @notice Estimate the score for an existing member, given some changes in GFI and capital
   * @param memberAddress the member's address
   * @param gfi the change in gfi holdings, denominated in GFI
   * @param capital the change in gfi holdings, denominated in USDC
   * @return score resulting score for the member given the GFI and capital changes
   */
  function estimateMemberScore(
    address memberAddress,
    int256 gfi,
    int256 capital
  ) external view returns (uint256 score);

  /// @notice Finalize all unfinalized epochs. Causes the reserve splitter to distribute
  ///  if there are unfinalized epochs so all possible rewards are distributed.
  function finalizeEpochs() external;
}

File 26 of 46 : IMembershipLedger.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;

interface IMembershipLedger {
  /**
   * @notice Set `addr`s allocated rewards back to 0
   * @param addr address to reset rewards on
   */
  function resetRewards(address addr) external;

  /**
   * @notice Allocate `amount` rewards for `addr` but do not send them
   * @param addr address to distribute rewards to
   * @param amount amount of rewards to allocate for `addr`
   * @return rewards total allocated to `addr`
   */
  function allocateRewardsTo(address addr, uint256 amount) external returns (uint256 rewards);

  /**
   * @notice Get the rewards allocated to a certain `addr`
   * @param addr the address to check pending rewards for
   * @return rewards pending rewards for `addr`
   */
  function getPendingRewardsFor(address addr) external view returns (uint256 rewards);

  /**
   * @notice Get the alpha parameter for the cobb douglas function. Will always be in (0,1).
   * @return numerator numerator for the alpha param
   * @return denominator denominator for the alpha param
   */
  function alpha() external view returns (uint128 numerator, uint128 denominator);
}

File 27 of 46 : IMembershipOrchestrator.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;

import {Context} from "../cake/Context.sol";

struct CapitalDeposit {
  /// Address of the asset being deposited
  /// @dev must be supported in CapitalAssets.sol
  address assetAddress;
  /// Id of the nft
  uint256 id;
}

struct Deposit {
  /// Amount of gfi to deposit
  uint256 gfi;
  /// List of capital deposits
  CapitalDeposit[] capitalDeposits;
}

struct DepositResult {
  uint256 membershipId;
  uint256 gfiPositionId;
  uint256[] capitalPositionIds;
}

struct ERC20Withdrawal {
  uint256 id;
  uint256 amount;
}

struct Withdrawal {
  /// List of gfi token ids to withdraw
  ERC20Withdrawal[] gfiPositions;
  /// List of capital token ids to withdraw
  uint256[] capitalPositions;
}

/**
 * @title MembershipOrchestrator
 * @notice Externally facing gateway to all Goldfinch membership functionality.
 * @author Goldfinch
 */
interface IMembershipOrchestrator {
  /**
   * @notice Deposit multiple assets defined in `multiDeposit`. Assets can include GFI, Staked Fidu,
   *  and others.
   * @param deposit struct describing all the assets to deposit
   * @return ids all of the ids of the created depoits, in the same order as deposit. If GFI is
   *  present, it will be the first id.
   */
  function deposit(Deposit calldata deposit) external returns (DepositResult memory);

  /**
   * @notice Withdraw multiple assets defined in `multiWithdraw`. Assets can be GFI or capital
   *  positions ids. Caller must have been permitted to act upon all of the positions.
   * @param withdrawal all of the GFI and Capital ids to withdraw
   */
  function withdraw(Withdrawal calldata withdrawal) external;

  /**
   * @notice Collect all membership rewards for the caller.
   * @return how many rewards were collected and sent to caller
   */
  function collectRewards() external returns (uint256);

  /**
   * @notice Harvest the rewards, interest, redeemable principal, or other assets
   *  associated with the underlying capital asset. For example, if given a PoolToken,
   *  this will collect the GFI rewards (if available), redeemable interest, and
   *  redeemable principal, and send that to the owner of the capital position.
   * @param capitalPositionIds id of the capital position to harvest the underlying asset of
   */
  function harvest(uint256[] calldata capitalPositionIds) external;

  /**
   * @notice Check how many rewards are claimable at this moment in time for caller.
   * @param addr the address to check claimable rewards for
   * @return how many rewards could be claimed by a call to `collectRewards`
   */
  function claimableRewards(address addr) external view returns (uint256);

  /**
   * @notice Check the voting power of a given address
   * @param addr the address to check the voting power of
   * @return the voting power
   */
  function votingPower(address addr) external view returns (uint256);

  /**
   * @notice Get all GFI in Membership held by `addr`. This returns the current eligible amount and the
   *  total amount of GFI.
   * @param addr the owner
   * @return eligibleAmount how much GFI is currently eligible for rewards
   * @return totalAmount how much GFI is currently eligible for rewards
   */
  function totalGFIHeldBy(
    address addr
  ) external view returns (uint256 eligibleAmount, uint256 totalAmount);

  /**
   * @notice Get all capital, denominated in USDC, in Membership held by `addr`. This returns the current
   *  eligible amount and the total USDC value of capital.
   * @param addr the owner
   * @return eligibleAmount how much USDC of capital is currently eligible for rewards
   * @return totalAmount how much  USDC of capital is currently eligible for rewards
   */
  function totalCapitalHeldBy(
    address addr
  ) external view returns (uint256 eligibleAmount, uint256 totalAmount);

  /**
   * @notice Get the member score of `addr`
   * @param addr the owner
   * @return eligibleScore the currently eligible score
   * @return totalScore the total score that will be eligible next epoch
   *
   * @dev if eligibleScore == totalScore then there are no changes between now and the next epoch
   */
  function memberScoreOf(
    address addr
  ) external view returns (uint256 eligibleScore, uint256 totalScore);

  /**
   * @notice Estimate rewards for a given epoch. For epochs at or before lastFinalizedEpoch
   *  this will be the fixed, accurate reward for the epoch. For the current and other
   *  non-finalized epochs, this will be the value as if the epoch were finalized in that
   *  moment.
   * @param epoch epoch to estimate the rewards of
   * @return rewards associated with `epoch`
   */
  function estimateRewardsFor(uint256 epoch) external view returns (uint256);

  /**
   * @notice Calculate what the Membership Score would be if a `gfi` amount of GFI and `capital` amount
   *  of Capital denominated in USDC were deposited.
   * @param gfi amount of GFI to estimate with
   * @param capital amount of capital to estimate with, denominated in USDC
   * @return score the resulting score
   */
  function calculateMemberScore(uint256 gfi, uint256 capital) external view returns (uint256 score);

  /**
   * @notice Get the sum of all member scores that are currently eligible and that will be eligible next epoch
   * @return eligibleTotal sum of all member scores that are currently eligible
   * @return nextEpochTotal sum of all member scores that will be eligible next epoch
   */
  function totalMemberScores()
    external
    view
    returns (uint256 eligibleTotal, uint256 nextEpochTotal);

  /**
   * @notice Estimate the score for an existing member, given some changes in GFI and capital
   * @param memberAddress the member's address
   * @param gfi the change in gfi holdings, denominated in GFI
   * @param capital the change in gfi holdings, denominated in USDC
   * @return score resulting score for the member given the GFI and capital changes
   */
  function estimateMemberScore(
    address memberAddress,
    int256 gfi,
    int256 capital
  ) external view returns (uint256 score);

  /// @notice Finalize all unfinalized epochs. Causes the reserve splitter to distribute
  ///  if there are unfinalized epochs so all possible rewards are distributed.
  function finalizeEpochs() external;
}

File 28 of 46 : IMembershipVault.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;

import "@openzeppelin/contracts-upgradeable/interfaces/IERC721Upgradeable.sol";

struct Position {
  // address owning the position
  address owner;
  // how much of the position is eligible as of checkpointEpoch
  uint256 eligibleAmount;
  // how much of the postion is eligible the epoch after checkpointEpoch
  uint256 nextEpochAmount;
  // when the position was first created
  uint256 createdTimestamp;
  // epoch of the last checkpoint
  uint256 checkpointEpoch;
}

/**
 * @title IMembershipVault
 * @notice Track assets held by owners in a vault, as well as the total held in the vault. Assets
 *  are not accounted for until the next epoch for MEV protection.
 * @author Goldfinch
 */
interface IMembershipVault is IERC721Upgradeable {
  /**
   * @notice Emitted when an owner has adjusted their holdings in a vault
   * @param owner the owner increasing their holdings
   * @param eligibleAmount the new eligible amount
   * @param nextEpochAmount the new next epoch amount
   */
  event AdjustedHoldings(address indexed owner, uint256 eligibleAmount, uint256 nextEpochAmount);

  /**
   * @notice Emitted when the total within the vault has changed
   * @param eligibleAmount new current amount
   * @param nextEpochAmount new next epoch amount
   */
  event VaultTotalUpdate(uint256 eligibleAmount, uint256 nextEpochAmount);

  /**
   * @notice Get the current value of `owner`. This changes depending on the current
   *  block.timestamp as increased holdings are not accounted for until the subsequent epoch.
   * @param owner address owning the positions
   * @return sum of all positions held by an address
   */
  function currentValueOwnedBy(address owner) external view returns (uint256);

  /**
   * @notice Get the total value in the vault as of block.timestamp
   * @return total value in the vault as of block.timestamp
   */
  function currentTotal() external view returns (uint256);

  /**
   * @notice Get the total value in the vault as of epoch
   * @return total value in the vault as of epoch
   */
  function totalAtEpoch(uint256 epoch) external view returns (uint256);

  /**
   * @notice Get the position owned by `owner`
   * @return position owned by `owner`
   */
  function positionOwnedBy(address owner) external view returns (Position memory);

  /**
   * @notice Record an adjustment in holdings. Eligible assets will update this epoch and
   *  total assets will become eligible the subsequent epoch.
   * @param owner the owner to checkpoint
   * @param eligibleAmount amount of points to apply to the current epoch
   * @param nextEpochAmount amount of points to apply to the next epoch
   * @return id of the position
   */
  function adjustHoldings(
    address owner,
    uint256 eligibleAmount,
    uint256 nextEpochAmount
  ) external returns (uint256);

  /**
   * @notice Checkpoint a specific owner & the vault total
   * @param owner the owner to checkpoint
   *
   * @dev to collect rewards, this must be called before `increaseHoldings` or
   *  `decreaseHoldings`. Those functions must call checkpoint internally
   *  so the historical data will be lost otherwise.
   */
  function checkpoint(address owner) external;
}

File 29 of 46 : IPoolTokens.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

import "./openzeppelin/IERC721.sol";

interface IPoolTokens is IERC721 {
  struct TokenInfo {
    address pool;
    uint256 tranche;
    uint256 principalAmount;
    uint256 principalRedeemed;
    uint256 interestRedeemed;
  }

  struct MintParams {
    uint256 principalAmount;
    uint256 tranche;
  }

  struct PoolInfo {
    uint256 totalMinted;
    uint256 totalPrincipalRedeemed;
    bool created;
  }

  /**
   * @notice Called by pool to create a debt position in a particular tranche and amount
   * @param params Struct containing the tranche and the amount
   * @param to The address that should own the position
   * @return tokenId The token ID (auto-incrementing integer across all pools)
   */
  function mint(MintParams calldata params, address to) external returns (uint256);

  /**
   * @notice Redeem principal and interest on a pool token. Called by valid pools as part of their redemption
   *  flow
   * @param tokenId pool token id
   * @param principalRedeemed principal to redeem. This cannot exceed the token's principal amount, and
   *  the redemption cannot cause the pool's total principal redeemed to exceed the pool's total minted
   *  principal
   * @param interestRedeemed interest to redeem.
   */
  function redeem(uint256 tokenId, uint256 principalRedeemed, uint256 interestRedeemed) external;

  /**
   * @notice Withdraw a pool token's principal up to the token's principalAmount. Called by valid pools
   *  as part of their withdraw flow before the pool is locked (i.e. before the principal is committed)
   * @param tokenId pool token id
   * @param principalAmount principal to withdraw
   */
  function withdrawPrincipal(uint256 tokenId, uint256 principalAmount) external;

  /**
   * @notice Burns a specific ERC721 token and removes deletes the token metadata for PoolTokens, BackerReards,
   *  and BackerStakingRewards
   * @param tokenId uint256 id of the ERC721 token to be burned.
   */
  function burn(uint256 tokenId) external;

  /**
   * @notice Called by the GoldfinchFactory to register the pool as a valid pool. Only valid pools can mint/redeem
   * tokens
   * @param newPool The address of the newly created pool
   */
  function onPoolCreated(address newPool) external;

  function getTokenInfo(uint256 tokenId) external view returns (TokenInfo memory);

  function getPoolInfo(address pool) external view returns (PoolInfo memory);

  /// @notice Query if `pool` is a valid pool. A pool is valid if it was created by the Goldfinch Factory
  function validPool(address pool) external view returns (bool);

  function isApprovedOrOwner(address spender, uint256 tokenId) external view returns (bool);

  /**
   * @notice Splits a pool token into two smaller positions. The original token is burned and all
   * its associated data is deleted.
   * @param tokenId id of the token to split.
   * @param newPrincipal1 principal amount for the first token in the split. The principal amount for the
   *  second token in the split is implicitly the original token's principal amount less newPrincipal1
   * @return tokenId1 id of the first token in the split
   * @return tokenId2 id of the second token in the split
   */
  function splitToken(
    uint256 tokenId,
    uint256 newPrincipal1
  ) external returns (uint256 tokenId1, uint256 tokenId2);

  /**
   * @notice Mint event emitted for a new TranchedPool deposit or when an existing pool token is
   *  split
   * @param owner address to which the token was minted
   * @param pool tranched pool that the deposit was in
   * @param tokenId ERC721 tokenId
   * @param amount the deposit amount
   * @param tranche id of the tranche of the deposit
   */
  event TokenMinted(
    address indexed owner,
    address indexed pool,
    uint256 indexed tokenId,
    uint256 amount,
    uint256 tranche
  );

  /**
   * @notice Redeem event emitted when interest and/or principal is redeemed in the token's pool
   * @param owner owner of the pool token
   * @param pool tranched pool that the token belongs to
   * @param principalRedeemed amount of principal redeemed from the pool
   * @param interestRedeemed amount of interest redeemed from the pool
   * @param tranche id of the tranche the token belongs to
   */
  event TokenRedeemed(
    address indexed owner,
    address indexed pool,
    uint256 indexed tokenId,
    uint256 principalRedeemed,
    uint256 interestRedeemed,
    uint256 tranche
  );

  /**
   * @notice Burn event emitted when the token owner/operator manually burns the token or burns
   *  it implicitly by splitting it
   * @param owner owner of the pool token
   * @param pool tranched pool that the token belongs to
   */
  event TokenBurned(address indexed owner, address indexed pool, uint256 indexed tokenId);

  /**
   * @notice Split event emitted when the token owner/operator splits the token
   * @param pool tranched pool to which the orginal and split tokens belong
   * @param tokenId id of the original token that was split
   * @param newTokenId1 id of the first split token
   * @param newPrincipal1 principalAmount of the first split token
   * @param newTokenId2 id of the second split token
   * @param newPrincipal2 principalAmount of the second split token
   */
  event TokenSplit(
    address indexed owner,
    address indexed pool,
    uint256 indexed tokenId,
    uint256 newTokenId1,
    uint256 newPrincipal1,
    uint256 newTokenId2,
    uint256 newPrincipal2
  );

  /**
   * @notice Principal Withdrawn event emitted when a token's principal is withdrawn from the pool
   *  BEFORE the pool's drawdown period
   * @param pool tranched pool of the token
   * @param principalWithdrawn amount of principal withdrawn from the pool
   */
  event TokenPrincipalWithdrawn(
    address indexed owner,
    address indexed pool,
    uint256 indexed tokenId,
    uint256 principalWithdrawn,
    uint256 tranche
  );
}

File 30 of 46 : IRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

/// @title IRouter
/// @author landakram
/// @notice This contract provides service discovery for contracts using the cake framework.
///   It can be used in conjunction with the convenience methods defined in the `Routing.Context`
///   and `Routing.Keys` libraries.
interface IRouter {
  event SetContract(bytes4 indexed key, address indexed addr);

  /// @notice Associate a routing key to a contract address
  /// @dev This function is only callable by the Router admin
  /// @param key A routing key (defined in the `Routing.Keys` libary)
  /// @param addr A contract address
  function setContract(bytes4 key, address addr) external;
}

File 31 of 46 : ISeniorPool.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

import {ITranchedPool} from "./ITranchedPool.sol";
import {ISeniorPoolEpochWithdrawals} from "./ISeniorPoolEpochWithdrawals.sol";

abstract contract ISeniorPool is ISeniorPoolEpochWithdrawals {
  uint256 public sharePrice;
  uint256 public totalLoansOutstanding;
  uint256 public totalWritedowns;

  function deposit(uint256 amount) external virtual returns (uint256 depositShares);

  function depositWithPermit(
    uint256 amount,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external virtual returns (uint256 depositShares);

  /**
   * @notice Withdraw `usdcAmount` of USDC, bypassing the epoch withdrawal system. Callable
   * by Zapper only.
   */
  function withdraw(uint256 usdcAmount) external virtual returns (uint256 amount);

  /**
   * @notice Withdraw `fiduAmount` of FIDU converted to USDC at the current share price,
   * bypassing the epoch withdrawal system. Callable by Zapper only
   */
  function withdrawInFidu(uint256 fiduAmount) external virtual returns (uint256 amount);

  function invest(ITranchedPool pool) external virtual returns (uint256);

  function estimateInvestment(ITranchedPool pool) external view virtual returns (uint256);

  function redeem(uint256 tokenId) external virtual;

  function writedown(uint256 tokenId) external virtual;

  function calculateWritedown(
    uint256 tokenId
  ) external view virtual returns (uint256 writedownAmount);

  function sharesOutstanding() external view virtual returns (uint256);

  function assets() external view virtual returns (uint256);

  function getNumShares(uint256 amount) public view virtual returns (uint256);

  event DepositMade(address indexed capitalProvider, uint256 amount, uint256 shares);
  event WithdrawalMade(address indexed capitalProvider, uint256 userAmount, uint256 reserveAmount);
  event InterestCollected(address indexed payer, uint256 amount);
  event PrincipalCollected(address indexed payer, uint256 amount);
  event ReserveFundsCollected(address indexed user, uint256 amount);
  event ReserveSharesCollected(address indexed user, address indexed reserve, uint256 amount);

  event PrincipalWrittenDown(address indexed tranchedPool, int256 amount);
  event InvestmentMadeInSenior(address indexed tranchedPool, uint256 amount);
  event InvestmentMadeInJunior(address indexed tranchedPool, uint256 amount);
}

File 32 of 46 : ISeniorPoolEpochWithdrawals.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;

pragma experimental ABIEncoderV2;

interface ISeniorPoolEpochWithdrawals {
  /**
   * @notice A withdrawal epoch
   * @param endsAt timestamp the epoch ends
   * @param fiduRequested amount of fidu requested in the epoch, including fidu
   *                      carried over from previous epochs
   * @param fiduLiquidated Amount of fidu that was liquidated at the end of this epoch
   * @param usdcAllocated Amount of usdc that was allocated to liquidate fidu.
   *                      Does not consider withdrawal fees.
   */
  struct Epoch {
    uint256 endsAt;
    uint256 fiduRequested;
    uint256 fiduLiquidated;
    uint256 usdcAllocated;
  }

  /**
   * @notice A user's request for withdrawal
   * @param epochCursor id of next epoch the user can liquidate their request
   * @param fiduRequested amount of fidu left to liquidate since last checkpoint
   * @param usdcWithdrawable amount of usdc available for a user to withdraw
   */
  struct WithdrawalRequest {
    uint256 epochCursor;
    uint256 usdcWithdrawable;
    uint256 fiduRequested;
  }

  /**
   * @notice Returns the amount of unallocated usdc in the senior pool, taking into account
   *         usdc that _will_ be allocated to withdrawals when a checkpoint happens
   */
  function usdcAvailable() external view returns (uint256);

  /// @notice Current duration of withdrawal epochs, in seconds
  function epochDuration() external view returns (uint256);

  /// @notice Update epoch duration
  function setEpochDuration(uint256 newEpochDuration) external;

  /// @notice The current withdrawal epoch
  function currentEpoch() external view returns (Epoch memory);

  /// @notice Get request by tokenId. A request is considered active if epochCursor > 0.
  function withdrawalRequest(uint256 tokenId) external view returns (WithdrawalRequest memory);

  /**
   * @notice Submit a request to withdraw `fiduAmount` of FIDU. Request is rejected
   * if caller already owns a request token. A non-transferrable request token is
   * minted to the caller
   * @return tokenId token minted to caller
   */
  function requestWithdrawal(uint256 fiduAmount) external returns (uint256 tokenId);

  /**
   * @notice Add `fiduAmount` FIDU to a withdrawal request for `tokenId`. Caller
   * must own tokenId
   */
  function addToWithdrawalRequest(uint256 fiduAmount, uint256 tokenId) external;

  /**
   * @notice Cancel request for tokenId. The fiduRequested (minus a fee) is returned
   * to the caller. Caller must own tokenId.
   * @return fiduReceived the fidu amount returned to the caller
   */
  function cancelWithdrawalRequest(uint256 tokenId) external returns (uint256 fiduReceived);

  /**
   * @notice Transfer the usdcWithdrawable of request for tokenId to the caller.
   * Caller must own tokenId
   */
  function claimWithdrawalRequest(uint256 tokenId) external returns (uint256 usdcReceived);

  /// @notice Emitted when the epoch duration is changed
  event EpochDurationChanged(uint256 newDuration);

  /// @notice Emitted when a new withdraw request has been created
  event WithdrawalRequested(
    uint256 indexed epochId,
    uint256 indexed tokenId,
    address indexed operator,
    uint256 fiduRequested
  );

  /// @notice Emitted when a user adds to their existing withdraw request
  /// @param epochId epoch that the withdraw was added to
  /// @param tokenId id of token that represents the position being added to
  /// @param operator address that added to the request
  /// @param fiduRequested amount of additional fidu added to request
  event WithdrawalAddedTo(
    uint256 indexed epochId,
    uint256 indexed tokenId,
    address indexed operator,
    uint256 fiduRequested
  );

  /// @notice Emitted when a withdraw request has been canceled
  event WithdrawalCanceled(
    uint256 indexed epochId,
    uint256 indexed tokenId,
    address indexed operator,
    uint256 fiduCanceled,
    uint256 reserveFidu
  );

  /// @notice Emitted when an epoch has been checkpointed
  /// @param epochId id of epoch that ended
  /// @param endTime timestamp the epoch ended
  /// @param fiduRequested amount of FIDU oustanding when the epoch ended
  /// @param usdcAllocated amount of USDC allocated to liquidate FIDU
  /// @param fiduLiquidated amount of FIDU liquidated using `usdcAllocated`
  event EpochEnded(
    uint256 indexed epochId,
    uint256 endTime,
    uint256 fiduRequested,
    uint256 usdcAllocated,
    uint256 fiduLiquidated
  );

  /// @notice Emitted when an epoch could not be finalized and is extended instead
  /// @param epochId id of epoch that was extended
  /// @param newEndTime new epoch end time
  /// @param oldEndTime previous epoch end time
  event EpochExtended(uint256 indexed epochId, uint256 newEndTime, uint256 oldEndTime);
}

File 33 of 46 : IStakingRewards.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;

pragma experimental ABIEncoderV2;

import {IERC721} from "./openzeppelin/IERC721.sol";
import {IERC721Metadata} from "./openzeppelin/IERC721Metadata.sol";
import {IERC721Enumerable} from "./openzeppelin/IERC721Enumerable.sol";

interface IStakingRewards is IERC721, IERC721Metadata, IERC721Enumerable {
  /// @notice Get the staking rewards position
  /// @param tokenId id of the position token
  /// @return position the position
  function getPosition(uint256 tokenId) external view returns (StakedPosition memory position);

  /// @notice Unstake an amount of `stakingToken()` (FIDU, FiduUSDCCurveLP, etc) associated with
  ///   a given position and transfer to msg.sender. Any remaining staked amount will continue to
  ///   accrue rewards.
  /// @dev This function checkpoints rewards
  /// @param tokenId A staking position token ID
  /// @param amount Amount of `stakingToken()` to be unstaked from the position
  function unstake(uint256 tokenId, uint256 amount) external;

  /// @notice Add `amount` to an existing FIDU position (`tokenId`)
  /// @param tokenId A staking position token ID
  /// @param amount Amount of `stakingToken()` to be added to tokenId's position
  function addToStake(uint256 tokenId, uint256 amount) external;

  /// @notice Returns the staked balance of a given position token.
  /// @dev The value returned is the bare amount, not the effective amount. The bare amount represents
  ///   the number of tokens the user has staked for a given position. The effective amount is the bare
  ///   amount multiplied by the token's underlying asset type multiplier. This multiplier is a crypto-
  ///   economic parameter determined by governance.
  /// @param tokenId A staking position token ID
  /// @return Amount of staked tokens denominated in `stakingToken().decimals()`
  function stakedBalanceOf(uint256 tokenId) external view returns (uint256);

  /// @notice Deposit to FIDU and USDC into the Curve LP, and stake your Curve LP tokens in the same transaction.
  /// @param fiduAmount The amount of FIDU to deposit
  /// @param usdcAmount The amount of USDC to deposit
  function depositToCurveAndStakeFrom(
    address nftRecipient,
    uint256 fiduAmount,
    uint256 usdcAmount
  ) external;

  /// @notice "Kick" a user's reward multiplier. If they are past their lock-up period, their reward
  ///   multiplier will be reset to 1x.
  /// @dev This will also checkpoint their rewards up to the current time.
  function kick(uint256 tokenId) external;

  /// @notice Accumulated rewards per token at the last checkpoint
  function accumulatedRewardsPerToken() external view returns (uint256);

  /// @notice The block timestamp when rewards were last checkpointed
  function lastUpdateTime() external view returns (uint256);

  /// @notice Claim rewards for a given staked position
  /// @param tokenId A staking position token ID
  /// @return amount of rewards claimed
  function getReward(uint256 tokenId) external returns (uint256);

  /* ========== EVENTS ========== */

  event RewardAdded(uint256 reward);
  event Staked(
    address indexed user,
    uint256 indexed tokenId,
    uint256 amount,
    StakedPositionType positionType,
    uint256 baseTokenExchangeRate
  );
  event DepositedAndStaked(
    address indexed user,
    uint256 depositedAmount,
    uint256 indexed tokenId,
    uint256 amount
  );
  event DepositedToCurve(
    address indexed user,
    uint256 fiduAmount,
    uint256 usdcAmount,
    uint256 tokensReceived
  );
  event DepositedToCurveAndStaked(
    address indexed user,
    uint256 fiduAmount,
    uint256 usdcAmount,
    uint256 indexed tokenId,
    uint256 amount
  );
  event AddToStake(
    address indexed user,
    uint256 indexed tokenId,
    uint256 amount,
    StakedPositionType positionType
  );
  event Unstaked(
    address indexed user,
    uint256 indexed tokenId,
    uint256 amount,
    StakedPositionType positionType
  );
  event UnstakedMultiple(address indexed user, uint256[] tokenIds, uint256[] amounts);
  event RewardPaid(address indexed user, uint256 indexed tokenId, uint256 reward);
  event RewardsParametersUpdated(
    address indexed who,
    uint256 targetCapacity,
    uint256 minRate,
    uint256 maxRate,
    uint256 minRateAtPercent,
    uint256 maxRateAtPercent
  );
  event EffectiveMultiplierUpdated(
    address indexed who,
    StakedPositionType positionType,
    uint256 multiplier
  );
}

/// @notice Indicates which ERC20 is staked
enum StakedPositionType {
  Fidu,
  CurveLP
}

struct Rewards {
  uint256 totalUnvested;
  uint256 totalVested;
  // @dev DEPRECATED (definition kept for storage slot)
  //   For legacy vesting positions, this was used in the case of slashing.
  //   For non-vesting positions, this is unused.
  uint256 totalPreviouslyVested;
  uint256 totalClaimed;
  uint256 startTime;
  // @dev DEPRECATED (definition kept for storage slot)
  //   For legacy vesting positions, this is the endTime of the vesting.
  //   For non-vesting positions, this is 0.
  uint256 endTime;
}

struct StakedPosition {
  // @notice Staked amount denominated in `stakingToken().decimals()`
  uint256 amount;
  // @notice Struct describing rewards owed with vesting
  Rewards rewards;
  // @notice Multiplier applied to staked amount when locking up position
  uint256 leverageMultiplier;
  // @notice Time in seconds after which position can be unstaked
  uint256 lockedUntil;
  // @notice Type of the staked position
  StakedPositionType positionType;
  // @notice Multiplier applied to staked amount to denominate in `baseStakingToken().decimals()`
  // @dev This field should not be used directly; it may be 0 for staked positions created prior to GIP-1.
  //  If you need this field, use `safeEffectiveMultiplier()`, which correctly handles old staked positions.
  uint256 unsafeEffectiveMultiplier;
  // @notice Exchange rate applied to staked amount to denominate in `baseStakingToken().decimals()`
  // @dev This field should not be used directly; it may be 0 for staked positions created prior to GIP-1.
  //  If you need this field, use `safeBaseTokenExchangeRate()`, which correctly handles old staked positions.
  uint256 unsafeBaseTokenExchangeRate;
}

File 34 of 46 : ITranchedPool.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

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

abstract contract ITranchedPool {
  IV2CreditLine public creditLine;
  uint256 public createdAt;
  enum Tranches {
    Reserved,
    Senior,
    Junior
  }

  struct TrancheInfo {
    uint256 id;
    uint256 principalDeposited;
    uint256 principalSharePrice;
    uint256 interestSharePrice;
    uint256 lockedUntil;
  }

  struct PoolSlice {
    TrancheInfo seniorTranche;
    TrancheInfo juniorTranche;
    uint256 totalInterestAccrued;
    uint256 principalDeployed;
  }

  function initialize(
    address _config,
    address _borrower,
    uint256 _juniorFeePercent,
    uint256 _limit,
    uint256 _interestApr,
    uint256 _paymentPeriodInDays,
    uint256 _termInDays,
    uint256 _lateFeeApr,
    uint256 _principalGracePeriodInDays,
    uint256 _fundableAt,
    uint256[] calldata _allowedUIDTypes
  ) public virtual;

  /**
   * @notice Get the array of all UID types that are allowed to interact with this pool.
   * @return array of UID types
   *
   * @dev This only exists on TranchedPools deployed from Nov 2022 onward
   */
  function getAllowedUIDTypes() external view virtual returns (uint256[] memory);

  function getTranche(uint256 tranche) external view virtual returns (TrancheInfo memory);

  function pay(uint256 amount) external virtual;

  function poolSlices(uint256 index) external view virtual returns (PoolSlice memory);

  function lockJuniorCapital() external virtual;

  function lockPool() external virtual;

  function initializeNextSlice(uint256 _fundableAt) external virtual;

  function totalJuniorDeposits() external view virtual returns (uint256);

  function drawdown(uint256 amount) external virtual;

  function setFundableAt(uint256 timestamp) external virtual;

  function deposit(uint256 tranche, uint256 amount) external virtual returns (uint256 tokenId);

  function assess() external virtual;

  function depositWithPermit(
    uint256 tranche,
    uint256 amount,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external virtual returns (uint256 tokenId);

  function availableToWithdraw(
    uint256 tokenId
  ) external view virtual returns (uint256 interestRedeemable, uint256 principalRedeemable);

  function withdraw(
    uint256 tokenId,
    uint256 amount
  ) external virtual returns (uint256 interestWithdrawn, uint256 principalWithdrawn);

  function withdrawMax(
    uint256 tokenId
  ) external virtual returns (uint256 interestWithdrawn, uint256 principalWithdrawn);

  function withdrawMultiple(
    uint256[] calldata tokenIds,
    uint256[] calldata amounts
  ) external virtual;

  function numSlices() external view virtual returns (uint256);
}

File 35 of 46 : IV2CreditLine.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

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

abstract contract IV2CreditLine is ICreditLine {
  function principal() external view virtual returns (uint256);

  function totalInterestAccrued() external view virtual returns (uint256);

  function termStartTime() external view virtual returns (uint256);

  function setLimit(uint256 newAmount) external virtual;

  function setMaxLimit(uint256 newAmount) external virtual;

  function setBalance(uint256 newBalance) external virtual;

  function setPrincipal(uint256 _principal) external virtual;

  function setTotalInterestAccrued(uint256 _interestAccrued) external virtual;

  function drawdown(uint256 amount) external virtual;

  function assess() external virtual returns (uint256, uint256, uint256);

  function initialize(
    address _config,
    address owner,
    address _borrower,
    uint256 _limit,
    uint256 _interestApr,
    uint256 _paymentPeriodInDays,
    uint256 _termInDays,
    uint256 _lateFeeApr,
    uint256 _principalGracePeriodInDays
  ) public virtual;

  function setTermEndTime(uint256 newTermEndTime) external virtual;

  function setNextDueTime(uint256 newNextDueTime) external virtual;

  function setInterestOwed(uint256 newInterestOwed) external virtual;

  function setPrincipalOwed(uint256 newPrincipalOwed) external virtual;

  function setInterestAccruedAsOf(uint256 newInterestAccruedAsOf) external virtual;

  function setWritedownAmount(uint256 newWritedownAmount) external virtual;

  function setLastFullPaymentTime(uint256 newLastFullPaymentTime) external virtual;

  function setLateFeeApr(uint256 newLateFeeApr) external virtual;
}

File 36 of 46 : IERC165.sol
pragma solidity >=0.6.0;

// This file copied from OZ, but with the version pragma updated to use >=.

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

File 37 of 46 : IERC721.sol
pragma solidity >=0.6.2;

// This file copied from OZ, but with the version pragma updated to use >= & reference other >= pragma interfaces.
// NOTE: Modified to reference our updated pragma version of IERC165
import "./IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

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

  /**
   * @dev Returns the owner of the NFT specified by `tokenId`.
   */
  function ownerOf(uint256 tokenId) external view returns (address owner);

  /**
   * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
   * another (`to`).
   *
   *
   *
   * Requirements:
   * - `from`, `to` cannot be zero.
   * - `tokenId` must be owned by `from`.
   * - If the caller is not `from`, it must be have been allowed to move this
   * NFT by either {approve} or {setApprovalForAll}.
   */
  function safeTransferFrom(address from, address to, uint256 tokenId) external;

  /**
   * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
   * another (`to`).
   *
   * Requirements:
   * - If the caller is not `from`, it must be approved to move this NFT by
   * either {approve} or {setApprovalForAll}.
   */
  function transferFrom(address from, address to, uint256 tokenId) external;

  function approve(address to, uint256 tokenId) external;

  function getApproved(uint256 tokenId) external view returns (address operator);

  function setApprovalForAll(address operator, bool _approved) external;

  function isApprovedForAll(address owner, address operator) external view returns (bool);

  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId,
    bytes calldata data
  ) external;
}

File 38 of 46 : IERC721Enumerable.sol
pragma solidity >=0.6.2;

// This file copied from OZ, but with the version pragma updated to use >=.

import "./IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
  function totalSupply() external view returns (uint256);

  function tokenOfOwnerByIndex(
    address owner,
    uint256 index
  ) external view returns (uint256 tokenId);

  function tokenByIndex(uint256 index) external view returns (uint256);
}

File 39 of 46 : IERC721Metadata.sol
pragma solidity >=0.6.2;

// This file copied from OZ, but with the version pragma updated to use >=.

import "./IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
  function name() external view returns (string memory);

  function symbol() external view returns (string memory);

  function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 40 of 46 : Arrays.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.16;

library Arrays {
  /**
   * @notice Removes an item from an array and replaces it with the (previously) last element in the array so
   *  there are no empty spaces. Assumes that `array` is not empty and index is valid.
   * @param array the array to remove from
   * @param index index of the item to remove
   * @return newLength length of the resulting array
   * @return replaced whether or not the index was replaced. Only false if the removed item was the last item
   *  in the array.
   */
  function reorderingRemove(
    uint256[] storage array,
    uint256 index
  ) internal returns (uint256 newLength, bool replaced) {
    newLength = array.length - 1;
    replaced = newLength != index;

    if (replaced) {
      array[index] = array[newLength];
    }

    array.pop();
  }
}

File 41 of 46 : FiduConversions.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.16;

library FiduConversions {
  uint256 internal constant FIDU_MANTISSA = 1e18;
  uint256 internal constant USDC_MANTISSA = 1e6;
  uint256 internal constant USDC_TO_FIDU_MANTISSA = FIDU_MANTISSA / USDC_MANTISSA;
  uint256 internal constant FIDU_USDC_CONVERSION_DECIMALS = USDC_TO_FIDU_MANTISSA * FIDU_MANTISSA;

  /**
   * @notice Convert Usdc to Fidu using a given share price
   * @param usdcAmount amount of usdc to convert
   * @param sharePrice share price to use to convert
   * @return fiduAmount converted fidu amount
   */
  function usdcToFidu(uint256 usdcAmount, uint256 sharePrice) internal pure returns (uint256) {
    return sharePrice > 0 ? (usdcAmount * FIDU_USDC_CONVERSION_DECIMALS) / sharePrice : 0;
  }

  /**
   * @notice Convert fidu to USDC using a given share price
   * @param fiduAmount fidu amount to convert
   * @param sharePrice share price to do the conversion with
   * @return usdcReceived usdc that will be received after converting
   */
  function fiduToUsdc(uint256 fiduAmount, uint256 sharePrice) internal pure returns (uint256) {
    return (fiduAmount * sharePrice) / FIDU_USDC_CONVERSION_DECIMALS;
  }
}

File 42 of 46 : Epochs.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.16;

library Epochs {
  uint256 internal constant EPOCH_SECONDS = 7 days;

  /**
   * @notice Get the epoch containing the timestamp `s`
   * @param s the timestamp
   * @return corresponding epoch
   */
  function fromSeconds(uint256 s) internal pure returns (uint256) {
    return s / EPOCH_SECONDS;
  }

  /**
   * @notice Get the current epoch for the block.timestamp
   * @return current epoch
   */
  function current() internal view returns (uint256) {
    return fromSeconds(block.timestamp);
  }

  /**
   * @notice Get the start timestamp for the current epoch
   * @return current epoch start timestamp
   */
  function currentEpochStartTimestamp() internal view returns (uint256) {
    return startOf(current());
  }

  /**
   * @notice Get the previous epoch given block.timestamp
   * @return previous epoch
   */
  function previous() internal view returns (uint256) {
    return current() - 1;
  }

  /**
   * @notice Get the next epoch given block.timestamp
   * @return next epoch
   */
  function next() internal view returns (uint256) {
    return current() + 1;
  }

  /**
   * @notice Get the Unix timestamp of the start of `epoch`
   * @param epoch the epoch
   * @return unix timestamp
   */
  function startOf(uint256 epoch) internal pure returns (uint256) {
    return epoch * EPOCH_SECONDS;
  }
}

File 43 of 46 : UserEpochTotals.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.16;

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

/// @dev Epoch Awareness
/// The Membership system relies on an epoch structure to incentivize economic behavior. Deposits
/// are tracked by epoch and only count toward yield enhancements if they have been present for
/// an entire epoch. This means positions have a specific lifetime:
/// 1. Deposit Epoch - Positions are in the membership system but do not count for rewards as they
///      were not in since the beginning of the epoch. Deposits are externally triggered.
/// 2. Eligible Epoch - Positions are in the membership system and count for rewards as they have been
///      present the entire epoch.
/// 3. Withdrawal Epoch - Positions are no longer in the membership system and forfeit their rewards
///      for the withdrawal epoch. Rewards are forfeited as the position was not present for the
///      entire epoch when withdrawn. Withdrawals are externally triggered.
///
/// All of these deposits' value is summed together to calculate the yield enhancement. A naive
/// approach is, for every summation query, iterate over all deposits and check if they were deposited
/// in the current epoch (so case (1)) or in a previous epoch (so case (2)). This has a high gas
/// cost, so we use another approach: UserEpochTotal.
///
/// UserEpochTotal is the total of the user's deposits as of its lastEpochUpdate- the last epoch that
/// the total was updated in. For that epoch, it tracks:
/// 1. Eligible Amount - The sum of deposits that are in their Eligible Epoch for the current epoch
/// 2. Total Amount - The sum of deposits that will be in their Eligible Epoch for the next epoch
///
/// It is not necessary to track previous epochs as deposits in those will already be eligible, or they
/// will have been withdrawn and already affected the eligible amount.
///
/// It is also unnecessary to track future epochs beyond the next one. Any deposit in the current epoch
/// will become eligible in the next epoch. It is not possible to have a deposit (or withdrawal) take
/// effect any further in the future.

struct UserEpochTotal {
  /// Total amount that will be eligible for membership, after `checkpointedAt` epoch
  uint256 totalAmount;
  /// Amount eligible for membership, as of `checkpointedAt` epoch
  uint256 eligibleAmount;
  /// Last epoch the total was checkpointed at
  uint256 checkpointedAt;
}

library UserEpochTotals {
  error InvalidDepositEpoch(uint256 epoch);

  /// @notice Record an increase of `amount` in the `total`. This is counted toward the
  ///  nextAmount as deposits must be present for an entire epoch to be valid.
  /// @param total storage pointer to the UserEpochTotal
  /// @param amount amount to increase the total by
  function recordIncrease(UserEpochTotal storage total, uint256 amount) internal {
    _checkpoint(total);

    total.totalAmount += amount;
  }

  /// @notice Record an increase of `amount` instantly based on the time of the deposit.
  ///  This is counted either:
  ///  1. To just the totalAmount if the deposit was this epoch
  ///  2. To both the totalAmount and eligibleAmount if the deposit was before this epoch
  /// @param total storage pointer to the UserEpochTotal
  /// @param amount amount to increase the total by
  function recordInstantIncrease(
    UserEpochTotal storage total,
    uint256 amount,
    uint256 depositTimestamp
  ) internal {
    uint256 depositEpoch = Epochs.fromSeconds(depositTimestamp);
    if (depositEpoch > Epochs.current()) revert InvalidDepositEpoch(depositEpoch);

    _checkpoint(total);

    if (depositEpoch < Epochs.current()) {
      // If this was deposited earlier, then it also counts towards eligible
      total.eligibleAmount += amount;
    }

    total.totalAmount += amount;
  }

  /// @notice Record a decrease of `amount` in the `total`. Depending on the `depositTimestamp`
  ///  this will withdraw from the total's currentAmount (if it's withdrawn from an already valid deposit)
  ///  or from the total's nextAmount (if it's withdrawn from a deposit this epoch).
  /// @param total storage pointer to the UserEpochTotal
  /// @param amount amount to decrease the total by
  /// @param depositTimestamp timestamp of the deposit associated with `amount`
  function recordDecrease(
    UserEpochTotal storage total,
    uint256 amount,
    uint256 depositTimestamp
  ) internal {
    uint256 depositEpoch = Epochs.fromSeconds(depositTimestamp);
    if (depositEpoch > Epochs.current()) revert InvalidDepositEpoch(depositEpoch);

    _checkpoint(total);

    total.totalAmount -= amount;

    if (depositEpoch < Epochs.current()) {
      // If this was deposited earlier, then it would have been promoted in _checkpoint and must be removed.
      total.eligibleAmount -= amount;
    }
  }

  /// @notice Get the up-to-date current and next amount for the `_total`. UserEpochTotals
  ///  may have a lastEpochUpdate of long ago. This returns the current and next amounts as if it had
  ///  been checkpointed just now.
  /// @param _total storage pointer to the UserEpochTotal
  /// @return current the currentAmount of the UserEpochTotal
  /// @return next the nextAmount of the UserEpochTotal
  function getTotals(
    UserEpochTotal storage _total
  ) internal view returns (uint256 current, uint256 next) {
    UserEpochTotal memory total = _total;
    if (Epochs.current() == total.checkpointedAt) {
      return (total.eligibleAmount, total.totalAmount);
    }

    return (total.totalAmount, total.totalAmount);
  }

  //////////////////////////////////////////////////////////////////
  // Private

  function _checkpoint(UserEpochTotal storage total) private {
    // Only promote the total amount if we've moved to the next epoch
    // after the last checkpoint.
    if (Epochs.current() <= total.checkpointedAt) return;

    total.eligibleAmount = total.totalAmount;

    total.checkpointedAt = Epochs.current();
  }
}

File 44 of 46 : CapitalAssets.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.16;

import "@openzeppelin/contracts-upgradeable/interfaces/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/interfaces/IERC721Upgradeable.sol";

import {Context} from "../../../../cake/Context.sol";
import {CapitalAssetType} from "../../../../interfaces/ICapitalLedger.sol";
import "../../../../cake/Routing.sol" as Routing;

/// @dev Adding a New Asset Type
/// 1. Create a new library in this directory of the name <AssetType>Asset.sol
/// 2. The library must implement the same functions as the other assets:
///   2.1 AssetType
///   2.2 isType
///   2.3 isValid - if the asset is an ERC721
///   2.4 getUsdcEquivalent
/// 3. Import the library below in "Supported assets"
/// 4. Add the new library to the corresponding `getSupportedType` function in this file
/// 5. Add the new library to the corresponding `getUsdcEquivalent` function in this file
/// 6. If the new library is an ERC721, add it to the `isValid` function in this file

// Supported assets
import {PoolTokensAsset} from "./PoolTokensAsset.sol";
import {StakedFiduAsset} from "./StakedFiduAsset.sol";

using Routing.Context for Context;

library CapitalAssets {
  /// Thrown when an asset has been requested that does not exist
  error InvalidAsset(address assetAddress);
  /// Thrown when an asset has been requested that does not exist
  error InvalidAssetWithId(address assetAddress, uint256 assetTokenId);

  /**
   * @notice Check if a specific `assetAddress` has a corresponding capital asset
   *  implementation and returns the asset type. Returns INVALID if no
   *  such asset exists.
   * @param context goldfinch context for routing
   * @param assetAddress the address of the asset's contract
   * @return type of the asset
   */
  function getSupportedType(
    Context context,
    address assetAddress
  ) internal view returns (CapitalAssetType) {
    if (StakedFiduAsset.isType(context, assetAddress)) return StakedFiduAsset.AssetType;
    if (PoolTokensAsset.isType(context, assetAddress)) return PoolTokensAsset.AssetType;

    return CapitalAssetType.INVALID;
  }

  //////////////////////////////////////////////////////////////////
  // ERC721

  /**
   * @notice Check if a specific token for a supported asset is valid or not. Returns false
   *  if the asset is not supported or the token is invalid
   * @param context goldfinch context for routing
   * @param assetAddress the address of the asset's contract
   * @param assetTokenId the token id
   * @return whether or not a specific token id of asset address is supported
   */
  function isValid(
    Context context,
    address assetAddress,
    uint256 assetTokenId
  ) internal view returns (bool) {
    if (StakedFiduAsset.isType(context, assetAddress))
      return StakedFiduAsset.isValid(context, assetTokenId);
    if (PoolTokensAsset.isType(context, assetAddress))
      return PoolTokensAsset.isValid(context, assetTokenId);

    return false;
  }

  /**
   * @notice Get the point-in-time USDC equivalent value of the ERC721 asset. This
   *  specifically attempts to return the "principle" or "at-risk" USDC value of
   *  the asset and does not include rewards, interest, or other benefits.
   * @param context goldfinch context for routing
   * @param asset ERC721 to evaluate
   * @param assetTokenId id of the token to evaluate
   * @return USDC equivalent value
   */
  function getUsdcEquivalent(
    Context context,
    IERC721Upgradeable asset,
    uint256 assetTokenId
  ) internal view returns (uint256) {
    if (PoolTokensAsset.isType(context, address(asset))) {
      return PoolTokensAsset.getUsdcEquivalent(context, assetTokenId);
    }

    if (StakedFiduAsset.isType(context, address(asset))) {
      return StakedFiduAsset.getUsdcEquivalent(context, assetTokenId);
    }

    revert InvalidAsset(address(asset));
  }

  /**
   * @notice Harvests the associated rewards, interest, and other accrued assets
   *  associated with the asset token. For example, if given a PoolToken asset,
   *  this will collect the GFI rewards (if available), redeemable interest, and
   *  redeemable principal, and send that to the `owner`.
   * @param context goldfinch context for routing
   * @param owner address to send the harvested assets to
   * @param asset ERC721 to harvest
   * @param assetTokenId id of the token to harvest
   */
  function harvest(
    Context context,
    address owner,
    IERC721Upgradeable asset,
    uint256 assetTokenId
  ) internal {
    if (PoolTokensAsset.isType(context, address(asset))) {
      return PoolTokensAsset.harvest(context, owner, assetTokenId);
    }

    if (StakedFiduAsset.isType(context, address(asset))) {
      return StakedFiduAsset.harvest(context, owner, assetTokenId);
    }

    revert InvalidAsset(address(asset));
  }
}

File 45 of 46 : PoolTokensAsset.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.16;

import "@openzeppelin/contracts-upgradeable/interfaces/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/interfaces/IERC721Upgradeable.sol";
// solhint-disable-next-line max-line-length
import {SafeERC20Upgradeable as SafeERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

import {Context} from "../../../../cake/Context.sol";
import "../../../../cake/Routing.sol" as Routing;

import {CapitalAssetType} from "../../../../interfaces/ICapitalLedger.sol";
import {IPoolTokens} from "../../../../interfaces/IPoolTokens.sol";

import {ITranchedPool} from "../../../../interfaces/ITranchedPool.sol";

using Routing.Context for Context;
using SafeERC20 for IERC20Upgradeable;

library PoolTokensAsset {
  /// Thrown when trying to harvest a pool token when not go-listed
  error NotGoListed(address owner);

  CapitalAssetType public constant AssetType = CapitalAssetType.ERC721;

  /**
   * @notice Get the type of asset that this contract adapts.
   * @return the asset type
   */
  function isType(Context context, address assetAddress) internal view returns (bool) {
    return assetAddress == address(context.poolTokens());
  }

  /**
   * @notice Get whether or not the given asset is valid
   * @return true if the represented tranche is or may be drawn down (so true if assets are doing work)
   */
  function isValid(Context context, uint256 assetTokenId) internal view returns (bool) {
    IPoolTokens.TokenInfo memory tokenInfo = context.poolTokens().getTokenInfo(assetTokenId);
    ITranchedPool tranchedPool = ITranchedPool(tokenInfo.pool);

    return tranchedPool.getTranche(tokenInfo.tranche).lockedUntil != 0;
  }

  /**
   * @notice Get the point-in-time USDC equivalent value of the Pool Token asset. This
   *  specifically attempts to return the "principle" or "at-risk" USDC value of
   *  the asset and does not include rewards, interest, or other benefits.
   * @param context goldfinch context for routing
   * @param assetTokenId tokenId of the Pool Token to evaluate
   * @return USDC equivalent value
   */
  function getUsdcEquivalent(
    Context context,
    uint256 assetTokenId
  ) internal view returns (uint256) {
    IPoolTokens.TokenInfo memory tokenInfo = context.poolTokens().getTokenInfo(assetTokenId);
    return tokenInfo.principalAmount - tokenInfo.principalRedeemed;
  }

  /**
   * @notice Harvest GFI rewards and redeemable interest and principal on PoolToken with id
   *  `assetTokenId` and send the harvested assets to `owner`.
   * @param context goldfinch context for routing
   * @param owner address to send the harvested assets to
   * @param assetTokenId id of the position to harvest
   */
  function harvest(Context context, address owner, uint256 assetTokenId) internal {
    IPoolTokens.TokenInfo memory tokenInfo = context.poolTokens().getTokenInfo(assetTokenId);
    ITranchedPool tranchedPool = ITranchedPool(tokenInfo.pool);

    if (!context.go().goOnlyIdTypes(owner, getAllowedUIDs(tokenInfo.pool))) {
      revert NotGoListed(owner);
    }

    (uint256 interestWithdrawn, uint256 principalWithdrawn) = tranchedPool.withdrawMax(
      assetTokenId
    );
    context.usdc().safeTransfer(owner, interestWithdrawn + principalWithdrawn);

    try context.backerRewards().withdraw(assetTokenId) returns (uint256 rewards) {
      // Withdraw can throw if the pool is late or if it's an early pool and doesn't
      // have associated backer rewards. Try/catch so the interest and principal can
      // still be harvested.

      context.gfi().safeTransfer(owner, rewards);
    } catch {}
  }

  function getAllowedUIDs(address poolAddress) private view returns (uint256[] memory allowedUIDs) {
    // TranchedPools are non-upgradeable and have different capabilites. One of the differences
    // is the `getAllowedUIDTypes` function, which is only available in contracts deployed from
    // Nov 2022 onward. To get around this limitation, we hardcode the expected UID requirements
    // based on the pool address for previous contracts. Otherwise, we use the available method.
    // Pools below are listed in chronological order for convenience.

    if (
      poolAddress == 0xefeB69eDf6B6999B0e3f2Fa856a2aCf3bdEA4ab5 || // almavest 3
      poolAddress == 0xaA2ccC5547f64C5dFfd0a624eb4aF2543A67bA65 || // tugende
      poolAddress == 0xc9BDd0D3B80CC6EfE79a82d850f44EC9B55387Ae || // cauris
      poolAddress == 0xe6C30756136e07eB5268c3232efBFBe645c1BA5A || // almavest 4
      poolAddress == 0x1d596D28A7923a22aA013b0e7082bbA23DAA656b // almavest 5
    ) {
      // Legacy pools that had custom checks upon signup

      allowedUIDs = new uint256[](1);
      allowedUIDs[0] = 0;
      return allowedUIDs;
    }

    if (poolAddress == 0x418749e294cAbce5A714EfcCC22a8AAde6F9dB57 /* almavest 6 */) {
      // Old pool that has internal UID check but does not provide a gas-efficient UID interface
      // Copied the pool's UID requirements below

      allowedUIDs = new uint256[](1);
      allowedUIDs[0] = 0;
      return allowedUIDs;
    }

    if (
      poolAddress == 0x00c27FC71b159a346e179b4A1608a0865e8A7470 || // stratos
      poolAddress == 0xd09a57127BC40D680Be7cb061C2a6629Fe71AbEf // cauris 2
    ) {
      // Old pools that have internal UID check but do not provide a gas-efficient UID interface
      // Copied the pools' UID requirements below

      allowedUIDs = new uint256[](2);
      allowedUIDs[0] = 0;
      allowedUIDs[1] = 1;
      return allowedUIDs;
    }

    if (
      poolAddress == 0xb26B42Dd5771689D0a7faEea32825ff9710b9c11 || // lend east 1
      poolAddress == 0x759f097f3153f5d62FF1C2D82bA78B6350F223e3 || // almavest 7
      poolAddress == 0x89d7C618a4EeF3065DA8ad684859a547548E6169 // addem capital
    ) {
      // Old pools that have internal UID check but do not provide a gas-efficient UID interface
      // Copied the pools' UID requirements below

      allowedUIDs = new uint256[](4);
      allowedUIDs[0] = 0;
      allowedUIDs[1] = 1;
      allowedUIDs[2] = 3;
      allowedUIDs[3] = 4;
      return allowedUIDs;
    }

    // All other and future pools implement getAllowedUIDTypes
    return ITranchedPool(poolAddress).getAllowedUIDTypes();
  }
}

File 46 of 46 : StakedFiduAsset.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.16;

import "@openzeppelin/contracts-upgradeable/interfaces/IERC20Upgradeable.sol";
// solhint-disable-next-line max-line-length
import {SafeERC20Upgradeable as SafeERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

import "../../../../library/FiduConversions.sol";
import {Context} from "../../../../cake/Context.sol";
import "../../../../cake/Routing.sol" as Routing;

import {CapitalAssetType} from "../../../../interfaces/ICapitalLedger.sol";
import {IStakingRewards, StakedPositionType} from "../../../../interfaces/IStakingRewards.sol";
import {ISeniorPool} from "../../../../interfaces/ISeniorPool.sol";

using Routing.Context for Context;
using SafeERC20 for IERC20Upgradeable;

library StakedFiduAsset {
  CapitalAssetType public constant AssetType = CapitalAssetType.ERC721;

  /**
   * @notice Get the type of asset that this contract adapts.
   * @return the asset type
   */
  function isType(Context context, address assetAddress) internal view returns (bool) {
    return assetAddress == address(context.stakingRewards());
  }

  /**
   * @notice Get whether or not the given asset is valid
   * @return true if the asset is Fidu type (not CurveLP)
   */
  function isValid(Context context, uint256 assetTokenId) internal view returns (bool) {
    return
      context.stakingRewards().getPosition(assetTokenId).positionType == StakedPositionType.Fidu;
  }

  /**
   * @notice Get the point-in-time USDC equivalent value of the ERC721 asset. This
   *  specifically attempts to return the "principle" or "at-risk" USDC value of
   *  the asset and does not include rewards, interest, or other benefits.
   * @param context goldfinch context for routing
   * @param assetTokenId id of the position to evaluate
   * @return USDC equivalent value
   */
  function getUsdcEquivalent(
    Context context,
    uint256 assetTokenId
  ) internal view returns (uint256) {
    uint256 stakedFiduBalance = context.stakingRewards().stakedBalanceOf(assetTokenId);
    return FiduConversions.fiduToUsdc(stakedFiduBalance, context.seniorPool().sharePrice());
  }

  /**
   * @notice Harvest GFI rewards on a staked fidu token and send them to `owner`.
   * @param context goldfinch context for routing
   * @param owner address to send the GFI to
   * @param assetTokenId id of the position to harvest
   */
  function harvest(Context context, address owner, uint256 assetTokenId) internal {
    // Sends reward to owner (this contract)
    uint256 reward = context.stakingRewards().getReward(assetTokenId);

    if (reward > 0) {
      context.gfi().safeTransfer(owner, reward);
    }
  }
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 100
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract Context","name":"_context","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"IndexGreaterThanTokenSupply","type":"error"},{"inputs":[{"internalType":"address","name":"assetAddress","type":"address"}],"name":"InvalidAsset","type":"error"},{"inputs":[{"internalType":"enum CapitalAssetType","name":"","type":"uint8"}],"name":"InvalidAssetType","type":"error"},{"inputs":[{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"uint256","name":"assetTokenId","type":"uint256"}],"name":"InvalidAssetWithId","type":"error"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"InvalidDepositEpoch","type":"error"},{"inputs":[],"name":"InvalidOwnerIndex","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"InvalidWithdrawAmount","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NotGoListed","type":"error"},{"inputs":[{"internalType":"address","name":"resource","type":"address"},{"internalType":"address","name":"accessor","type":"address"}],"name":"RequiresOperator","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroDeposit","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"assetAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assetTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usdcEquivalent","type":"uint256"}],"name":"CapitalERC721Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":false,"internalType":"address","name":"assetAddress","type":"address"}],"name":"CapitalERC721Harvest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":false,"internalType":"address","name":"assetAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositTimestamp","type":"uint256"}],"name":"CapitalERC721Withdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":false,"internalType":"address","name":"assetAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"usdcEquivalent","type":"uint256"}],"name":"CapitalPositionAdjustment","type":"event"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"assetAddressOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"uint256","name":"assetTokenId","type":"uint256"}],"name":"depositERC721","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"erc721IdOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"positions","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"ownedIndex","type":"uint256"},{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"uint256","name":"usdcEquivalent","type":"uint256"},{"internalType":"uint256","name":"depositTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"totalsOf","outputs":[{"internalType":"uint256","name":"eligibleAmount","type":"uint256"},{"internalType":"uint256","name":"totalAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b5060405162002b1238038062002b12833981016040819052620000349162000046565b6001600160a01b031660805262000078565b6000602082840312156200005957600080fd5b81516001600160a01b03811681146200007157600080fd5b9392505050565b608051612a3f620000d36000396000818161035f015281816105de01528181610639015281816106880152818161070e015281816108be0152818161093601528181610eb501528181610f58015261100d0152612a3f6000f3fe608060405234801561001057600080fd5b50600436106100bf5760003560e01c80634f6ccce71161007c5780634f6ccce71461016e5780636352211e1461018157806370a08231146101b757806399fbab88146101e0578063c4664c6314610262578063ddc632621461028e578063e305f306146102a157600080fd5b8063150b7a02146100c457806318160ddd146100f95780632e1a7d4d1461010b5780632f745c5914610120578063331ded1a14610133578063427d743214610146575b600080fd5b6100e36100d2366004612320565b630a85bd0160e11b95945050505050565b6040516100f091906123bf565b60405180910390f35b6003545b6040519081526020016100f0565b61011e6101193660046123d4565b6102c1565b005b6100fd61012e3660046123ed565b610546565b6100fd610141366004612419565b6105bc565b61015961015436600461245a565b6107e8565b604080519283526020830191909152016100f0565b6100fd61017c3660046123d4565b610814565b6101aa61018f3660046123d4565b6000908152602081905260409020546001600160a01b031690565b6040516100f09190612477565b6100fd6101c536600461245a565b6001600160a01b031660009081526001602052604090205490565b6102296101ee3660046123d4565b600060208190529081526040902080546001820154600283015460038401546004909401546001600160a01b03938416949293909116919085565b604080516001600160a01b039687168152602081019590955292909416918301919091526060820152608081019190915260a0016100f0565b6101aa6102703660046123d4565b6000908152602081905260409020600201546001600160a01b031690565b61011e61029c3660046123d4565b610849565b6100fd6102af3660046123d4565b60009081526004602052604090205490565b6000805160206129ea8339815191526102da81336109a8565b600082815260208181526040808320815160a08101835281546001600160a01b03808216835260018401805484880152600285018054928316968501968752600386018054606087015260048701805460808801528c8b52988a90526001600160a01b03199485169096559088905591169055908490559183905551909190610384907f000000000000000000000000000000000000000000000000000000000000000090610a0b565b6060830151608084015184516001600160a01b031660009081526002602052604090209293506103b5929190610a44565b81516001600160a01b031660009081526001602090815260408220908401519091906103e2908390610acd565b915050801561042a578360200151600080848760200151815481106104095761040961248b565b90600052602060002001548152602001908152602001600020600101819055505b600183600181111561043e5761043e6124a1565b0361051a57600086815260046020819052604080832080549390558681015187519151632142170760e11b81526001600160a01b03909116926342842e0e9261048b9230928791016124b7565b600060405180830381600087803b1580156104a557600080fd5b505af11580156104b9573d6000803e3d6000fd5b5050865160408089015160808a015182518d81526001600160a01b0392831660208201529283015290911692507f50065562d2b38b7cc51ee877211c37a54ff469d129d1a67bfeb97e0c2994a316915060600160405180910390a25061053e565b826040516308a8fee960e31b815260040161053591906124db565b60405180910390fd5b505050505050565b6001600160a01b038216600090815260016020526040812054821061057e57604051638f832b9b60e01b815260040160405180910390fd5b6001600160a01b03831660009081526001602052604090208054839081106105a8576105a861248b565b906000526020600020015490505b92915050565b60006000805160206129ea8339815191526105d781336109a8565b60016106037f000000000000000000000000000000000000000000000000000000000000000086610a0b565b6001811115610614576106146124a1565b1461063457836040516337bce3c560e11b81526004016105359190612477565b61065f7f00000000000000000000000000000000000000000000000000000000000000008585610b56565b610680578383604051634eb9917160e11b8152600401610535929190612503565b8360006106ae7f00000000000000000000000000000000000000000000000000000000000000008387610b9c565b905060006106bd888884610beb565b604080516020808201835289825260008481526004825283812092519092556001600160a01b038c16825260029052209091506106fa9083610cb0565b826001600160a01b03166342842e0e61073b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610cd6565b30896040518463ffffffff1660e01b815260040161075b939291906124b7565b600060405180830381600087803b15801561077557600080fd5b505af1158015610789573d6000803e3d6000fd5b505060408051848152602081018a90529081018590526001600160a01b03808b1693508b1691507fb1c56a0ea823a5b59843a1c0223e87b70affc07e96fc346165322958cdef13949060600160405180910390a3979650505050505050565b6001600160a01b0381166000908152600260205260408120819061080b90610db4565b91509150915091565b600061081f60035490565b821061083e57604051633f7ee38f60e11b815260040160405180910390fd5b6105b6826001612532565b6000805160206129ea83398151915261086281336109a8565b600082815260208181526040808320815160a08101835281546001600160a01b0390811682526001830154948201949094526002820154909316918301829052600381015460608401526004015460808301529091906108e3907f000000000000000000000000000000000000000000000000000000000000000090610a0b565b905060018160018111156108f9576108f96124a1565b1461091957806040516308a8fee960e31b815260040161053591906124db565b815160408084015160008781526004602052919091205461095d927f0000000000000000000000000000000000000000000000000000000000000000929091610e05565b837f54c43c33795a11f3152d46be48143a092092361e37f765f6f9da0251aabf1f9683604001516040516109919190612477565b60405180910390a26109a284610e59565b50505050565b6001600160a01b0381166109cf5760405163d92e233d60e01b815260040160405180910390fd5b6109d98282610fff565b610a075760405163889a56bb60e01b81523060048201526001600160a01b0382166024820152604401610535565b5050565b6000610a17838361110a565b15610a24575060016105b6565b610a2e838361113a565b15610a3b575060016105b6565b50600092915050565b6000610a4f8261114e565b9050610a5961115d565b811115610a7c57604051632029102760e21b815260048101829052602401610535565b610a858461116d565b82846000016000828254610a999190612545565b90915550610aa7905061115d565b8110156109a25782846001016000828254610ac29190612545565b909155505050505050565b81546000908190610ae090600190612545565b915050818114801590610b2957838281548110610aff57610aff61248b565b9060005260206000200154848481548110610b1c57610b1c61248b565b6000918252602090912001555b83805480610b3957610b39612558565b600190038181906000526020600020016000905590559250929050565b6000610b62848461110a565b15610b7857610b718483611199565b9050610b95565b610b82848461113a565b15610b9157610b71848361123a565b5060005b9392505050565b6000610ba8848461113a565b15610bb757610b718483611341565b610bc1848461110a565b15610bd057610b7184836113e2565b826040516337bce3c560e11b81526004016105359190612477565b6003805460009182610bfc8361256e565b9091555050600380546040805160a0810182526001600160a01b0397881680825260009081526001602081815284832080548286019081529a8c16858701908152606086019a8b52426080870190815288865285845296852095518654908e166001600160a01b03199182161787559b51868501555160028601805491909d169b169a909a17909a559651948201949094559051600490910155838652845493840185559381529390932001819055919050565b610cb98261116d565b80826000016000828254610ccd9190612532565b90915550505050565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3a9190612587565b6001600160a01b0316630c0450126000805160206129ea8339815191526040518263ffffffff1660e01b8152600401610d7391906123bf565b602060405180830381865afa158015610d90573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b69190612587565b6040805160608101825282548152600183015460208201526002830154918101829052600091829190610de561115d565b03610dfb57602081015190519094909350915050565b5193849350915050565b610e0f848361113a565b15610e2457610e1f8484836114e3565b6109a2565b610e2e848361110a565b15610e3e57610e1f84848361175a565b816040516337bce3c560e11b81526004016105359190612477565b600081815260208181526040808320815160a08101835281546001600160a01b039081168252600183015494820194909452600282015490931691830182905260038101546060840152600401546080830152909190610eda907f000000000000000000000000000000000000000000000000000000000000000090610a0b565b90506001816001811115610ef057610ef06124a1565b14610f1057806040516308a8fee960e31b815260040161053591906124db565b6060820151608083015183516001600160a01b03166000908152600260205260409020610f3e929091610a44565b6040808301516000858152600460205291822054610f7d917f000000000000000000000000000000000000000000000000000000000000000091610b9c565b600085815260208181526040808320600301849055608087015187516001600160a01b031684526002909252909120919250610fbb919083906117fd565b837f6a28020abc16896e42077f4597b339d5cdf310f437dee250bcfb161a8539b27a846040015183604051610ff1929190612503565b60405180910390a250505050565b6000816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611069573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108d9190612587565b6001600160a01b0316630c045012856040518263ffffffff1660e01b81526004016110b891906123bf565b602060405180830381865afa1580156110d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f99190612587565b6001600160a01b0316149392505050565b600061111e836001600160a01b031661187b565b6001600160a01b0316826001600160a01b031614905092915050565b600061111e836001600160a01b031661192a565b60006105b662093a80836125a4565b60006111684261114e565b905090565b806002015461117a61115d565b116111825750565b8054600182015561119161115d565b600290910155565b6000806111ae846001600160a01b031661187b565b6001600160a01b031663eb02c301846040518263ffffffff1660e01b81526004016111db91815260200190565b61018060405180830381865afa1580156111f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061121d9190612690565b608001516001811115611232576112326124a1565b149392505050565b60008061124f846001600160a01b031661192a565b6001600160a01b0316638c7a63ae846040518263ffffffff1660e01b815260040161127c91815260200190565b60a060405180830381865afa158015611299573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bd9190612751565b8051602082015160405163d972e8ad60e01b81526004810191909152919250906001600160a01b0382169063d972e8ad9060240160a060405180830381865afa15801561130e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133291906127ac565b60800151151595945050505050565b600080611356846001600160a01b031661192a565b6001600160a01b0316638c7a63ae846040518263ffffffff1660e01b815260040161138391815260200190565b60a060405180830381865afa1580156113a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c49190612751565b9050806060015181604001516113da9190612545565b949350505050565b6000806113f7846001600160a01b031661187b565b6001600160a01b031663aa04295f846040518263ffffffff1660e01b815260040161142491815260200190565b602060405180830381865afa158015611441573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146591906127fc565b90506113da8161147d866001600160a01b03166119d9565b6001600160a01b031663872697296040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114de91906127fc565b611a88565b60006114f7846001600160a01b031661192a565b6001600160a01b0316638c7a63ae836040518263ffffffff1660e01b815260040161152491815260200190565b60a060405180830381865afa158015611541573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115659190612751565b805190915061157c6001600160a01b038616611abe565b6001600160a01b0316631852f200856115988560000151611b6d565b6040518363ffffffff1660e01b81526004016115b5929190612815565b602060405180830381865afa1580156115d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f6919061286b565b61161557836040516373bb9c8960e01b81526004016105359190612477565b604051632a8a9f1360e21b81526004810184905260009081906001600160a01b0384169063aa2a7c4c9060240160408051808303816000875af1158015611660573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611684919061288d565b90925090506116b9866116978385612532565b6116a98a6001600160a01b0316611ec3565b6001600160a01b03169190611f72565b6116cb876001600160a01b0316611fcd565b6001600160a01b0316632e1a7d4d866040518263ffffffff1660e01b81526004016116f891815260200190565b6020604051808303816000875af1925050508015611733575060408051601f3d908101601f19168201909252611730918101906127fc565b60015b156117515761174f87826116a98b6001600160a01b031661207c565b505b50505050505050565b600061176e846001600160a01b031661187b565b6001600160a01b0316631c4b774b836040518263ffffffff1660e01b815260040161179b91815260200190565b6020604051808303816000875af11580156117ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117de91906127fc565b905080156109a2576109a283826116a9876001600160a01b031661207c565b60006118088261114e565b905061181261115d565b81111561183557604051632029102760e21b815260048101829052602401610535565b61183e8461116d565b61184661115d565b81101561186757828460010160008282546118619190612532565b90915550505b82846000016000828254610ac29190612532565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118df9190612587565b6001600160a01b0316630c0450127f5a31296b3cecaf3aaa3146a140f6ddf6d21caa95e28243dc507d90d7e883be796040518263ffffffff1660e01b8152600401610d7391906123bf565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa15801561196a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061198e9190612587565b6001600160a01b0316630c0450127f7c9945d349966e663465dd2ad9f3ebe33d671877c22c40ca3a051e02e14e15ff6040518263ffffffff1660e01b8152600401610d7391906123bf565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a3d9190612587565b6001600160a01b0316630c0450127fd4a8913f0cbda73931759328604779fb81c2513f90d252b9c4a96a5dd8327d886040518263ffffffff1660e01b8152600401610d7391906123bf565b6000670de0b6b3a7640000611aa0620f4240826125a4565b611aaa91906128b1565b611ab483856128b1565b610b9591906125a4565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611afe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b229190612587565b6001600160a01b0316630c0450127fc61c93ad97e9ad12d54663c76606860c6c3a3f53a59b88cd4bc1e3f43edc17956040518263ffffffff1660e01b8152600401610d7391906123bf565b606073efeb69edf6b6999b0e3f2fa856a2acf3bdea4ab56001600160a01b0383161480611bb6575073aa2ccc5547f64c5dffd0a624eb4af2543a67ba656001600160a01b038316145b80611bdd575073c9bdd0d3b80cc6efe79a82d850f44ec9b55387ae6001600160a01b038316145b80611c04575073e6c30756136e07eb5268c3232efbfbe645c1ba5a6001600160a01b038316145b80611c2b5750731d596d28a7923a22aa013b0e7082bba23daa656b6001600160a01b038316145b15611c855760015b604051908082528060200260200182016040528015611c5c578160200160208202803683370190505b509050600081600081518110611c7457611c7461248b565b602002602001018181525050919050565b6001600160a01b03821673418749e294cabce5a714efccc22a8aade6f9db5703611cb0576001611c33565b72c27fc71b159a346e179b4a1608a0865e8a74706001600160a01b0383161480611cf6575073d09a57127bc40d680be7cb061c2a6629fe71abef6001600160a01b038316145b15611d51576040805160028082526060820183529091602083019080368337019050509050600081600081518110611d3057611d3061248b565b602002602001018181525050600181600181518110611c7457611c7461248b565b73b26b42dd5771689d0a7faeea32825ff9710b9c116001600160a01b0383161480611d98575073759f097f3153f5d62ff1c2d82ba78b6350f223e36001600160a01b038316145b80611dbf57507389d7c618a4eef3065da8ad684859a547548e61696001600160a01b038316145b15611e5d5760408051600480825260a082019092529060208201608080368337019050509050600081600081518110611dfa57611dfa61248b565b602002602001018181525050600181600181518110611e1b57611e1b61248b565b602002602001018181525050600381600281518110611e3c57611e3c61248b565b602002602001018181525050600481600381518110611c7457611c7461248b565b816001600160a01b031663c78bed866040518163ffffffff1660e01b8152600401600060405180830381865afa158015611e9b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105b691908101906128d0565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f279190612587565b6001600160a01b0316630c0450127fd6aca1be9729c13d677335161321649cccae6a591554772516700f986f942eaa6040518263ffffffff1660e01b8152600401610d7391906123bf565b611fc88363a9059cbb60e01b8484604051602401611f91929190612503565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261212b565b505050565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa15801561200d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120319190612587565b6001600160a01b0316630c0450127f1cd0c2e09edd1d00946872d4dfa90e139030cf08dd8b201be0c3e01ae1534c576040518263ffffffff1660e01b8152600401610d7391906123bf565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120e09190612587565b6001600160a01b0316630c0450127f6fdcaa094af96c56039c701c590da2284d9e0c364f2dd8ee7d43419f0eefd2936040518263ffffffff1660e01b8152600401610d7391906123bf565b6000612180826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166121fd9092919063ffffffff16565b805190915015611fc8578080602001905181019061219e919061286b565b611fc85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610535565b60606113da848460008585843b6122565760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610535565b600080866001600160a01b03168587604051612272919061299a565b60006040518083038185875af1925050503d80600081146122af576040519150601f19603f3d011682016040523d82523d6000602084013e6122b4565b606091505b50915091506122c48282866122cf565b979650505050505050565b606083156122de575081610b95565b8251156122ee5782518084602001fd5b8160405162461bcd60e51b815260040161053591906129b6565b6001600160a01b038116811461231d57600080fd5b50565b60008060008060006080868803121561233857600080fd5b853561234381612308565b9450602086013561235381612308565b935060408601359250606086013567ffffffffffffffff8082111561237757600080fd5b818801915088601f83011261238b57600080fd5b81358181111561239a57600080fd5b8960208285010111156123ac57600080fd5b9699959850939650602001949392505050565b6001600160e01b031991909116815260200190565b6000602082840312156123e657600080fd5b5035919050565b6000806040838503121561240057600080fd5b823561240b81612308565b946020939093013593505050565b60008060006060848603121561242e57600080fd5b833561243981612308565b9250602084013561244981612308565b929592945050506040919091013590565b60006020828403121561246c57600080fd5b8135610b9581612308565b6001600160a01b0391909116815260200190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b6001600160a01b039384168152919092166020820152604081019190915260600190565b60208101600283106124fd57634e487b7160e01b600052602160045260246000fd5b91905290565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052601160045260246000fd5b808201808211156105b6576105b661251c565b818103818111156105b6576105b661251c565b634e487b7160e01b600052603160045260246000fd5b6000600182016125805761258061251c565b5060010190565b60006020828403121561259957600080fd5b8151610b9581612308565b6000826125c157634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052604160045260246000fd5b60405160e0810167ffffffffffffffff811182821017156125ff576125ff6125c6565b60405290565b60405160c0810167ffffffffffffffff811182821017156125ff576125ff6125c6565b60405160a0810167ffffffffffffffff811182821017156125ff576125ff6125c6565b604051601f8201601f1916810167ffffffffffffffff81118282101715612674576126746125c6565b604052919050565b80516002811061268b57600080fd5b919050565b60008183036101808112156126a457600080fd5b6126ac6125dc565b8351815260c0601f19830112156126c257600080fd5b6126ca612605565b91506020840151825260408401516020830152606084015160408301526080840151606083015260a0840151608083015260c084015160a083015281602082015260e08401516040820152610100840151606082015261272d610120850161267c565b608082015261014084015160a08201526101609093015160c0840152509092915050565b600060a0828403121561276357600080fd5b61276b612628565b825161277681612308565b80825250602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b600060a082840312156127be57600080fd5b6127c6612628565b82518152602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b60006020828403121561280e57600080fd5b5051919050565b6001600160a01b038316815260406020808301829052835191830182905260009184820191906060850190845b8181101561285e57845183529383019391830191600101612842565b5090979650505050505050565b60006020828403121561287d57600080fd5b81518015158114610b9557600080fd5b600080604083850312156128a057600080fd5b505080516020909101519092909150565b60008160001904831182151516156128cb576128cb61251c565b500290565b600060208083850312156128e357600080fd5b825167ffffffffffffffff808211156128fb57600080fd5b818501915085601f83011261290f57600080fd5b815181811115612921576129216125c6565b8060051b915061293284830161264b565b818152918301840191848101908884111561294c57600080fd5b938501935b8385101561296a57845182529385019390850190612951565b98975050505050505050565b60005b83811015612991578181015183820152602001612979565b50506000910152565b600082516129ac818460208701612976565b9190910192915050565b60208152600082518060208401526129d5816040850160208701612976565b601f01601f1916919091016040019291505056fe9e45de9588e74df19c5f35dd4ddf9c698e7a3e36b4004fd44cb75621507d7092a26469706673582212202c288a699522a8ece2b9cf6db116af017d3c938cd5dea7f99604701c52253f0c64736f6c63430008100033000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c83

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100bf5760003560e01c80634f6ccce71161007c5780634f6ccce71461016e5780636352211e1461018157806370a08231146101b757806399fbab88146101e0578063c4664c6314610262578063ddc632621461028e578063e305f306146102a157600080fd5b8063150b7a02146100c457806318160ddd146100f95780632e1a7d4d1461010b5780632f745c5914610120578063331ded1a14610133578063427d743214610146575b600080fd5b6100e36100d2366004612320565b630a85bd0160e11b95945050505050565b6040516100f091906123bf565b60405180910390f35b6003545b6040519081526020016100f0565b61011e6101193660046123d4565b6102c1565b005b6100fd61012e3660046123ed565b610546565b6100fd610141366004612419565b6105bc565b61015961015436600461245a565b6107e8565b604080519283526020830191909152016100f0565b6100fd61017c3660046123d4565b610814565b6101aa61018f3660046123d4565b6000908152602081905260409020546001600160a01b031690565b6040516100f09190612477565b6100fd6101c536600461245a565b6001600160a01b031660009081526001602052604090205490565b6102296101ee3660046123d4565b600060208190529081526040902080546001820154600283015460038401546004909401546001600160a01b03938416949293909116919085565b604080516001600160a01b039687168152602081019590955292909416918301919091526060820152608081019190915260a0016100f0565b6101aa6102703660046123d4565b6000908152602081905260409020600201546001600160a01b031690565b61011e61029c3660046123d4565b610849565b6100fd6102af3660046123d4565b60009081526004602052604090205490565b6000805160206129ea8339815191526102da81336109a8565b600082815260208181526040808320815160a08101835281546001600160a01b03808216835260018401805484880152600285018054928316968501968752600386018054606087015260048701805460808801528c8b52988a90526001600160a01b03199485169096559088905591169055908490559183905551909190610384907f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c8390610a0b565b6060830151608084015184516001600160a01b031660009081526002602052604090209293506103b5929190610a44565b81516001600160a01b031660009081526001602090815260408220908401519091906103e2908390610acd565b915050801561042a578360200151600080848760200151815481106104095761040961248b565b90600052602060002001548152602001908152602001600020600101819055505b600183600181111561043e5761043e6124a1565b0361051a57600086815260046020819052604080832080549390558681015187519151632142170760e11b81526001600160a01b03909116926342842e0e9261048b9230928791016124b7565b600060405180830381600087803b1580156104a557600080fd5b505af11580156104b9573d6000803e3d6000fd5b5050865160408089015160808a015182518d81526001600160a01b0392831660208201529283015290911692507f50065562d2b38b7cc51ee877211c37a54ff469d129d1a67bfeb97e0c2994a316915060600160405180910390a25061053e565b826040516308a8fee960e31b815260040161053591906124db565b60405180910390fd5b505050505050565b6001600160a01b038216600090815260016020526040812054821061057e57604051638f832b9b60e01b815260040160405180910390fd5b6001600160a01b03831660009081526001602052604090208054839081106105a8576105a861248b565b906000526020600020015490505b92915050565b60006000805160206129ea8339815191526105d781336109a8565b60016106037f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c8386610a0b565b6001811115610614576106146124a1565b1461063457836040516337bce3c560e11b81526004016105359190612477565b61065f7f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c838585610b56565b610680578383604051634eb9917160e11b8152600401610535929190612503565b8360006106ae7f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c838387610b9c565b905060006106bd888884610beb565b604080516020808201835289825260008481526004825283812092519092556001600160a01b038c16825260029052209091506106fa9083610cb0565b826001600160a01b03166342842e0e61073b7f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c836001600160a01b0316610cd6565b30896040518463ffffffff1660e01b815260040161075b939291906124b7565b600060405180830381600087803b15801561077557600080fd5b505af1158015610789573d6000803e3d6000fd5b505060408051848152602081018a90529081018590526001600160a01b03808b1693508b1691507fb1c56a0ea823a5b59843a1c0223e87b70affc07e96fc346165322958cdef13949060600160405180910390a3979650505050505050565b6001600160a01b0381166000908152600260205260408120819061080b90610db4565b91509150915091565b600061081f60035490565b821061083e57604051633f7ee38f60e11b815260040160405180910390fd5b6105b6826001612532565b6000805160206129ea83398151915261086281336109a8565b600082815260208181526040808320815160a08101835281546001600160a01b0390811682526001830154948201949094526002820154909316918301829052600381015460608401526004015460808301529091906108e3907f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c8390610a0b565b905060018160018111156108f9576108f96124a1565b1461091957806040516308a8fee960e31b815260040161053591906124db565b815160408084015160008781526004602052919091205461095d927f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c83929091610e05565b837f54c43c33795a11f3152d46be48143a092092361e37f765f6f9da0251aabf1f9683604001516040516109919190612477565b60405180910390a26109a284610e59565b50505050565b6001600160a01b0381166109cf5760405163d92e233d60e01b815260040160405180910390fd5b6109d98282610fff565b610a075760405163889a56bb60e01b81523060048201526001600160a01b0382166024820152604401610535565b5050565b6000610a17838361110a565b15610a24575060016105b6565b610a2e838361113a565b15610a3b575060016105b6565b50600092915050565b6000610a4f8261114e565b9050610a5961115d565b811115610a7c57604051632029102760e21b815260048101829052602401610535565b610a858461116d565b82846000016000828254610a999190612545565b90915550610aa7905061115d565b8110156109a25782846001016000828254610ac29190612545565b909155505050505050565b81546000908190610ae090600190612545565b915050818114801590610b2957838281548110610aff57610aff61248b565b9060005260206000200154848481548110610b1c57610b1c61248b565b6000918252602090912001555b83805480610b3957610b39612558565b600190038181906000526020600020016000905590559250929050565b6000610b62848461110a565b15610b7857610b718483611199565b9050610b95565b610b82848461113a565b15610b9157610b71848361123a565b5060005b9392505050565b6000610ba8848461113a565b15610bb757610b718483611341565b610bc1848461110a565b15610bd057610b7184836113e2565b826040516337bce3c560e11b81526004016105359190612477565b6003805460009182610bfc8361256e565b9091555050600380546040805160a0810182526001600160a01b0397881680825260009081526001602081815284832080548286019081529a8c16858701908152606086019a8b52426080870190815288865285845296852095518654908e166001600160a01b03199182161787559b51868501555160028601805491909d169b169a909a17909a559651948201949094559051600490910155838652845493840185559381529390932001819055919050565b610cb98261116d565b80826000016000828254610ccd9190612532565b90915550505050565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3a9190612587565b6001600160a01b0316630c0450126000805160206129ea8339815191526040518263ffffffff1660e01b8152600401610d7391906123bf565b602060405180830381865afa158015610d90573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b69190612587565b6040805160608101825282548152600183015460208201526002830154918101829052600091829190610de561115d565b03610dfb57602081015190519094909350915050565b5193849350915050565b610e0f848361113a565b15610e2457610e1f8484836114e3565b6109a2565b610e2e848361110a565b15610e3e57610e1f84848361175a565b816040516337bce3c560e11b81526004016105359190612477565b600081815260208181526040808320815160a08101835281546001600160a01b039081168252600183015494820194909452600282015490931691830182905260038101546060840152600401546080830152909190610eda907f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c8390610a0b565b90506001816001811115610ef057610ef06124a1565b14610f1057806040516308a8fee960e31b815260040161053591906124db565b6060820151608083015183516001600160a01b03166000908152600260205260409020610f3e929091610a44565b6040808301516000858152600460205291822054610f7d917f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c8391610b9c565b600085815260208181526040808320600301849055608087015187516001600160a01b031684526002909252909120919250610fbb919083906117fd565b837f6a28020abc16896e42077f4597b339d5cdf310f437dee250bcfb161a8539b27a846040015183604051610ff1929190612503565b60405180910390a250505050565b6000816001600160a01b03167f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c836001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611069573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108d9190612587565b6001600160a01b0316630c045012856040518263ffffffff1660e01b81526004016110b891906123bf565b602060405180830381865afa1580156110d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f99190612587565b6001600160a01b0316149392505050565b600061111e836001600160a01b031661187b565b6001600160a01b0316826001600160a01b031614905092915050565b600061111e836001600160a01b031661192a565b60006105b662093a80836125a4565b60006111684261114e565b905090565b806002015461117a61115d565b116111825750565b8054600182015561119161115d565b600290910155565b6000806111ae846001600160a01b031661187b565b6001600160a01b031663eb02c301846040518263ffffffff1660e01b81526004016111db91815260200190565b61018060405180830381865afa1580156111f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061121d9190612690565b608001516001811115611232576112326124a1565b149392505050565b60008061124f846001600160a01b031661192a565b6001600160a01b0316638c7a63ae846040518263ffffffff1660e01b815260040161127c91815260200190565b60a060405180830381865afa158015611299573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bd9190612751565b8051602082015160405163d972e8ad60e01b81526004810191909152919250906001600160a01b0382169063d972e8ad9060240160a060405180830381865afa15801561130e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133291906127ac565b60800151151595945050505050565b600080611356846001600160a01b031661192a565b6001600160a01b0316638c7a63ae846040518263ffffffff1660e01b815260040161138391815260200190565b60a060405180830381865afa1580156113a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c49190612751565b9050806060015181604001516113da9190612545565b949350505050565b6000806113f7846001600160a01b031661187b565b6001600160a01b031663aa04295f846040518263ffffffff1660e01b815260040161142491815260200190565b602060405180830381865afa158015611441573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146591906127fc565b90506113da8161147d866001600160a01b03166119d9565b6001600160a01b031663872697296040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114de91906127fc565b611a88565b60006114f7846001600160a01b031661192a565b6001600160a01b0316638c7a63ae836040518263ffffffff1660e01b815260040161152491815260200190565b60a060405180830381865afa158015611541573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115659190612751565b805190915061157c6001600160a01b038616611abe565b6001600160a01b0316631852f200856115988560000151611b6d565b6040518363ffffffff1660e01b81526004016115b5929190612815565b602060405180830381865afa1580156115d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f6919061286b565b61161557836040516373bb9c8960e01b81526004016105359190612477565b604051632a8a9f1360e21b81526004810184905260009081906001600160a01b0384169063aa2a7c4c9060240160408051808303816000875af1158015611660573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611684919061288d565b90925090506116b9866116978385612532565b6116a98a6001600160a01b0316611ec3565b6001600160a01b03169190611f72565b6116cb876001600160a01b0316611fcd565b6001600160a01b0316632e1a7d4d866040518263ffffffff1660e01b81526004016116f891815260200190565b6020604051808303816000875af1925050508015611733575060408051601f3d908101601f19168201909252611730918101906127fc565b60015b156117515761174f87826116a98b6001600160a01b031661207c565b505b50505050505050565b600061176e846001600160a01b031661187b565b6001600160a01b0316631c4b774b836040518263ffffffff1660e01b815260040161179b91815260200190565b6020604051808303816000875af11580156117ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117de91906127fc565b905080156109a2576109a283826116a9876001600160a01b031661207c565b60006118088261114e565b905061181261115d565b81111561183557604051632029102760e21b815260048101829052602401610535565b61183e8461116d565b61184661115d565b81101561186757828460010160008282546118619190612532565b90915550505b82846000016000828254610ac29190612532565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118df9190612587565b6001600160a01b0316630c0450127f5a31296b3cecaf3aaa3146a140f6ddf6d21caa95e28243dc507d90d7e883be796040518263ffffffff1660e01b8152600401610d7391906123bf565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa15801561196a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061198e9190612587565b6001600160a01b0316630c0450127f7c9945d349966e663465dd2ad9f3ebe33d671877c22c40ca3a051e02e14e15ff6040518263ffffffff1660e01b8152600401610d7391906123bf565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a3d9190612587565b6001600160a01b0316630c0450127fd4a8913f0cbda73931759328604779fb81c2513f90d252b9c4a96a5dd8327d886040518263ffffffff1660e01b8152600401610d7391906123bf565b6000670de0b6b3a7640000611aa0620f4240826125a4565b611aaa91906128b1565b611ab483856128b1565b610b9591906125a4565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611afe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b229190612587565b6001600160a01b0316630c0450127fc61c93ad97e9ad12d54663c76606860c6c3a3f53a59b88cd4bc1e3f43edc17956040518263ffffffff1660e01b8152600401610d7391906123bf565b606073efeb69edf6b6999b0e3f2fa856a2acf3bdea4ab56001600160a01b0383161480611bb6575073aa2ccc5547f64c5dffd0a624eb4af2543a67ba656001600160a01b038316145b80611bdd575073c9bdd0d3b80cc6efe79a82d850f44ec9b55387ae6001600160a01b038316145b80611c04575073e6c30756136e07eb5268c3232efbfbe645c1ba5a6001600160a01b038316145b80611c2b5750731d596d28a7923a22aa013b0e7082bba23daa656b6001600160a01b038316145b15611c855760015b604051908082528060200260200182016040528015611c5c578160200160208202803683370190505b509050600081600081518110611c7457611c7461248b565b602002602001018181525050919050565b6001600160a01b03821673418749e294cabce5a714efccc22a8aade6f9db5703611cb0576001611c33565b72c27fc71b159a346e179b4a1608a0865e8a74706001600160a01b0383161480611cf6575073d09a57127bc40d680be7cb061c2a6629fe71abef6001600160a01b038316145b15611d51576040805160028082526060820183529091602083019080368337019050509050600081600081518110611d3057611d3061248b565b602002602001018181525050600181600181518110611c7457611c7461248b565b73b26b42dd5771689d0a7faeea32825ff9710b9c116001600160a01b0383161480611d98575073759f097f3153f5d62ff1c2d82ba78b6350f223e36001600160a01b038316145b80611dbf57507389d7c618a4eef3065da8ad684859a547548e61696001600160a01b038316145b15611e5d5760408051600480825260a082019092529060208201608080368337019050509050600081600081518110611dfa57611dfa61248b565b602002602001018181525050600181600181518110611e1b57611e1b61248b565b602002602001018181525050600381600281518110611e3c57611e3c61248b565b602002602001018181525050600481600381518110611c7457611c7461248b565b816001600160a01b031663c78bed866040518163ffffffff1660e01b8152600401600060405180830381865afa158015611e9b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105b691908101906128d0565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f279190612587565b6001600160a01b0316630c0450127fd6aca1be9729c13d677335161321649cccae6a591554772516700f986f942eaa6040518263ffffffff1660e01b8152600401610d7391906123bf565b611fc88363a9059cbb60e01b8484604051602401611f91929190612503565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261212b565b505050565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa15801561200d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120319190612587565b6001600160a01b0316630c0450127f1cd0c2e09edd1d00946872d4dfa90e139030cf08dd8b201be0c3e01ae1534c576040518263ffffffff1660e01b8152600401610d7391906123bf565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120e09190612587565b6001600160a01b0316630c0450127f6fdcaa094af96c56039c701c590da2284d9e0c364f2dd8ee7d43419f0eefd2936040518263ffffffff1660e01b8152600401610d7391906123bf565b6000612180826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166121fd9092919063ffffffff16565b805190915015611fc8578080602001905181019061219e919061286b565b611fc85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610535565b60606113da848460008585843b6122565760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610535565b600080866001600160a01b03168587604051612272919061299a565b60006040518083038185875af1925050503d80600081146122af576040519150601f19603f3d011682016040523d82523d6000602084013e6122b4565b606091505b50915091506122c48282866122cf565b979650505050505050565b606083156122de575081610b95565b8251156122ee5782518084602001fd5b8160405162461bcd60e51b815260040161053591906129b6565b6001600160a01b038116811461231d57600080fd5b50565b60008060008060006080868803121561233857600080fd5b853561234381612308565b9450602086013561235381612308565b935060408601359250606086013567ffffffffffffffff8082111561237757600080fd5b818801915088601f83011261238b57600080fd5b81358181111561239a57600080fd5b8960208285010111156123ac57600080fd5b9699959850939650602001949392505050565b6001600160e01b031991909116815260200190565b6000602082840312156123e657600080fd5b5035919050565b6000806040838503121561240057600080fd5b823561240b81612308565b946020939093013593505050565b60008060006060848603121561242e57600080fd5b833561243981612308565b9250602084013561244981612308565b929592945050506040919091013590565b60006020828403121561246c57600080fd5b8135610b9581612308565b6001600160a01b0391909116815260200190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b6001600160a01b039384168152919092166020820152604081019190915260600190565b60208101600283106124fd57634e487b7160e01b600052602160045260246000fd5b91905290565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052601160045260246000fd5b808201808211156105b6576105b661251c565b818103818111156105b6576105b661251c565b634e487b7160e01b600052603160045260246000fd5b6000600182016125805761258061251c565b5060010190565b60006020828403121561259957600080fd5b8151610b9581612308565b6000826125c157634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052604160045260246000fd5b60405160e0810167ffffffffffffffff811182821017156125ff576125ff6125c6565b60405290565b60405160c0810167ffffffffffffffff811182821017156125ff576125ff6125c6565b60405160a0810167ffffffffffffffff811182821017156125ff576125ff6125c6565b604051601f8201601f1916810167ffffffffffffffff81118282101715612674576126746125c6565b604052919050565b80516002811061268b57600080fd5b919050565b60008183036101808112156126a457600080fd5b6126ac6125dc565b8351815260c0601f19830112156126c257600080fd5b6126ca612605565b91506020840151825260408401516020830152606084015160408301526080840151606083015260a0840151608083015260c084015160a083015281602082015260e08401516040820152610100840151606082015261272d610120850161267c565b608082015261014084015160a08201526101609093015160c0840152509092915050565b600060a0828403121561276357600080fd5b61276b612628565b825161277681612308565b80825250602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b600060a082840312156127be57600080fd5b6127c6612628565b82518152602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b60006020828403121561280e57600080fd5b5051919050565b6001600160a01b038316815260406020808301829052835191830182905260009184820191906060850190845b8181101561285e57845183529383019391830191600101612842565b5090979650505050505050565b60006020828403121561287d57600080fd5b81518015158114610b9557600080fd5b600080604083850312156128a057600080fd5b505080516020909101519092909150565b60008160001904831182151516156128cb576128cb61251c565b500290565b600060208083850312156128e357600080fd5b825167ffffffffffffffff808211156128fb57600080fd5b818501915085601f83011261290f57600080fd5b815181811115612921576129216125c6565b8060051b915061293284830161264b565b818152918301840191848101908884111561294c57600080fd5b938501935b8385101561296a57845182529385019390850190612951565b98975050505050505050565b60005b83811015612991578181015183820152602001612979565b50506000910152565b600082516129ac818460208701612976565b9190910192915050565b60208152600082518060208401526129d5816040850160208701612976565b601f01601f1916919091016040019291505056fe9e45de9588e74df19c5f35dd4ddf9c698e7a3e36b4004fd44cb75621507d7092a26469706673582212202c288a699522a8ece2b9cf6db116af017d3c938cd5dea7f99604701c52253f0c64736f6c63430008100033

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

000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c83

-----Decoded View---------------
Arg [0] : _context (address): 0xd16BC944Bf20c86c4ED47Ce1a330a18538674C83

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c83


Block Transaction Difficulty 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
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.