ETH Price: $3,419.02 (-0.07%)

Contract

0xbd431E6ACF024c216b7Ae0a1dC67D448ec628ec5
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60a06040168919522023-03-23 18:15:23614 days ago1679595323IN
 Create: CapitalLedger
0 ETH0.1134480946.15292605

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 49 : 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 49 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

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

File 5 of 49 : 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 49 : 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 49 : 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 49 : 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 49 : 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 49 : 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 49 : 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 49 : 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 49 : 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 49 : 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 49 : 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 49 : 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 49 : 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 49 : 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 49 : ICallableLoan.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

import {ILoan} from "./ILoan.sol";
import {ISchedule} from "./ISchedule.sol";
import {IGoldfinchConfig} from "./IGoldfinchConfig.sol";

/// A LoanPhase represents a period of time during which certain callable loan actions are prohibited.
/// @param Prefunding Starts when a loan is created and ends at fundableAt.
/// In Prefunding, all actions are prohibited or ineffectual.
/// @param Funding Starts at the fundableAt timestamp and ends at the first borrower drawdown.
/// In Funding, lenders can deposit principal to mint a pool token and they can withdraw their deposited principal.
/// @param DrawdownPeriod Starts when the first borrower drawdown occurs and
/// ends after ConfigHelper.DrawdownPeriodInSeconds elapses.
/// In DrawdownPeriod, the borrower can drawdown principal as many times as they want.
/// Lenders cannot withdraw their principal, deposit new principal, or submit call requests.
/// @param InProgress Starts after ConfigHelper.DrawdownPeriodInSeconds elapses and never ends.
/// In InProgress, all post-funding & drawdown actions are allowed (not withdraw, deposit, or drawdown).
/// When a loan is fully paid back, we do not update the loan state, but most of these actions will
/// be prohibited or ineffectual.
enum LoanPhase {
  Prefunding,
  Funding,
  DrawdownPeriod,
  InProgress
}

/// @dev A CallableLoan is a loan which allows the lender to call the borrower's principal.
///     The lender can call the borrower's principal at any time, and the borrower must pay back the principal
///     by the end of the call request period.
/// @dev The ICallableLoanErrors interface contains all errors due to Solidity version compatibility with custom errors.
interface ICallableLoan is ILoan {
  /*================================================================================
  Structs
  ================================================================================*/
  /// @param principalDeposited The amount of principal deposited towards this call request period.
  /// @param principalPaid The amount of principal which has already been paid back towards this call request period.
  ///                      There are 3 ways principal paid can enter a CallRequestPeriod.
  ///                      1. Converted from principalReserved after a call request period becomes due.
  ///                      2. Moved from uncalled tranche as the result of a call request.
  ///                      3. Paid directly when a CallRequestPeriod is past due and has a remaining balance.
  /// @param principalReserved The amount of principal reserved for this call request period.
  ///                          Payments to a not-yet-due CallRequestPeriod are applied to principalReserved.
  /// @param interestPaid The amount of interest paid towards this call request period.
  struct CallRequestPeriod {
    uint256 principalDeposited;
    uint256 principalPaid;
    uint256 principalReserved;
    uint256 interestPaid;
  }

  /// @param principalDeposited The amount of uncalled, deposited principal.
  /// @param principalPaid The amount of principal which has already been paid back.
  ///                      There are two ways uncalled principal can be paid.
  ///                      1. Remainder after drawdowns.
  ///                      2. Conversion from principalReserved after a call request period becomes due.
  ///                         All call requested principal outstanding must already be paid
  ///                         (or have principal reserved) before uncalled principal can be paid.
  ///                      3. Paid directly after term end time.
  /// @param principalReserved The amount of principal reserved for uncalled tranche.
  ///                          principalReserved is greedily moved to call request periods (as much as can fill)
  ///                          when a call request is submitted.
  /// @param interestPaid The amount of interest paid towards uncalled capital.
  struct UncalledCapitalInfo {
    uint256 principalDeposited;
    uint256 principalPaid;
    uint256 principalReserved;
    uint256 interestPaid;
  }

  /*================================================================================
  Functions
  ================================================================================*/
  /// @notice Initialize the pool. Can only be called once, and should be called in the same transaction as
  ///   contract creation to avoid initialization front-running
  /// @param _config address of GoldfinchConfig
  /// @param _borrower address of borrower, a non-transferrable role for performing privileged actions like
  ///   drawdown
  /// @param _numLockupPeriods the number of periods at the tail end of a principal period during which call requests
  ///   are not allowed
  /// @param _interestApr interest rate for the loan
  /// @param _lateFeeApr late fee interest rate for the loan, which kicks in `LatenessGracePeriodInDays` days after a
  ///   payment becomes late
  /// @param _fundableAt earliest time at which the first slice can be funded
  function initialize(
    IGoldfinchConfig _config,
    address _borrower,
    uint256 _limit,
    uint256 _interestApr,
    uint256 _numLockupPeriods,
    ISchedule _schedule,
    uint256 _lateFeeApr,
    uint256 _fundableAt,
    uint256[] calldata _allowedUIDTypes
  ) external;

  /// @notice Submits a call request for the specified pool token and amount
  ///         Mints a new, called pool token of the called amount.
  ///         Splits off any uncalled amount as a new uncalled pool token.
  /// @param amountToCall The amount of the pool token that should be called.
  /// @param poolTokenId The id of the pool token that should be called.
  /// @return callRequestedTokenId  Token id of the call requested token.
  /// @return remainingTokenId Token id of the remaining token.
  function submitCall(
    uint256 amountToCall,
    uint256 poolTokenId
  ) external returns (uint256, uint256);

  function schedule() external view returns (ISchedule);

  function nextDueTimeAt(uint256 timestamp) external view returns (uint256);

  function nextPrincipalDueTime() external view returns (uint256);

  function numLockupPeriods() external view returns (uint256);

  function inLockupPeriod() external view returns (bool);

  function getUncalledCapitalInfo() external view returns (UncalledCapitalInfo memory);

  function getCallRequestPeriod(
    uint256 callRequestPeriodIndex
  ) external view returns (CallRequestPeriod memory);

  function uncalledCapitalTrancheIndex() external view returns (uint256);

  function availableToCall(uint256 tokenId) external view returns (uint256);

  /// @notice Returns the current phase of the loan.
  ///         See documentation on LoanPhase enum.
  function loanPhase() external view returns (LoanPhase);

  /// @notice Returns the current balance of the loan which will be used for
  ///         interest calculations.
  ///         Settles any principal reserved if a call request period has
  ///         ended since the last checkpoint
  ///         Excludes principal reserved for future call request periods
  function interestBearingBalance() external view returns (uint256);

  /// @notice Returns a naive estimate of the interest owed at the timestamp.
  ///         Omits any late fees, and assumes no future payments.
  function estimateOwedInterestAt(uint256 timestamp) external view returns (uint256);

  /// @notice Returns a naive estimate of the interest owed at the timestamp.
  ///         Omits any late fees, and assumes no future payments.
  function estimateOwedInterestAt(
    uint256 balance,
    uint256 timestamp
  ) external view returns (uint256);

  /*================================================================================
  Events
  ================================================================================*/
  event CallRequestSubmitted(
    uint256 indexed originalTokenId,
    uint256 indexed callRequestedTokenId,
    uint256 indexed remainingTokenId,
    uint256 callAmount
  );
  event DepositsLocked(address indexed loan);
}

File 20 of 49 : 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 21 of 49 : ICreditLine.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

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

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

interface ICreditLine {
  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);

  function borrower() external view returns (address);

  function currentLimit() external view returns (uint256);

  function limit() external view returns (uint256);

  function maxLimit() external view returns (uint256);

  function interestApr() external view returns (uint256);

  function lateFeeApr() external view returns (uint256);

  function isLate() external view returns (bool);

  function withinPrincipalGracePeriod() external view returns (bool);

  /// @notice Cumulative interest accrued up to now
  function totalInterestAccrued() external view returns (uint256);

  /// @notice Cumulative interest accrued up to `timestamp`
  function totalInterestAccruedAt(uint256 timestamp) external view returns (uint256);

  /// @notice Cumulative interest paid back up to now
  function totalInterestPaid() external view returns (uint256);

  /// @notice Cumulative interest owed up to now
  function totalInterestOwed() external view returns (uint256);

  /// @notice Cumulative interest owed up to `timestamp`
  function totalInterestOwedAt(uint256 timestamp) external view returns (uint256);

  /// @notice Interest that would be owed at `timestamp`
  function interestOwedAt(uint256 timestamp) external view returns (uint256);

  /// @notice Interest accrued in the current payment period up to now. Converted to
  ///   owed interest once we cross into the next payment period. Is 0 if the
  ///   current time is after loan maturity (all interest accrued immediately becomes
  ///   interest owed).
  function interestAccrued() external view returns (uint256);

  /// @notice Interest accrued in the current payment period for `timestamp`. Coverted to
  ///   owed interest once we cross into the payment period after `timestamp`. Is 0
  ///   if `timestamp` is after loan maturity (all interest accrued immediately becomes
  ///   interest owed).
  function interestAccruedAt(uint256 timestamp) external view returns (uint256);

  /// @notice Principal owed up to `timestamp`
  function principalOwedAt(uint256 timestamp) external view returns (uint256);

  /// @notice Returns the total amount of principal thats been paid
  function totalPrincipalPaid() external view returns (uint256);

  /// @notice Cumulative principal owed at timestamp
  function totalPrincipalOwedAt(uint256 timestamp) external view returns (uint256);

  /// @notice Cumulative principal owed at current timestamp
  function totalPrincipalOwed() external view returns (uint256);

  function setLimit(uint256 newAmount) external;

  function setMaxLimit(uint256 newAmount) external;

  /// @notice Time of first drawdown
  function termStartTime() external view returns (uint256);

  /// @notice Process a bulk payment, allocating the payment amount based on the payment waterfall
  function pay(uint paymentAmount) external returns (ILoan.PaymentAllocation memory);

  /**
   * Process a payment according to the waterfall described in `Accountant.allocatePayment`
   * @param principalPayment principal payment amount
   * @param interestPayment interest payment amount
   * @return payment allocation
   */
  function pay(
    uint256 principalPayment,
    uint256 interestPayment
  ) external returns (ILoan.PaymentAllocation memory);

  /// @notice Drawdown on the line
  /// @param amount amount to drawdown. Cannot exceed the line's limit
  function drawdown(uint256 amount) external;
}

File 22 of 49 : 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 23 of 49 : 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 24 of 49 : 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 25 of 49 : IGoldfinchConfig.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

interface IGoldfinchConfig {
  /**
   * @dev Adds a user to go-list
   * @param _member address to add to go-list
   */
  function addToGoList(address _member) external;

  /**
   * @dev removes a user from go-list
   * @param _member address to remove from go-list
   */
  function removeFromGoList(address _member) external;

  /**
   * @dev adds many users to go-list at once
   * @param _members addresses to ad to go-list
   */
  function bulkAddToGoList(address[] calldata _members) external;

  /**
   * @dev removes many users from go-list at once
   * @param _members addresses to remove from go-list
   */
  function bulkRemoveFromGoList(address[] calldata _members) external;

  function getNumber(uint256 index) external view returns (uint256);

  /*
    Using custom getters in case we want to change underlying implementation later,
    or add checks or validations later on.
  */
  function getAddress(uint256 index) external view returns (address);

  function setAddress(uint256 index, address newAddress) external;

  function setNumber(uint256 index, uint256 newNumber) external;
}

File 26 of 49 : ILoan.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;
import {ISchedule} from "./ISchedule.sol";
import {ICreditLine} from "./ICreditLine.sol";

enum LoanType {
  TranchedPool,
  CallableLoan
}

interface ILoan {
  /// @notice getLoanType was added to support the new callable loan type.
  ///         It is not supported in older versions of ILoan (e.g. legacy TranchedPools)
  function getLoanType() external view returns (LoanType);

  /// @notice Pool's credit line, responsible for managing the loan's accounting variables
  function creditLine() external view returns (ICreditLine);

  /// @notice Time when the pool was initialized. Zero if uninitialized
  function createdAt() external view returns (uint256);

  /// @notice Pay down interest + principal. Excess payments are refunded to the caller
  /// @param amount USDC amount to pay
  /// @return PaymentAllocation info on how the payment was allocated
  /// @dev {this} must be approved by msg.sender to transfer {amount} of USDC
  function pay(uint256 amount) external returns (PaymentAllocation memory);

  /// @notice Compute interest and principal owed on the current balance at a future timestamp
  /// @param timestamp time to calculate up to
  /// @return interestOwed amount of obligated interest owed at `timestamp`
  /// @return interestAccrued amount of accrued interest (not yet owed) that can be paid at `timestamp`
  /// @return principalOwed amount of principal owed at `timestamp`
  function getAmountsOwed(
    uint256 timestamp
  ) external view returns (uint256 interestOwed, uint256 interestAccrued, uint256 principalOwed);

  function getAllowedUIDTypes() external view returns (uint256[] memory);

  /// @notice Drawdown the loan. The credit line's balance should increase by the amount drawn down.
  ///   Junior capital must be locked before this function can be called. If senior capital isn't locked
  ///   then this function will lock it for you (convenience to avoid calling lockPool() separately).
  ///   This function should revert if the amount requested exceeds the the current slice's currentLimit
  ///   This function should revert if the caller is not the borrower.
  /// @param amount USDC to drawdown. This amount is transferred to the caller
  function drawdown(uint256 amount) external;

  /// @notice Update `fundableAt` to a new timestamp. Only the borrower can call this.
  function setFundableAt(uint256 newFundableAt) external;

  /// @notice Supply capital to this pool. Caller can't deposit to the junior tranche if the junior pool is locked.
  ///   Caller can't deposit to a senior tranche if the pool is locked. Caller can't deposit if they are missing the
  ///   required UID NFT.
  /// @param tranche id of tranche to supply capital to. Id must correspond to a tranche in the current slice.
  /// @param amount amount of capital to supply
  /// @return tokenId NFT representing your position in this pool
  function deposit(uint256 tranche, uint256 amount) external returns (uint256 tokenId);

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

  /// @notice Query the max amount available to withdraw for tokenId's position
  /// @param tokenId position to query max amount withdrawable for
  /// @return interestRedeemable total interest withdrawable on the position
  /// @return principalRedeemable total principal redeemable on the position
  function availableToWithdraw(
    uint256 tokenId
  ) external view returns (uint256 interestRedeemable, uint256 principalRedeemable);

  /// @notice Withdraw an already deposited amount if the funds are available. Caller must be the owner or
  ///   approved by the owner on tokenId. Amount withdrawn is sent to the caller.
  /// @param tokenId the NFT representing the position
  /// @param amount amount to withdraw (must be <= interest+principal available to withdraw)
  /// @return interestWithdrawn interest withdrawn
  /// @return principalWithdrawn principal withdrawn
  function withdraw(
    uint256 tokenId,
    uint256 amount
  ) external returns (uint256 interestWithdrawn, uint256 principalWithdrawn);

  /// @notice Similar to withdraw but withdraw the max interest and principal available for `tokenId`
  function withdrawMax(
    uint256 tokenId
  ) external returns (uint256 interestWithdrawn, uint256 principalWithdrawn);

  /// @notice Withdraw from multiple tokens
  /// @param tokenIds NFT positions to withdraw. Caller must be an owner or approved on all tokens in the array
  /// @param amounts amounts to withdraw from positions such that amounts[i] is withdrawn from position tokenIds[i]
  function withdrawMultiple(uint256[] calldata tokenIds, uint256[] calldata amounts) external;

  /// @notice Result of applying a payment to a v2 pool
  /// @param owedInterestPayment payment portion of interest owed
  /// @param accruedInterestPayment payment portion of accrued (but not yet owed) interest
  /// @param principalPayment payment portion on principal owed
  /// @param additionalBalancePayment payment portion on any balance that is currently owed
  /// @param paymentRemaining payment amount leftover
  struct PaymentAllocation {
    uint256 owedInterestPayment;
    uint256 accruedInterestPayment;
    uint256 principalPayment;
    uint256 additionalBalancePayment;
    uint256 paymentRemaining;
  }
  /// @notice Event emitted on payment
  /// @param payer address that made the payment
  /// @param pool pool to which the payment was made
  /// @param interest amount of payment allocated to interest (obligated + additional)
  /// @param principal amount of payment allocated to principal owed and remaining balance
  /// @param remaining any excess payment amount that wasn't allocated to a debt owed
  /// @param reserve of payment that went to the protocol reserve
  event PaymentApplied(
    address indexed payer,
    address indexed pool,
    uint256 interest,
    uint256 principal,
    uint256 remaining,
    uint256 reserve
  );
  event DepositMade(
    address indexed owner,
    uint256 indexed tranche,
    uint256 indexed tokenId,
    uint256 amount
  );

  /// @notice While owner is the label of the first argument, it is actually the sender of the transaction.
  event WithdrawalMade(
    address indexed owner,
    uint256 indexed tranche,
    uint256 indexed tokenId,
    uint256 interestWithdrawn,
    uint256 principalWithdrawn
  );
  event ReserveFundsCollected(address indexed from, uint256 amount);
  event DrawdownMade(address indexed borrower, uint256 amount);
  event DrawdownsPaused(address indexed pool);
  event DrawdownsUnpaused(address indexed pool);
  event EmergencyShutdown(address indexed pool);
}

File 27 of 49 : 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 28 of 49 : 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 29 of 49 : 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 30 of 49 : 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 31 of 49 : 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 32 of 49 : 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 33 of 49 : 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 34 of 49 : ISchedule.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

interface ISchedule {
  function periodsPerPrincipalPeriod() external view returns (uint256);

  function periodsInTerm() external view returns (uint256);

  function periodsPerInterestPeriod() external view returns (uint256);

  function gracePrincipalPeriods() external view returns (uint256);

  /**
   * @notice Returns the period that timestamp resides in
   */
  function periodAt(uint256 startTime, uint256 timestamp) external view returns (uint256);

  /**
   * @notice Returns the principal period that timestamp resides in
   */
  function principalPeriodAt(uint256 startTime, uint256 timestamp) external view returns (uint256);

  /**
   * @notice Returns the interest period that timestamp resides in
   */
  function interestPeriodAt(uint256 startTime, uint256 timestamp) external view returns (uint256);

  /**
   * @notice Returns true if the given timestamp resides in a principal grace period
   */
  function withinPrincipalGracePeriodAt(
    uint256 startTime,
    uint256 timestamp
  ) external view returns (bool);

  /**
   * Returns the next timestamp where either principal or interest will come due following `timestamp`
   */
  function nextDueTimeAt(uint256 startTime, uint256 timestamp) external view returns (uint256);

  /**
   * @notice Returns the previous timestamp where either principal or timestamp came due
   */
  function previousDueTimeAt(uint256 startTime, uint256 timestamp) external view returns (uint256);

  /**
   * @notice Returns the previous timestamp where new interest came due
   */
  function previousInterestDueTimeAt(
    uint256 startTime,
    uint256 timestamp
  ) external view returns (uint256);

  /**
   * @notice Returns the previous timestamp where new principal came due
   */
  function previousPrincipalDueTimeAt(
    uint256 startTime,
    uint256 timestamp
  ) external view returns (uint256);

  /**
   * @notice Returns the total number of principal periods
   */
  function totalPrincipalPeriods() external view returns (uint256);

  /**
   * @notice Returns the total number of interest periods
   */
  function totalInterestPeriods() external view returns (uint256);

  /**
   * @notice Returns the timestamp that the term will end
   */
  function termEndTime(uint256 startTime) external view returns (uint256);

  /**
   * @notice Returns the timestamp that the term began
   */
  function termStartTime(uint256 startTime) external view returns (uint256);

  /**
   * @notice Returns the next time principal will come due, or the termEndTime if there are no more due times
   */
  function nextPrincipalDueTimeAt(
    uint256 startTime,
    uint256 timestamp
  ) external view returns (uint256);

  /**
   * @notice Returns the next time interest will come due, or the termEndTime if there are no more due times
   */
  function nextInterestDueTimeAt(
    uint256 startTime,
    uint256 timestamp
  ) external view returns (uint256);

  /**
   * @notice Returns the end time of the given period.
   */
  function periodEndTime(uint256 startTime, uint256 period) external view returns (uint256);
}

File 35 of 49 : 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 36 of 49 : 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 37 of 49 : 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 38 of 49 : ITranchedPool.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;
import {ISchedule} from "./ISchedule.sol";
import {ILoan} from "./ILoan.sol";
import {ICreditLine} from "./ICreditLine.sol";

interface ITranchedPool is ILoan {
  struct TrancheInfo {
    uint256 id;
    uint256 principalDeposited;
    uint256 principalSharePrice;
    uint256 interestSharePrice;
    uint256 lockedUntil;
  }
  struct PoolSlice {
    TrancheInfo seniorTranche;
    TrancheInfo juniorTranche;
    uint256 totalInterestAccrued;
    uint256 principalDeployed;
  }
  enum Tranches {
    Reserved,
    Senior,
    Junior
  }

  /// @notice Initialize the pool. Can only be called once, and should be called in the same transaction as
  ///   contract creation to avoid initialization front-running
  /// @param _config address of GoldfinchConfig
  /// @param _borrower address of borrower, a non-transferrable role for performing privileged actions like
  ///   drawdown
  /// @param _juniorFeePercent percent (whole number) of senior interest that gets re-allocated to the junior tranche.
  ///   valid range is [0, 100]
  /// @param _limit the max USDC amount that can be drawn down across all pool slices
  /// @param _interestApr interest rate for the loan
  /// @param _lateFeeApr late fee interest rate for the loan, which kicks in `LatenessGracePeriodInDays` days after a
  ///   payment becomes late
  /// @param _fundableAt earliest time at which the first slice can be funded
  function initialize(
    address _config,
    address _borrower,
    uint256 _juniorFeePercent,
    uint256 _limit,
    uint256 _interestApr,
    ISchedule _schedule,
    uint256 _lateFeeApr,
    uint256 _fundableAt,
    uint256[] calldata _allowedUIDTypes
  ) external;

  /// @notice Pay down the credit line, separating the principal and interest payments. You must pay back all interest
  ///   before paying back principal. Excess payments are refunded to the caller
  /// @param principalPayment USDC amount to pay down principal
  /// @param interestPayment USDC amount to pay down interest
  /// @return PaymentAllocation info on how the payment was allocated
  /// @dev {this} must be approved by msg.sender to transfer {principalPayment} + {interestPayment} of USDC
  function pay(
    uint256 principalPayment,
    uint256 interestPayment
  ) external returns (PaymentAllocation memory);

  /// @notice TrancheInfo for tranche with id `trancheId`. The senior tranche of slice i has id 2*(i-1)+1. The
  ///   junior tranche of slice i has id 2*i. Slice indices start at 1.
  /// @param trancheId id of tranche. Valid ids are in the range [1, 2*numSlices]
  function getTranche(uint256 trancheId) external view returns (ITranchedPool.TrancheInfo memory);

  /// @notice Get a slice by index
  /// @param index of slice. Valid indices are on the interval [0, numSlices - 1]
  function poolSlices(uint256 index) external view returns (ITranchedPool.PoolSlice memory);

  /// @notice Lock the junior capital in the junior tranche of the current slice. The capital is locked for
  ///   `DrawdownPeriodInSeconds` seconds and gives the senior pool time to decide how much to invest (ensure
  ///   leverage ratio cannot change for the period). During this period the borrower has the option to lock
  ///   the senior capital by calling `lockPool()`. Backers may withdraw their junior capital if the the senior
  ///   tranche has not been locked and the drawdown period has ended. Only the borrower can call this function.
  function lockJuniorCapital() external;

  /// @notice Lock the senior capital in the senior tranche of the current slice and reset the lock period of
  ///   the junior capital to match the senior capital lock period. During this period the borrower has the
  ///   option to draw down the pool. Beyond the drawdown period any unused capital is available to withdraw by
  ///   all depositors.
  function lockPool() external;

  /// @notice Initialize the next slice for the pool. Enables backers and the senior pool to provide additional
  ///   capital to the borrower.
  /// @param _fundableAt time at which the new slice (now the current slice) becomes fundable
  function initializeNextSlice(uint256 _fundableAt) external;

  /// @notice Query the total capital supplied to the pool's junior tranches
  function totalJuniorDeposits() external view returns (uint256);

  function assess() external;

  /// @notice Get the current number of slices for this pool
  /// @return numSlices total current slice count
  function numSlices() external view returns (uint256);

  // Note: This has to exactly match the event in the TranchingLogic library for events to be emitted
  // correctly
  event SharePriceUpdated(
    address indexed pool,
    uint256 indexed tranche,
    uint256 principalSharePrice,
    int256 principalDelta,
    uint256 interestSharePrice,
    int256 interestDelta
  );
  event CreditLineMigrated(ICreditLine indexed oldCreditLine, ICreditLine indexed newCreditLine);
  event TrancheLocked(address indexed pool, uint256 trancheId, uint256 lockedUntil);
  event SliceCreated(address indexed pool, uint256 sliceId);
}

File 39 of 49 : IERC165.sol
// SPDX-License-Identifier: MIT

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 40 of 49 : IERC721.sol
// SPDX-License-Identifier: MIT

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 41 of 49 : 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 42 of 49 : 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 43 of 49 : 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 44 of 49 : 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 45 of 49 : 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 46 of 49 : 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 47 of 49 : 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 48 of 49 : 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";
import {ICallableLoan} from "../../../../interfaces/ICallableLoan.sol";
import {ILoan, LoanType} from "../../../../interfaces/ILoan.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);

    // Legacy TranchedPools do not support ILoan#getLoanType
    LoanType loanType = LoanType.TranchedPool;

    try ILoan(tokenInfo.pool).getLoanType() returns (LoanType _loanType) {
      loanType = _loanType;
    } catch {}

    if (loanType == LoanType.TranchedPool) {
      return ITranchedPool(tokenInfo.pool).getTranche(tokenInfo.tranche).lockedUntil != 0;
    } else if (loanType == LoanType.CallableLoan) {
      return ICallableLoan(tokenInfo.pool).uncalledCapitalTrancheIndex() == tokenInfo.tranche;
    }
  }

  /**
   * @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);
    ILoan loan = ILoan(tokenInfo.pool);

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

    (uint256 interestWithdrawn, uint256 principalWithdrawn) = loan.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 ILoan(poolAddress).getAllowedUIDTypes();
  }
}

File 49 of 49 : 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"}]

60a06040523480156200001157600080fd5b5060405162002c5538038062002c55833981016040819052620000349162000046565b6001600160a01b031660805262000078565b6000602082840312156200005957600080fd5b81516001600160a01b03811681146200007157600080fd5b9392505050565b608051612b82620000d36000396000818161035f015281816105de01528181610639015281816106880152818161070e015281816108be0152818161093601528181610eb501528181610f58015261100d0152612b826000f3fe608060405234801561001057600080fd5b50600436106100bf5760003560e01c80634f6ccce71161007c5780634f6ccce71461016e5780636352211e1461018157806370a08231146101b757806399fbab88146101e0578063c4664c6314610262578063ddc632621461028e578063e305f306146102a157600080fd5b8063150b7a02146100c457806318160ddd146100f95780632e1a7d4d1461010b5780632f745c5914610120578063331ded1a14610133578063427d743214610146575b600080fd5b6100e36100d236600461243d565b630a85bd0160e11b95945050505050565b6040516100f091906124dc565b60405180910390f35b6003545b6040519081526020016100f0565b61011e6101193660046124f1565b6102c1565b005b6100fd61012e36600461250a565b610546565b6100fd610141366004612536565b6105bc565b610159610154366004612577565b6107e8565b604080519283526020830191909152016100f0565b6100fd61017c3660046124f1565b610814565b6101aa61018f3660046124f1565b6000908152602081905260409020546001600160a01b031690565b6040516100f09190612594565b6100fd6101c5366004612577565b6001600160a01b031660009081526001602052604090205490565b6102296101ee3660046124f1565b600060208190529081526040902080546001820154600283015460038401546004909401546001600160a01b03938416949293909116919085565b604080516001600160a01b039687168152602081019590955292909416918301919091526060820152608081019190915260a0016100f0565b6101aa6102703660046124f1565b6000908152602081905260409020600201546001600160a01b031690565b61011e61029c3660046124f1565b610849565b6100fd6102af3660046124f1565b60009081526004602052604090205490565b600080516020612b2d8339815191526102da81336109a8565b600082815260208181526040808320815160a08101835281546001600160a01b03808216835260018401805484880152600285018054928316968501968752600386018054606087015260048701805460808801528c8b52988a90526001600160a01b03199485169096559088905591169055908490559183905551909190610384907f000000000000000000000000000000000000000000000000000000000000000090610a0b565b6060830151608084015184516001600160a01b031660009081526002602052604090209293506103b5929190610a44565b81516001600160a01b031660009081526001602090815260408220908401519091906103e2908390610acd565b915050801561042a57836020015160008084876020015181548110610409576104096125a8565b90600052602060002001548152602001908152602001600020600101819055505b600183600181111561043e5761043e6125be565b0361051a57600086815260046020819052604080832080549390558681015187519151632142170760e11b81526001600160a01b03909116926342842e0e9261048b9230928791016125d4565b600060405180830381600087803b1580156104a557600080fd5b505af11580156104b9573d6000803e3d6000fd5b5050865160408089015160808a015182518d81526001600160a01b0392831660208201529283015290911692507f50065562d2b38b7cc51ee877211c37a54ff469d129d1a67bfeb97e0c2994a316915060600160405180910390a25061053e565b826040516308a8fee960e31b815260040161053591906125f8565b60405180910390fd5b505050505050565b6001600160a01b038216600090815260016020526040812054821061057e57604051638f832b9b60e01b815260040160405180910390fd5b6001600160a01b03831660009081526001602052604090208054839081106105a8576105a86125a8565b906000526020600020015490505b92915050565b6000600080516020612b2d8339815191526105d781336109a8565b60016106037f000000000000000000000000000000000000000000000000000000000000000086610a0b565b6001811115610614576106146125be565b1461063457836040516337bce3c560e11b81526004016105359190612594565b61065f7f00000000000000000000000000000000000000000000000000000000000000008585610b56565b610680578383604051634eb9917160e11b8152600401610535929190612620565b8360006106ae7f00000000000000000000000000000000000000000000000000000000000000008387610b9c565b905060006106bd888884610beb565b604080516020808201835289825260008481526004825283812092519092556001600160a01b038c16825260029052209091506106fa9083610cb0565b826001600160a01b03166342842e0e61073b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610cd6565b30896040518463ffffffff1660e01b815260040161075b939291906125d4565b600060405180830381600087803b15801561077557600080fd5b505af1158015610789573d6000803e3d6000fd5b505060408051848152602081018a90529081018590526001600160a01b03808b1693508b1691507fb1c56a0ea823a5b59843a1c0223e87b70affc07e96fc346165322958cdef13949060600160405180910390a3979650505050505050565b6001600160a01b0381166000908152600260205260408120819061080b90610db4565b91509150915091565b600061081f60035490565b821061083e57604051633f7ee38f60e11b815260040160405180910390fd5b6105b682600161264f565b600080516020612b2d83398151915261086281336109a8565b600082815260208181526040808320815160a08101835281546001600160a01b0390811682526001830154948201949094526002820154909316918301829052600381015460608401526004015460808301529091906108e3907f000000000000000000000000000000000000000000000000000000000000000090610a0b565b905060018160018111156108f9576108f96125be565b1461091957806040516308a8fee960e31b815260040161053591906125f8565b815160408084015160008781526004602052919091205461095d927f0000000000000000000000000000000000000000000000000000000000000000929091610e05565b837f54c43c33795a11f3152d46be48143a092092361e37f765f6f9da0251aabf1f9683604001516040516109919190612594565b60405180910390a26109a284610e59565b50505050565b6001600160a01b0381166109cf5760405163d92e233d60e01b815260040160405180910390fd5b6109d98282610fff565b610a075760405163889a56bb60e01b81523060048201526001600160a01b0382166024820152604401610535565b5050565b6000610a17838361110a565b15610a24575060016105b6565b610a2e838361113a565b15610a3b575060016105b6565b50600092915050565b6000610a4f8261114e565b9050610a5961115d565b811115610a7c57604051632029102760e21b815260048101829052602401610535565b610a858461116d565b82846000016000828254610a999190612662565b90915550610aa7905061115d565b8110156109a25782846001016000828254610ac29190612662565b909155505050505050565b81546000908190610ae090600190612662565b915050818114801590610b2957838281548110610aff57610aff6125a8565b9060005260206000200154848481548110610b1c57610b1c6125a8565b6000918252602090912001555b83805480610b3957610b39612675565b600190038181906000526020600020016000905590559250929050565b6000610b62848461110a565b15610b7857610b718483611199565b9050610b95565b610b82848461113a565b15610b9157610b71848361123a565b5060005b9392505050565b6000610ba8848461113a565b15610bb757610b71848361145e565b610bc1848461110a565b15610bd057610b7184836114ff565b826040516337bce3c560e11b81526004016105359190612594565b6003805460009182610bfc8361268b565b9091555050600380546040805160a0810182526001600160a01b0397881680825260009081526001602081815284832080548286019081529a8c16858701908152606086019a8b52426080870190815288865285845296852095518654908e166001600160a01b03199182161787559b51868501555160028601805491909d169b169a909a17909a559651948201949094559051600490910155838652845493840185559381529390932001819055919050565b610cb98261116d565b80826000016000828254610ccd919061264f565b90915550505050565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3a91906126a4565b6001600160a01b0316630c045012600080516020612b2d8339815191526040518263ffffffff1660e01b8152600401610d7391906124dc565b602060405180830381865afa158015610d90573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b691906126a4565b6040805160608101825282548152600183015460208201526002830154918101829052600091829190610de561115d565b03610dfb57602081015190519094909350915050565b5193849350915050565b610e0f848361113a565b15610e2457610e1f848483611600565b6109a2565b610e2e848361110a565b15610e3e57610e1f848483611877565b816040516337bce3c560e11b81526004016105359190612594565b600081815260208181526040808320815160a08101835281546001600160a01b039081168252600183015494820194909452600282015490931691830182905260038101546060840152600401546080830152909190610eda907f000000000000000000000000000000000000000000000000000000000000000090610a0b565b90506001816001811115610ef057610ef06125be565b14610f1057806040516308a8fee960e31b815260040161053591906125f8565b6060820151608083015183516001600160a01b03166000908152600260205260409020610f3e929091610a44565b6040808301516000858152600460205291822054610f7d917f000000000000000000000000000000000000000000000000000000000000000091610b9c565b600085815260208181526040808320600301849055608087015187516001600160a01b031684526002909252909120919250610fbb9190839061191a565b837f6a28020abc16896e42077f4597b339d5cdf310f437dee250bcfb161a8539b27a846040015183604051610ff1929190612620565b60405180910390a250505050565b6000816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611069573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108d91906126a4565b6001600160a01b0316630c045012856040518263ffffffff1660e01b81526004016110b891906124dc565b602060405180830381865afa1580156110d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f991906126a4565b6001600160a01b0316149392505050565b600061111e836001600160a01b0316611998565b6001600160a01b0316826001600160a01b031614905092915050565b600061111e836001600160a01b0316611a47565b60006105b662093a80836126c1565b60006111684261114e565b905090565b806002015461117a61115d565b116111825750565b8054600182015561119161115d565b600290910155565b6000806111ae846001600160a01b0316611998565b6001600160a01b031663eb02c301846040518263ffffffff1660e01b81526004016111db91815260200190565b61018060405180830381865afa1580156111f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061121d91906127b6565b608001516001811115611232576112326125be565b149392505050565b60008061124f846001600160a01b0316611a47565b6001600160a01b0316638c7a63ae846040518263ffffffff1660e01b815260040161127c91815260200190565b60a060405180830381865afa158015611299573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bd9190612877565b9050600081600001516001600160a01b031663191401196040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801561131f575060408051601f3d908101601f1916820190925261131c918101906128d2565b60015b156113275790505b600081600181111561133b5761133b6125be565b036113c8578151602083015160405163d972e8ad60e01b81526001600160a01b039092169163d972e8ad916113769160040190815260200190565b60a060405180830381865afa158015611393573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b791906128ef565b6080015160001415925050506105b6565b60018160018111156113dc576113dc6125be565b0361145657816020015182600001516001600160a01b0316632ba443d26040518163ffffffff1660e01b8152600401602060405180830381865afa158015611428573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144c919061293f565b14925050506105b6565b505092915050565b600080611473846001600160a01b0316611a47565b6001600160a01b0316638c7a63ae846040518263ffffffff1660e01b81526004016114a091815260200190565b60a060405180830381865afa1580156114bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e19190612877565b9050806060015181604001516114f79190612662565b949350505050565b600080611514846001600160a01b0316611998565b6001600160a01b031663aa04295f846040518263ffffffff1660e01b815260040161154191815260200190565b602060405180830381865afa15801561155e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611582919061293f565b90506114f78161159a866001600160a01b0316611af6565b6001600160a01b031663872697296040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115fb919061293f565b611ba5565b6000611614846001600160a01b0316611a47565b6001600160a01b0316638c7a63ae836040518263ffffffff1660e01b815260040161164191815260200190565b60a060405180830381865afa15801561165e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116829190612877565b80519091506116996001600160a01b038616611bdb565b6001600160a01b0316631852f200856116b58560000151611c8a565b6040518363ffffffff1660e01b81526004016116d2929190612958565b602060405180830381865afa1580156116ef573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171391906129ae565b61173257836040516373bb9c8960e01b81526004016105359190612594565b604051632a8a9f1360e21b81526004810184905260009081906001600160a01b0384169063aa2a7c4c9060240160408051808303816000875af115801561177d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117a191906129d0565b90925090506117d6866117b4838561264f565b6117c68a6001600160a01b0316611fe0565b6001600160a01b0316919061208f565b6117e8876001600160a01b03166120ea565b6001600160a01b0316632e1a7d4d866040518263ffffffff1660e01b815260040161181591815260200190565b6020604051808303816000875af1925050508015611850575060408051601f3d908101601f1916820190925261184d9181019061293f565b60015b1561186e5761186c87826117c68b6001600160a01b0316612199565b505b50505050505050565b600061188b846001600160a01b0316611998565b6001600160a01b0316631c4b774b836040518263ffffffff1660e01b81526004016118b891815260200190565b6020604051808303816000875af11580156118d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118fb919061293f565b905080156109a2576109a283826117c6876001600160a01b0316612199565b60006119258261114e565b905061192f61115d565b81111561195257604051632029102760e21b815260048101829052602401610535565b61195b8461116d565b61196361115d565b811015611984578284600101600082825461197e919061264f565b90915550505b82846000016000828254610ac2919061264f565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119fc91906126a4565b6001600160a01b0316630c0450127f5a31296b3cecaf3aaa3146a140f6ddf6d21caa95e28243dc507d90d7e883be796040518263ffffffff1660e01b8152600401610d7391906124dc565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aab91906126a4565b6001600160a01b0316630c0450127f7c9945d349966e663465dd2ad9f3ebe33d671877c22c40ca3a051e02e14e15ff6040518263ffffffff1660e01b8152600401610d7391906124dc565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b5a91906126a4565b6001600160a01b0316630c0450127fd4a8913f0cbda73931759328604779fb81c2513f90d252b9c4a96a5dd8327d886040518263ffffffff1660e01b8152600401610d7391906124dc565b6000670de0b6b3a7640000611bbd620f4240826126c1565b611bc791906129f4565b611bd183856129f4565b610b9591906126c1565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c3f91906126a4565b6001600160a01b0316630c0450127fc61c93ad97e9ad12d54663c76606860c6c3a3f53a59b88cd4bc1e3f43edc17956040518263ffffffff1660e01b8152600401610d7391906124dc565b606073efeb69edf6b6999b0e3f2fa856a2acf3bdea4ab56001600160a01b0383161480611cd3575073aa2ccc5547f64c5dffd0a624eb4af2543a67ba656001600160a01b038316145b80611cfa575073c9bdd0d3b80cc6efe79a82d850f44ec9b55387ae6001600160a01b038316145b80611d21575073e6c30756136e07eb5268c3232efbfbe645c1ba5a6001600160a01b038316145b80611d485750731d596d28a7923a22aa013b0e7082bba23daa656b6001600160a01b038316145b15611da25760015b604051908082528060200260200182016040528015611d79578160200160208202803683370190505b509050600081600081518110611d9157611d916125a8565b602002602001018181525050919050565b6001600160a01b03821673418749e294cabce5a714efccc22a8aade6f9db5703611dcd576001611d50565b72c27fc71b159a346e179b4a1608a0865e8a74706001600160a01b0383161480611e13575073d09a57127bc40d680be7cb061c2a6629fe71abef6001600160a01b038316145b15611e6e576040805160028082526060820183529091602083019080368337019050509050600081600081518110611e4d57611e4d6125a8565b602002602001018181525050600181600181518110611d9157611d916125a8565b73b26b42dd5771689d0a7faeea32825ff9710b9c116001600160a01b0383161480611eb5575073759f097f3153f5d62ff1c2d82ba78b6350f223e36001600160a01b038316145b80611edc57507389d7c618a4eef3065da8ad684859a547548e61696001600160a01b038316145b15611f7a5760408051600480825260a082019092529060208201608080368337019050509050600081600081518110611f1757611f176125a8565b602002602001018181525050600181600181518110611f3857611f386125a8565b602002602001018181525050600381600281518110611f5957611f596125a8565b602002602001018181525050600481600381518110611d9157611d916125a8565b816001600160a01b031663c78bed866040518163ffffffff1660e01b8152600401600060405180830381865afa158015611fb8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105b69190810190612a13565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015612020573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061204491906126a4565b6001600160a01b0316630c0450127fd6aca1be9729c13d677335161321649cccae6a591554772516700f986f942eaa6040518263ffffffff1660e01b8152600401610d7391906124dc565b6120e58363a9059cbb60e01b84846040516024016120ae929190612620565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612248565b505050565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa15801561212a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214e91906126a4565b6001600160a01b0316630c0450127f1cd0c2e09edd1d00946872d4dfa90e139030cf08dd8b201be0c3e01ae1534c576040518263ffffffff1660e01b8152600401610d7391906124dc565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121fd91906126a4565b6001600160a01b0316630c0450127f6fdcaa094af96c56039c701c590da2284d9e0c364f2dd8ee7d43419f0eefd2936040518263ffffffff1660e01b8152600401610d7391906124dc565b600061229d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661231a9092919063ffffffff16565b8051909150156120e557808060200190518101906122bb91906129ae565b6120e55760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610535565b60606114f7848460008585843b6123735760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610535565b600080866001600160a01b0316858760405161238f9190612add565b60006040518083038185875af1925050503d80600081146123cc576040519150601f19603f3d011682016040523d82523d6000602084013e6123d1565b606091505b50915091506123e18282866123ec565b979650505050505050565b606083156123fb575081610b95565b82511561240b5782518084602001fd5b8160405162461bcd60e51b81526004016105359190612af9565b6001600160a01b038116811461243a57600080fd5b50565b60008060008060006080868803121561245557600080fd5b853561246081612425565b9450602086013561247081612425565b935060408601359250606086013567ffffffffffffffff8082111561249457600080fd5b818801915088601f8301126124a857600080fd5b8135818111156124b757600080fd5b8960208285010111156124c957600080fd5b9699959850939650602001949392505050565b6001600160e01b031991909116815260200190565b60006020828403121561250357600080fd5b5035919050565b6000806040838503121561251d57600080fd5b823561252881612425565b946020939093013593505050565b60008060006060848603121561254b57600080fd5b833561255681612425565b9250602084013561256681612425565b929592945050506040919091013590565b60006020828403121561258957600080fd5b8135610b9581612425565b6001600160a01b0391909116815260200190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b6001600160a01b039384168152919092166020820152604081019190915260600190565b602081016002831061261a57634e487b7160e01b600052602160045260246000fd5b91905290565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052601160045260246000fd5b808201808211156105b6576105b6612639565b818103818111156105b6576105b6612639565b634e487b7160e01b600052603160045260246000fd5b60006001820161269d5761269d612639565b5060010190565b6000602082840312156126b657600080fd5b8151610b9581612425565b6000826126de57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052604160045260246000fd5b60405160e0810167ffffffffffffffff8111828210171561271c5761271c6126e3565b60405290565b60405160c0810167ffffffffffffffff8111828210171561271c5761271c6126e3565b60405160a0810167ffffffffffffffff8111828210171561271c5761271c6126e3565b604051601f8201601f1916810167ffffffffffffffff81118282101715612791576127916126e3565b604052919050565b6002811061243a57600080fd5b80516127b181612799565b919050565b60008183036101808112156127ca57600080fd5b6127d26126f9565b8351815260c0601f19830112156127e857600080fd5b6127f0612722565b91506020840151825260408401516020830152606084015160408301526080840151606083015260a0840151608083015260c084015160a083015281602082015260e08401516040820152610100840151606082015261285361012085016127a6565b608082015261014084015160a08201526101609093015160c0840152509092915050565b600060a0828403121561288957600080fd5b612891612745565b825161289c81612425565b80825250602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b6000602082840312156128e457600080fd5b8151610b9581612799565b600060a0828403121561290157600080fd5b612909612745565b82518152602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b60006020828403121561295157600080fd5b5051919050565b6001600160a01b038316815260406020808301829052835191830182905260009184820191906060850190845b818110156129a157845183529383019391830191600101612985565b5090979650505050505050565b6000602082840312156129c057600080fd5b81518015158114610b9557600080fd5b600080604083850312156129e357600080fd5b505080516020909101519092909150565b6000816000190483118215151615612a0e57612a0e612639565b500290565b60006020808385031215612a2657600080fd5b825167ffffffffffffffff80821115612a3e57600080fd5b818501915085601f830112612a5257600080fd5b815181811115612a6457612a646126e3565b8060051b9150612a75848301612768565b8181529183018401918481019088841115612a8f57600080fd5b938501935b83851015612aad57845182529385019390850190612a94565b98975050505050505050565b60005b83811015612ad4578181015183820152602001612abc565b50506000910152565b60008251612aef818460208701612ab9565b9190910192915050565b6020815260008251806020840152612b18816040850160208701612ab9565b601f01601f1916919091016040019291505056fe9e45de9588e74df19c5f35dd4ddf9c698e7a3e36b4004fd44cb75621507d7092a2646970667358221220885dc70c47907ae4225ece8e8fca76a7e0e467aff664ad04b5fa955395eeec6964736f6c63430008100033000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c83

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100bf5760003560e01c80634f6ccce71161007c5780634f6ccce71461016e5780636352211e1461018157806370a08231146101b757806399fbab88146101e0578063c4664c6314610262578063ddc632621461028e578063e305f306146102a157600080fd5b8063150b7a02146100c457806318160ddd146100f95780632e1a7d4d1461010b5780632f745c5914610120578063331ded1a14610133578063427d743214610146575b600080fd5b6100e36100d236600461243d565b630a85bd0160e11b95945050505050565b6040516100f091906124dc565b60405180910390f35b6003545b6040519081526020016100f0565b61011e6101193660046124f1565b6102c1565b005b6100fd61012e36600461250a565b610546565b6100fd610141366004612536565b6105bc565b610159610154366004612577565b6107e8565b604080519283526020830191909152016100f0565b6100fd61017c3660046124f1565b610814565b6101aa61018f3660046124f1565b6000908152602081905260409020546001600160a01b031690565b6040516100f09190612594565b6100fd6101c5366004612577565b6001600160a01b031660009081526001602052604090205490565b6102296101ee3660046124f1565b600060208190529081526040902080546001820154600283015460038401546004909401546001600160a01b03938416949293909116919085565b604080516001600160a01b039687168152602081019590955292909416918301919091526060820152608081019190915260a0016100f0565b6101aa6102703660046124f1565b6000908152602081905260409020600201546001600160a01b031690565b61011e61029c3660046124f1565b610849565b6100fd6102af3660046124f1565b60009081526004602052604090205490565b600080516020612b2d8339815191526102da81336109a8565b600082815260208181526040808320815160a08101835281546001600160a01b03808216835260018401805484880152600285018054928316968501968752600386018054606087015260048701805460808801528c8b52988a90526001600160a01b03199485169096559088905591169055908490559183905551909190610384907f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c8390610a0b565b6060830151608084015184516001600160a01b031660009081526002602052604090209293506103b5929190610a44565b81516001600160a01b031660009081526001602090815260408220908401519091906103e2908390610acd565b915050801561042a57836020015160008084876020015181548110610409576104096125a8565b90600052602060002001548152602001908152602001600020600101819055505b600183600181111561043e5761043e6125be565b0361051a57600086815260046020819052604080832080549390558681015187519151632142170760e11b81526001600160a01b03909116926342842e0e9261048b9230928791016125d4565b600060405180830381600087803b1580156104a557600080fd5b505af11580156104b9573d6000803e3d6000fd5b5050865160408089015160808a015182518d81526001600160a01b0392831660208201529283015290911692507f50065562d2b38b7cc51ee877211c37a54ff469d129d1a67bfeb97e0c2994a316915060600160405180910390a25061053e565b826040516308a8fee960e31b815260040161053591906125f8565b60405180910390fd5b505050505050565b6001600160a01b038216600090815260016020526040812054821061057e57604051638f832b9b60e01b815260040160405180910390fd5b6001600160a01b03831660009081526001602052604090208054839081106105a8576105a86125a8565b906000526020600020015490505b92915050565b6000600080516020612b2d8339815191526105d781336109a8565b60016106037f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c8386610a0b565b6001811115610614576106146125be565b1461063457836040516337bce3c560e11b81526004016105359190612594565b61065f7f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c838585610b56565b610680578383604051634eb9917160e11b8152600401610535929190612620565b8360006106ae7f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c838387610b9c565b905060006106bd888884610beb565b604080516020808201835289825260008481526004825283812092519092556001600160a01b038c16825260029052209091506106fa9083610cb0565b826001600160a01b03166342842e0e61073b7f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c836001600160a01b0316610cd6565b30896040518463ffffffff1660e01b815260040161075b939291906125d4565b600060405180830381600087803b15801561077557600080fd5b505af1158015610789573d6000803e3d6000fd5b505060408051848152602081018a90529081018590526001600160a01b03808b1693508b1691507fb1c56a0ea823a5b59843a1c0223e87b70affc07e96fc346165322958cdef13949060600160405180910390a3979650505050505050565b6001600160a01b0381166000908152600260205260408120819061080b90610db4565b91509150915091565b600061081f60035490565b821061083e57604051633f7ee38f60e11b815260040160405180910390fd5b6105b682600161264f565b600080516020612b2d83398151915261086281336109a8565b600082815260208181526040808320815160a08101835281546001600160a01b0390811682526001830154948201949094526002820154909316918301829052600381015460608401526004015460808301529091906108e3907f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c8390610a0b565b905060018160018111156108f9576108f96125be565b1461091957806040516308a8fee960e31b815260040161053591906125f8565b815160408084015160008781526004602052919091205461095d927f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c83929091610e05565b837f54c43c33795a11f3152d46be48143a092092361e37f765f6f9da0251aabf1f9683604001516040516109919190612594565b60405180910390a26109a284610e59565b50505050565b6001600160a01b0381166109cf5760405163d92e233d60e01b815260040160405180910390fd5b6109d98282610fff565b610a075760405163889a56bb60e01b81523060048201526001600160a01b0382166024820152604401610535565b5050565b6000610a17838361110a565b15610a24575060016105b6565b610a2e838361113a565b15610a3b575060016105b6565b50600092915050565b6000610a4f8261114e565b9050610a5961115d565b811115610a7c57604051632029102760e21b815260048101829052602401610535565b610a858461116d565b82846000016000828254610a999190612662565b90915550610aa7905061115d565b8110156109a25782846001016000828254610ac29190612662565b909155505050505050565b81546000908190610ae090600190612662565b915050818114801590610b2957838281548110610aff57610aff6125a8565b9060005260206000200154848481548110610b1c57610b1c6125a8565b6000918252602090912001555b83805480610b3957610b39612675565b600190038181906000526020600020016000905590559250929050565b6000610b62848461110a565b15610b7857610b718483611199565b9050610b95565b610b82848461113a565b15610b9157610b71848361123a565b5060005b9392505050565b6000610ba8848461113a565b15610bb757610b71848361145e565b610bc1848461110a565b15610bd057610b7184836114ff565b826040516337bce3c560e11b81526004016105359190612594565b6003805460009182610bfc8361268b565b9091555050600380546040805160a0810182526001600160a01b0397881680825260009081526001602081815284832080548286019081529a8c16858701908152606086019a8b52426080870190815288865285845296852095518654908e166001600160a01b03199182161787559b51868501555160028601805491909d169b169a909a17909a559651948201949094559051600490910155838652845493840185559381529390932001819055919050565b610cb98261116d565b80826000016000828254610ccd919061264f565b90915550505050565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3a91906126a4565b6001600160a01b0316630c045012600080516020612b2d8339815191526040518263ffffffff1660e01b8152600401610d7391906124dc565b602060405180830381865afa158015610d90573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b691906126a4565b6040805160608101825282548152600183015460208201526002830154918101829052600091829190610de561115d565b03610dfb57602081015190519094909350915050565b5193849350915050565b610e0f848361113a565b15610e2457610e1f848483611600565b6109a2565b610e2e848361110a565b15610e3e57610e1f848483611877565b816040516337bce3c560e11b81526004016105359190612594565b600081815260208181526040808320815160a08101835281546001600160a01b039081168252600183015494820194909452600282015490931691830182905260038101546060840152600401546080830152909190610eda907f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c8390610a0b565b90506001816001811115610ef057610ef06125be565b14610f1057806040516308a8fee960e31b815260040161053591906125f8565b6060820151608083015183516001600160a01b03166000908152600260205260409020610f3e929091610a44565b6040808301516000858152600460205291822054610f7d917f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c8391610b9c565b600085815260208181526040808320600301849055608087015187516001600160a01b031684526002909252909120919250610fbb9190839061191a565b837f6a28020abc16896e42077f4597b339d5cdf310f437dee250bcfb161a8539b27a846040015183604051610ff1929190612620565b60405180910390a250505050565b6000816001600160a01b03167f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c836001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611069573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108d91906126a4565b6001600160a01b0316630c045012856040518263ffffffff1660e01b81526004016110b891906124dc565b602060405180830381865afa1580156110d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110f991906126a4565b6001600160a01b0316149392505050565b600061111e836001600160a01b0316611998565b6001600160a01b0316826001600160a01b031614905092915050565b600061111e836001600160a01b0316611a47565b60006105b662093a80836126c1565b60006111684261114e565b905090565b806002015461117a61115d565b116111825750565b8054600182015561119161115d565b600290910155565b6000806111ae846001600160a01b0316611998565b6001600160a01b031663eb02c301846040518263ffffffff1660e01b81526004016111db91815260200190565b61018060405180830381865afa1580156111f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061121d91906127b6565b608001516001811115611232576112326125be565b149392505050565b60008061124f846001600160a01b0316611a47565b6001600160a01b0316638c7a63ae846040518263ffffffff1660e01b815260040161127c91815260200190565b60a060405180830381865afa158015611299573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112bd9190612877565b9050600081600001516001600160a01b031663191401196040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801561131f575060408051601f3d908101601f1916820190925261131c918101906128d2565b60015b156113275790505b600081600181111561133b5761133b6125be565b036113c8578151602083015160405163d972e8ad60e01b81526001600160a01b039092169163d972e8ad916113769160040190815260200190565b60a060405180830381865afa158015611393573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b791906128ef565b6080015160001415925050506105b6565b60018160018111156113dc576113dc6125be565b0361145657816020015182600001516001600160a01b0316632ba443d26040518163ffffffff1660e01b8152600401602060405180830381865afa158015611428573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144c919061293f565b14925050506105b6565b505092915050565b600080611473846001600160a01b0316611a47565b6001600160a01b0316638c7a63ae846040518263ffffffff1660e01b81526004016114a091815260200190565b60a060405180830381865afa1580156114bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e19190612877565b9050806060015181604001516114f79190612662565b949350505050565b600080611514846001600160a01b0316611998565b6001600160a01b031663aa04295f846040518263ffffffff1660e01b815260040161154191815260200190565b602060405180830381865afa15801561155e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611582919061293f565b90506114f78161159a866001600160a01b0316611af6565b6001600160a01b031663872697296040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115fb919061293f565b611ba5565b6000611614846001600160a01b0316611a47565b6001600160a01b0316638c7a63ae836040518263ffffffff1660e01b815260040161164191815260200190565b60a060405180830381865afa15801561165e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116829190612877565b80519091506116996001600160a01b038616611bdb565b6001600160a01b0316631852f200856116b58560000151611c8a565b6040518363ffffffff1660e01b81526004016116d2929190612958565b602060405180830381865afa1580156116ef573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171391906129ae565b61173257836040516373bb9c8960e01b81526004016105359190612594565b604051632a8a9f1360e21b81526004810184905260009081906001600160a01b0384169063aa2a7c4c9060240160408051808303816000875af115801561177d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117a191906129d0565b90925090506117d6866117b4838561264f565b6117c68a6001600160a01b0316611fe0565b6001600160a01b0316919061208f565b6117e8876001600160a01b03166120ea565b6001600160a01b0316632e1a7d4d866040518263ffffffff1660e01b815260040161181591815260200190565b6020604051808303816000875af1925050508015611850575060408051601f3d908101601f1916820190925261184d9181019061293f565b60015b1561186e5761186c87826117c68b6001600160a01b0316612199565b505b50505050505050565b600061188b846001600160a01b0316611998565b6001600160a01b0316631c4b774b836040518263ffffffff1660e01b81526004016118b891815260200190565b6020604051808303816000875af11580156118d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118fb919061293f565b905080156109a2576109a283826117c6876001600160a01b0316612199565b60006119258261114e565b905061192f61115d565b81111561195257604051632029102760e21b815260048101829052602401610535565b61195b8461116d565b61196361115d565b811015611984578284600101600082825461197e919061264f565b90915550505b82846000016000828254610ac2919061264f565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119fc91906126a4565b6001600160a01b0316630c0450127f5a31296b3cecaf3aaa3146a140f6ddf6d21caa95e28243dc507d90d7e883be796040518263ffffffff1660e01b8152600401610d7391906124dc565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aab91906126a4565b6001600160a01b0316630c0450127f7c9945d349966e663465dd2ad9f3ebe33d671877c22c40ca3a051e02e14e15ff6040518263ffffffff1660e01b8152600401610d7391906124dc565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b5a91906126a4565b6001600160a01b0316630c0450127fd4a8913f0cbda73931759328604779fb81c2513f90d252b9c4a96a5dd8327d886040518263ffffffff1660e01b8152600401610d7391906124dc565b6000670de0b6b3a7640000611bbd620f4240826126c1565b611bc791906129f4565b611bd183856129f4565b610b9591906126c1565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c3f91906126a4565b6001600160a01b0316630c0450127fc61c93ad97e9ad12d54663c76606860c6c3a3f53a59b88cd4bc1e3f43edc17956040518263ffffffff1660e01b8152600401610d7391906124dc565b606073efeb69edf6b6999b0e3f2fa856a2acf3bdea4ab56001600160a01b0383161480611cd3575073aa2ccc5547f64c5dffd0a624eb4af2543a67ba656001600160a01b038316145b80611cfa575073c9bdd0d3b80cc6efe79a82d850f44ec9b55387ae6001600160a01b038316145b80611d21575073e6c30756136e07eb5268c3232efbfbe645c1ba5a6001600160a01b038316145b80611d485750731d596d28a7923a22aa013b0e7082bba23daa656b6001600160a01b038316145b15611da25760015b604051908082528060200260200182016040528015611d79578160200160208202803683370190505b509050600081600081518110611d9157611d916125a8565b602002602001018181525050919050565b6001600160a01b03821673418749e294cabce5a714efccc22a8aade6f9db5703611dcd576001611d50565b72c27fc71b159a346e179b4a1608a0865e8a74706001600160a01b0383161480611e13575073d09a57127bc40d680be7cb061c2a6629fe71abef6001600160a01b038316145b15611e6e576040805160028082526060820183529091602083019080368337019050509050600081600081518110611e4d57611e4d6125a8565b602002602001018181525050600181600181518110611d9157611d916125a8565b73b26b42dd5771689d0a7faeea32825ff9710b9c116001600160a01b0383161480611eb5575073759f097f3153f5d62ff1c2d82ba78b6350f223e36001600160a01b038316145b80611edc57507389d7c618a4eef3065da8ad684859a547548e61696001600160a01b038316145b15611f7a5760408051600480825260a082019092529060208201608080368337019050509050600081600081518110611f1757611f176125a8565b602002602001018181525050600181600181518110611f3857611f386125a8565b602002602001018181525050600381600281518110611f5957611f596125a8565b602002602001018181525050600481600381518110611d9157611d916125a8565b816001600160a01b031663c78bed866040518163ffffffff1660e01b8152600401600060405180830381865afa158015611fb8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105b69190810190612a13565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015612020573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061204491906126a4565b6001600160a01b0316630c0450127fd6aca1be9729c13d677335161321649cccae6a591554772516700f986f942eaa6040518263ffffffff1660e01b8152600401610d7391906124dc565b6120e58363a9059cbb60e01b84846040516024016120ae929190612620565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612248565b505050565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa15801561212a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214e91906126a4565b6001600160a01b0316630c0450127f1cd0c2e09edd1d00946872d4dfa90e139030cf08dd8b201be0c3e01ae1534c576040518263ffffffff1660e01b8152600401610d7391906124dc565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121fd91906126a4565b6001600160a01b0316630c0450127f6fdcaa094af96c56039c701c590da2284d9e0c364f2dd8ee7d43419f0eefd2936040518263ffffffff1660e01b8152600401610d7391906124dc565b600061229d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661231a9092919063ffffffff16565b8051909150156120e557808060200190518101906122bb91906129ae565b6120e55760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610535565b60606114f7848460008585843b6123735760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610535565b600080866001600160a01b0316858760405161238f9190612add565b60006040518083038185875af1925050503d80600081146123cc576040519150601f19603f3d011682016040523d82523d6000602084013e6123d1565b606091505b50915091506123e18282866123ec565b979650505050505050565b606083156123fb575081610b95565b82511561240b5782518084602001fd5b8160405162461bcd60e51b81526004016105359190612af9565b6001600160a01b038116811461243a57600080fd5b50565b60008060008060006080868803121561245557600080fd5b853561246081612425565b9450602086013561247081612425565b935060408601359250606086013567ffffffffffffffff8082111561249457600080fd5b818801915088601f8301126124a857600080fd5b8135818111156124b757600080fd5b8960208285010111156124c957600080fd5b9699959850939650602001949392505050565b6001600160e01b031991909116815260200190565b60006020828403121561250357600080fd5b5035919050565b6000806040838503121561251d57600080fd5b823561252881612425565b946020939093013593505050565b60008060006060848603121561254b57600080fd5b833561255681612425565b9250602084013561256681612425565b929592945050506040919091013590565b60006020828403121561258957600080fd5b8135610b9581612425565b6001600160a01b0391909116815260200190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b6001600160a01b039384168152919092166020820152604081019190915260600190565b602081016002831061261a57634e487b7160e01b600052602160045260246000fd5b91905290565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052601160045260246000fd5b808201808211156105b6576105b6612639565b818103818111156105b6576105b6612639565b634e487b7160e01b600052603160045260246000fd5b60006001820161269d5761269d612639565b5060010190565b6000602082840312156126b657600080fd5b8151610b9581612425565b6000826126de57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052604160045260246000fd5b60405160e0810167ffffffffffffffff8111828210171561271c5761271c6126e3565b60405290565b60405160c0810167ffffffffffffffff8111828210171561271c5761271c6126e3565b60405160a0810167ffffffffffffffff8111828210171561271c5761271c6126e3565b604051601f8201601f1916810167ffffffffffffffff81118282101715612791576127916126e3565b604052919050565b6002811061243a57600080fd5b80516127b181612799565b919050565b60008183036101808112156127ca57600080fd5b6127d26126f9565b8351815260c0601f19830112156127e857600080fd5b6127f0612722565b91506020840151825260408401516020830152606084015160408301526080840151606083015260a0840151608083015260c084015160a083015281602082015260e08401516040820152610100840151606082015261285361012085016127a6565b608082015261014084015160a08201526101609093015160c0840152509092915050565b600060a0828403121561288957600080fd5b612891612745565b825161289c81612425565b80825250602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b6000602082840312156128e457600080fd5b8151610b9581612799565b600060a0828403121561290157600080fd5b612909612745565b82518152602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b60006020828403121561295157600080fd5b5051919050565b6001600160a01b038316815260406020808301829052835191830182905260009184820191906060850190845b818110156129a157845183529383019391830191600101612985565b5090979650505050505050565b6000602082840312156129c057600080fd5b81518015158114610b9557600080fd5b600080604083850312156129e357600080fd5b505080516020909101519092909150565b6000816000190483118215151615612a0e57612a0e612639565b500290565b60006020808385031215612a2657600080fd5b825167ffffffffffffffff80821115612a3e57600080fd5b818501915085601f830112612a5257600080fd5b815181811115612a6457612a646126e3565b8060051b9150612a75848301612768565b8181529183018401918481019088841115612a8f57600080fd5b938501935b83851015612aad57845182529385019390850190612a94565b98975050505050505050565b60005b83811015612ad4578181015183820152602001612abc565b50506000910152565b60008251612aef818460208701612ab9565b9190910192915050565b6020815260008251806020840152612b18816040850160208701612ab9565b601f01601f1916919091016040019291505056fe9e45de9588e74df19c5f35dd4ddf9c698e7a3e36b4004fd44cb75621507d7092a2646970667358221220885dc70c47907ae4225ece8e8fca76a7e0e467aff664ad04b5fa955395eeec6964736f6c63430008100033

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.