ETH Price: $3,380.23 (-0.80%)
Gas: 4 Gwei

Contract

0xba7B8e4ae02C33afF14e472856259DDc46B6DC21
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
0x60a06040159928792022-11-17 22:59:59589 days ago1668725999IN
 Create: MembershipVault
0 ETH0.0219628616.18673974

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MembershipVault

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 100 runs

Other Settings:
default evmVersion, MIT license
File 1 of 40 : MembershipVault.sol
// SPDX-License-Identifier: MIT
// solhint-disable func-name-mixedcase

pragma solidity ^0.8.16;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/interfaces/IERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/interfaces/IERC721MetadataUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";

import {IMembershipVault, Position} from "../../../interfaces/IMembershipVault.sol";

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

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

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

using Routing.Context for Context;
using StringsUpgradeable for uint256;

/**
 * @title MembershipVault
 * @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
 */
contract MembershipVault is
  IMembershipVault,
  Base,
  ERC721NonTransferable,
  IERC721EnumerableUpgradeable,
  IERC721MetadataUpgradeable,
  Initializable
{
  /// Thrown when depositing from address(0)
  error ZeroAddressInvalid();
  /// Thrown when trying to access tokens from an address with no tokens
  error NoTokensOwned();
  /// Thrown when trying to access more than one token for an address
  error OneTokenPerAddress();
  /// Thrown when querying token supply with an index greater than the supply
  error IndexGreaterThanTokenSupply();
  /// Thrown when checking totals in future epochs
  error NoTotalsInFutureEpochs();
  /// Thrown when adjusting holdings in an unsupported way
  error InvalidHoldingsAdjustment(uint256 eligibleAmount, uint256 nextEpochAmount);
  /// Thrown when requesting a nonexistant token
  error NonexistantToken(uint256 tokenId);

  /**
   * @notice The vault has been checkpointed
   * @param total how much is stored in the vault at the current block.timestamp
   */
  event Checkpoint(uint256 total);

  /// @notice Totals by epoch. totalAmounts is always tracking past epochs, the current
  ///   epoch, and the next epoch. There are a few cases:
  ///   1. Checkpointing
  ///      Noop for the same epoch. Checkpointing occurs before any mutative action
  ///      so for new epochs, the last-set epoch value (totalAmounts[previousEpoch + 1])
  ///      is copied to each epoch up to the current epoch + 1
  ///   2. Increasing
  ///      Checkpointing already occurred, so current epoch and next epoch
  ///      are properly set up. Increasing just updates the next epoch value
  ///   3. Decreasing
  ///      Checkpointing already occurred like above. Decreasing updates the eligible
  ///      and next epoch values
  mapping(uint256 => uint256) private totalAmounts;

  /// @notice last epoch the vault was checkpointed
  uint256 private checkpointEpoch;

  /// @notice all positions held by the vault
  mapping(uint256 => Position) private positions;

  /// @notice owners and their position
  mapping(address => uint256) private owners;

  /// @notice counter tracking most current membership id
  uint256 private membershipIdsTracker;

  /// @notice base uri for the nft
  string public baseURI;

  //solhint-disable-next-line no-empty-blocks
  constructor(Context _context) Base(_context) {}

  function initialize() public initializer {
    checkpointEpoch = Epochs.current();
  }

  //////////////////////////////////////////////////////////////////
  // ERC 721 + Enumerable

  function totalSupply() public view returns (uint256) {
    return membershipIdsTracker;
  }

  function ownerOf(uint256 membershipId) external view returns (address owner) {
    return positions[membershipId].owner;
  }

  function balanceOf(address owner) external view returns (uint256) {
    return owners[owner] > 0 ? 1 : 0;
  }

  function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256) {
    uint256 membershipId = owners[owner];
    if (membershipId == 0) revert NoTokensOwned();
    if (index > 0) revert OneTokenPerAddress();

    return membershipId;
  }

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

    return index + 1;
  }

  function supportsInterface(bytes4 id) external pure override returns (bool) {
    return (id == ERCInterfaces.ERC721 || id == ERCInterfaces.ERC721_ENUMERABLE || id == ERCInterfaces.ERC165);
  }

  //////////////////////////////////////////////////////////////////
  // ERC721 Metadata

  /// @inheritdoc IERC721MetadataUpgradeable
  function name() external pure returns (string memory) {
    return "Goldfinch Membership";
  }

  /// @inheritdoc IERC721MetadataUpgradeable
  function symbol() external pure returns (string memory) {
    return "GFMEMBER";
  }

  /// @inheritdoc IERC721MetadataUpgradeable
  function tokenURI(uint256 tokenId) external view returns (string memory) {
    if (tokenId == 0) revert NonexistantToken(tokenId);
    if (tokenId > membershipIdsTracker) revert NonexistantToken(tokenId);

    return string(abi.encodePacked(baseURI, tokenId.toString()));
  }

  /// @notice Set the base uri for the contract
  function setBaseURI(string calldata uri) external onlyAdmin {
    baseURI = uri;
  }

  //////////////////////////////////////////////////////////////////
  // IMembershipVault

  /// @inheritdoc IMembershipVault
  function currentValueOwnedBy(address owner) external view override returns (uint256) {
    Position memory position = positions[owners[owner]];
    if (Epochs.current() > position.checkpointEpoch) {
      return position.nextEpochAmount;
    }

    return position.eligibleAmount;
  }

  /// @inheritdoc IMembershipVault
  function currentTotal() external view override returns (uint256) {
    return totalAtEpoch(Epochs.current());
  }

  /// @inheritdoc IMembershipVault
  function totalAtEpoch(uint256 epoch) public view returns (uint256) {
    if (epoch > Epochs.next()) revert NoTotalsInFutureEpochs();

    if (epoch > checkpointEpoch) {
      // If querying for an epoch past the checkpoint, always use the next amount. This is the amount
      // that will become eligible for every epoch after `checkpointEpoch`.

      return totalAmounts[checkpointEpoch + 1];
    }

    return totalAmounts[epoch];
  }

  /// @inheritdoc IMembershipVault
  function positionOwnedBy(address owner) external view returns (Position memory) {
    return positions[owners[owner]];
  }

  // @inheritdoc IMembershipVault
  function adjustHoldings(
    address owner,
    uint256 eligibleAmount,
    uint256 nextEpochAmount
  ) external onlyOperator(Routing.Keys.MembershipDirector) returns (uint256) {
    if (nextEpochAmount < eligibleAmount) revert InvalidHoldingsAdjustment(eligibleAmount, nextEpochAmount);

    uint256 membershipId = _fetchOrCreateMembership(owner);

    _checkpoint(owner);

    Position memory position = positions[membershipId];

    positions[membershipId].eligibleAmount = eligibleAmount;
    positions[membershipId].nextEpochAmount = nextEpochAmount;

    totalAmounts[Epochs.current()] = (totalAmounts[Epochs.current()] - position.eligibleAmount) + eligibleAmount;
    totalAmounts[Epochs.next()] = (totalAmounts[Epochs.next()] - position.nextEpochAmount) + nextEpochAmount;

    emit AdjustedHoldings({owner: owner, eligibleAmount: eligibleAmount, nextEpochAmount: nextEpochAmount});
    emit VaultTotalUpdate({
      eligibleAmount: totalAmounts[Epochs.current()],
      nextEpochAmount: totalAmounts[Epochs.next()]
    });

    return membershipId;
  }

  // @inheritdoc IMembershipVault
  function checkpoint(address owner) external onlyOperator(Routing.Keys.MembershipDirector) {
    return _checkpoint(owner);
  }

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

  function _fetchOrCreateMembership(address owner) private returns (uint256) {
    if (owner == address(0)) revert ZeroAddressInvalid();

    uint256 membershipId = owners[owner];
    if (membershipId > 0) return membershipId;

    membershipIdsTracker++;
    membershipId = membershipIdsTracker;

    positions[membershipId].owner = owner;
    positions[membershipId].createdTimestamp = block.timestamp;
    positions[membershipId].checkpointEpoch = Epochs.current();

    owners[owner] = membershipId;

    emit Transfer({from: address(0), to: owner, tokenId: membershipId});

    return membershipId;
  }

  function _checkpoint(address owner) private {
    uint256 currentEpoch = Epochs.current();

    if (currentEpoch > checkpointEpoch) {
      // Promote the last checkpoint's nextAmount to all subsequent epochs up to currentEpoch + 1. This
      // guarantees that total[current] and total[next] are always properly set before any operations
      // are performed.
      uint256 lastCheckpointNextAmount = totalAmounts[checkpointEpoch + 1];
      for (uint256 epoch = checkpointEpoch + 2; epoch <= currentEpoch + 1; epoch++) {
        totalAmounts[epoch] = lastCheckpointNextAmount;
      }

      checkpointEpoch = Epochs.current();
    }

    uint256 membershipId = owners[owner];
    if (membershipId > 0) {
      // positionId of 0 means that no position exists. This occurs if checkpoint is called
      // before a position is created.

      Position memory previousPosition = positions[membershipId];

      // Promote `nextEpochAmount` to `eligibleAmount` if epochs have progressed
      if (currentEpoch > previousPosition.checkpointEpoch) {
        positions[membershipId].eligibleAmount = previousPosition.nextEpochAmount;
        positions[membershipId].checkpointEpoch = Epochs.current();
      }
    }

    emit Checkpoint(totalAmounts[Epochs.current()]);
  }
}

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

pragma solidity ^0.8.0;

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

File 3 of 40 : IERC721EnumerableUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../token/ERC721/extensions/IERC721EnumerableUpgradeable.sol";

File 4 of 40 : IERC721MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../token/ERC721/extensions/IERC721MetadataUpgradeable.sol";

File 5 of 40 : IERC721Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

File 6 of 40 : 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 7 of 40 : 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 8 of 40 : 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 9 of 40 : IERC721EnumerableUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC721Upgradeable.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721EnumerableUpgradeable is IERC721Upgradeable {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}

File 10 of 40 : IERC721MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC721Upgradeable.sol";

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

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 11 of 40 : StringsUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

File 12 of 40 : 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 13 of 40 : 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 14 of 40 : 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 15 of 40 : 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 16 of 40 : 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 17 of 40 : 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 {ISeniorPool} from "../interfaces/ISeniorPool.sol";
import {IPoolTokens} from "../interfaces/IPoolTokens.sol";
import {IStakingRewards} from "../interfaces/IStakingRewards.sol";

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

import {Router} from "./Router.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"));
}

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

File 18 of 40 : 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 19 of 40 : 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
  );

  /// 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 Get the asset address of the position. Example: For an ERC721 position, this
   *  returns the address of that ERC721 contract.
   * @param positionId id of the position
   * @return asset address of the position
   */
  function assetAddressOf(uint256 positionId) external view returns (address);

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

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

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

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

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

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

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

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

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

  function limit() external view returns (uint256);

  function maxLimit() external view returns (uint256);

  function interestApr() external view returns (uint256);

  function paymentPeriodInDays() external view returns (uint256);

  function principalGracePeriodInDays() external view returns (uint256);

  function termInDays() external view returns (uint256);

  function lateFeeApr() external view returns (uint256);

  function isLate() external view returns (bool);

  function withinPrincipalGracePeriod() external view returns (bool);

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

  function interestOwed() external view returns (uint256);

  function principalOwed() external view returns (uint256);

  function termEndTime() external view returns (uint256);

  function nextDueTime() external view returns (uint256);

  function interestAccruedAsOf() external view returns (uint256);

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

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

pragma solidity >=0.6.12;

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

  function distribute() external;

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

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

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

pragma solidity >=0.6.12;

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

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

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

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

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

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

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

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

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

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

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

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

File 23 of 40 : 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 24 of 40 : 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 25 of 40 : 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 26 of 40 : 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 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 27 of 40 : 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 28 of 40 : IPoolTokens.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

import "./openzeppelin/IERC721.sol";

interface IPoolTokens is IERC721 {
  event TokenMinted(
    address indexed owner,
    address indexed pool,
    uint256 indexed tokenId,
    uint256 amount,
    uint256 tranche
  );

  event TokenRedeemed(
    address indexed owner,
    address indexed pool,
    uint256 indexed tokenId,
    uint256 principalRedeemed,
    uint256 interestRedeemed,
    uint256 tranche
  );
  event TokenBurned(address indexed owner, address indexed pool, uint256 indexed tokenId);

  struct TokenInfo {
    address pool;
    uint256 tranche;
    uint256 principalAmount;
    uint256 principalRedeemed;
    uint256 interestRedeemed;
  }

  struct MintParams {
    uint256 principalAmount;
    uint256 tranche;
  }

  function mint(MintParams calldata params, address to) external returns (uint256);

  function redeem(
    uint256 tokenId,
    uint256 principalRedeemed,
    uint256 interestRedeemed
  ) external;

  function withdrawPrincipal(uint256 tokenId, uint256 principalAmount) external;

  function burn(uint256 tokenId) external;

  function onPoolCreated(address newPool) external;

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

  function validPool(address sender) external view returns (bool);

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

File 29 of 40 : 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 30 of 40 : ISeniorPool.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

import "./ITranchedPool.sol";

abstract contract ISeniorPool {
  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);

  function withdraw(uint256 usdcAmount) external virtual returns (uint256 amount);

  function withdrawInFidu(uint256 fiduAmount) external virtual returns (uint256 amount);

  function sweepToCompound() public virtual;

  function sweepFromCompound() public virtual;

  function invest(ITranchedPool pool) public virtual;

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

  function redeem(uint256 tokenId) public virtual;

  function writedown(uint256 tokenId) public virtual;

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

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

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

File 31 of 40 : 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 {
  function getPosition(uint256 tokenId) external view returns (StakedPosition memory position);

  function unstake(uint256 tokenId, uint256 amount) external;

  function addToStake(uint256 tokenId, uint256 amount) external;

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

  function depositToCurveAndStakeFrom(
    address nftRecipient,
    uint256 fiduAmount,
    uint256 usdcAmount
  ) external;

  function kick(uint256 tokenId) external;

  function accumulatedRewardsPerToken() external view returns (uint256);

  function lastUpdateTime() external view 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 Unstaked(address indexed user, uint256 indexed tokenId, uint256 amount, StakedPositionType positionType);
  event UnstakedMultiple(address indexed user, uint256[] tokenIds, uint256[] amounts);
  event UnstakedAndWithdrew(address indexed user, uint256 usdcReceivedAmount, uint256 indexed tokenId, uint256 amount);
  event UnstakedAndWithdrewMultiple(
    address indexed user,
    uint256 usdcReceivedAmount,
    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 32 of 40 : ITranchedPool.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

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

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

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

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

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

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

  function pay(uint256 amount) external virtual;

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

  function lockJuniorCapital() external virtual;

  function lockPool() external virtual;

  function initializeNextSlice(uint256 _fundableAt) external virtual;

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

  function drawdown(uint256 amount) external virtual;

  function setFundableAt(uint256 timestamp) external virtual;

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

  function assess() external virtual;

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

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

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

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

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

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

File 33 of 40 : IV2CreditLine.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

import "./ICreditLine.sol";

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

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

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

  function setLimit(uint256 newAmount) external virtual;

  function setMaxLimit(uint256 newAmount) external virtual;

  function setBalance(uint256 newBalance) external virtual;

  function setPrincipal(uint256 _principal) external virtual;

  function setTotalInterestAccrued(uint256 _interestAccrued) external virtual;

  function drawdown(uint256 amount) external virtual;

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

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

  function setTermEndTime(uint256 newTermEndTime) external virtual;

  function setNextDueTime(uint256 newNextDueTime) external virtual;

  function setInterestOwed(uint256 newInterestOwed) external virtual;

  function setPrincipalOwed(uint256 newPrincipalOwed) external virtual;

  function setInterestAccruedAsOf(uint256 newInterestAccruedAsOf) external virtual;

  function setWritedownAmount(uint256 newWritedownAmount) external virtual;

  function setLastFullPaymentTime(uint256 newLastFullPaymentTime) external virtual;

  function setLateFeeApr(uint256 newLateFeeApr) external virtual;
}

File 34 of 40 : IERC165.sol
pragma solidity >=0.6.0;

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

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

File 35 of 40 : IERC721.sol
pragma solidity >=0.6.2;

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

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

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

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

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

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

  function approve(address to, uint256 tokenId) external;

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

  function setApprovalForAll(address operator, bool _approved) external;

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

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

File 36 of 40 : 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 37 of 40 : 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 38 of 40 : ERC721NonTransferable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.16;

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

/**
 * @title A read only ERC721 token
 * @notice A abstract registry of NFTs that only allows reading the NFTs and nothing
 *         else (no minting, transferring, etc). This acts as a "view" into some set
 *         of NFTs that may not otherwise adhere to the ERC721 standard.
 * @dev See `Transfer Mechanism` in the following link for the inspiration
 *      behind this class: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md#rationale
 */

abstract contract ERC721NonTransferable is IERC721Upgradeable {
  // Throw if a mutating function is called
  error ReadOnly();

  function safeTransferFrom(
    address,
    address,
    uint256
  ) external pure {
    revert ReadOnly();
  }

  function transferFrom(
    address,
    address,
    uint256
  ) external pure {
    revert ReadOnly();
  }

  function approve(address, uint256) external pure {
    revert ReadOnly();
  }

  function getApproved(uint256) external pure returns (address) {
    revert ReadOnly();
  }

  function setApprovalForAll(address, bool) external pure {
    revert ReadOnly();
  }

  function isApprovedForAll(address, address) external pure returns (bool) {
    revert ReadOnly();
  }

  function safeTransferFrom(
    address,
    address,
    uint256,
    bytes calldata
  ) external pure {
    revert ReadOnly();
  }
}

File 39 of 40 : ERCInterfaces.sol
pragma solidity ^0.8.16;

library ERCInterfaces {
  bytes4 internal constant ERC721 = 0x80ac58cd;
  bytes4 internal constant ERC721_ENUMERABLE = 0x780e9d63;
  bytes4 internal constant ERC165 = 0x01ffc9a7;
}

File 40 of 40 : 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;
  }
}

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":"uint256","name":"eligibleAmount","type":"uint256"},{"internalType":"uint256","name":"nextEpochAmount","type":"uint256"}],"name":"InvalidHoldingsAdjustment","type":"error"},{"inputs":[],"name":"NoTokensOwned","type":"error"},{"inputs":[],"name":"NoTotalsInFutureEpochs","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NonexistantToken","type":"error"},{"inputs":[],"name":"OneTokenPerAddress","type":"error"},{"inputs":[],"name":"ReadOnly","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":"ZeroAddressInvalid","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"eligibleAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nextEpochAmount","type":"uint256"}],"name":"AdjustedHoldings","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"total","type":"uint256"}],"name":"Checkpoint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"eligibleAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nextEpochAmount","type":"uint256"}],"name":"VaultTotalUpdate","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"eligibleAmount","type":"uint256"},{"internalType":"uint256","name":"nextEpochAmount","type":"uint256"}],"name":"adjustHoldings","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"currentValueOwnedBy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"membershipId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"positionOwnedBy","outputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"eligibleAmount","type":"uint256"},{"internalType":"uint256","name":"nextEpochAmount","type":"uint256"},{"internalType":"uint256","name":"createdTimestamp","type":"uint256"},{"internalType":"uint256","name":"checkpointEpoch","type":"uint256"}],"internalType":"struct Position","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"id","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","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":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"totalAtEpoch","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":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"pure","type":"function"}]

60a060405234801561001057600080fd5b5060405161182038038061182083398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b60805161178e6100926000396000818161057101526110c2015261178e6000f3fe608060405234801561001057600080fd5b50600436106101635760003560e01c80636352211e116100ce578063a22cb46511610087578063a22cb46514610300578063a350ed481461030e578063a5f2f66714610321578063a972985e1461037a578063b88d4fde1461038d578063c87b56dd1461039b578063e985e9c5146103ae57600080fd5b80636352211e1461027d5780636c0360eb146102a657806370a08231146102ae5780638129fc1c146102c15780639017a13c146102c957806395d89b41146102dc57600080fd5b80632f745c59116101205780632f745c59146102295780633da8bfc91461023c5780633de2d9721461024457806342842e0e1461021b5780634f6ccce71461025757806355f804b31461026a57600080fd5b806301ffc9a71461016857806306fdde0314610190578063081812fc146101c9578063095ea7b3146101f457806318160ddd1461020957806323b872dd1461021b575b600080fd5b61017b6101763660046111bf565b6103bc565b60405190151581526020015b60405180910390f35b6040805180820190915260148152730476f6c6466696e6368204d656d626572736869760641b60208201525b604051610187919061120d565b6101dc6101d7366004611240565b61040e565b6040516001600160a01b039091168152602001610187565b61020761020236600461126e565b610429565b005b6005545b604051908152602001610187565b61020761020236600461129a565b61020d61023736600461126e565b610442565b61020d6104a1565b61020d6102523660046112db565b6104b3565b61020d610265366004611240565b610537565b610207610278366004611341565b61056c565b6101dc61028b366004611240565b6000908152600360205260409020546001600160a01b031690565b6101bc610612565b61020d6102bc3660046112db565b6106a0565b6102076106d0565b61020d6102d7366004611240565b610793565b60408051808201909152600881526723a326a2a6a122a960c11b60208201526101bc565b610207610202366004611383565b61020d61031c3660046113c1565b610803565b61033461032f3660046112db565b610a43565b604051610187919081516001600160a01b031681526020808301519082015260408083015190820152606080830151908201526080918201519181019190915260a00190565b6102076103883660046112db565b610ade565b6102076102023660046113f6565b6101bc6103a9366004611240565b610b16565b61017b6101d7366004611469565b60006001600160e01b031982166380ac58cd60e01b14806103ed57506001600160e01b0319821663780e9d6360e01b145b8061040857506001600160e01b031982166301ffc9a760e01b145b92915050565b6000604051631a850ed760e31b815260040160405180910390fd5b604051631a850ed760e31b815260040160405180910390fd5b6001600160a01b03821660009081526004602052604081205480820361047b576040516334b5d06d60e21b815260040160405180910390fd5b821561049a57604051632316143b60e11b815260040160405180910390fd5b9392505050565b60006104ae6102d7610b94565b905090565b6001600160a01b0380821660009081526004602081815260408084205484526003808352818520825160a08101845281549097168752600181015493870193909352600283015491860191909152810154606085015201546080830181905290919061051d610b94565b111561052d576040015192915050565b6020015192915050565b600061054260055490565b821061056157604051633f7ee38f60e11b815260040160405180910390fd5b6104088260016114ad565b61059e7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610b9f565b604051632a6e138b60e11b81523060048201523360248201526001600160a01b0391909116906354dc27169060440160006040518083038186803b1580156105e557600080fd5b505afa1580156105f9573d6000803e3d6000fd5b506006925061060d9150839050848361155e565b505050565b6006805461061f906114d6565b80601f016020809104026020016040519081016040528092919081815260200182805461064b906114d6565b80156106985780601f1061066d57610100808354040283529160200191610698565b820191906000526020600020905b81548152906001019060200180831161067b57829003601f168201915b505050505081565b6001600160a01b0381166000908152600460205260408120546106c45760006106c7565b60015b60ff1692915050565b600054610100900460ff16806106e9575060005460ff16155b6107515760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b600054610100900460ff16158015610773576000805461ffff19166101011790555b61077b610b94565b6002558015610790576000805461ff00191690555b50565b600061079d610c8f565b8211156107bd57604051630bb92ffd60e21b815260040160405180910390fd5b6002548211156107f0576001600060025460016107da91906114ad565b8152602001908152602001600020549050919050565b5060009081526001602052604090205490565b60007f037db0999be9065c5f1306135b75a6ae2af104ed9f0d72669950e21e87a33f766108308133610ca4565b8383101561085b576040516368caeba760e01b81526004810185905260248101849052604401610748565b600061086686610d03565b905061087186610e02565b6000818152600360208181526040808420815160a08101835281546001600160a01b031681526001808301805483870190815260028501805496850196909652848801546060850152600490940154608084015288885295909452938a905590889055519192889291906108e3610b94565b8152602001908152602001600020546108fc919061161f565b61090691906114ad565b60016000610912610b94565b81526020019081526020016000208190555084816040015160016000610936610c8f565b81526020019081526020016000205461094f919061161f565b61095991906114ad565b60016000610965610c8f565b815260200190815260200160002081905550866001600160a01b03167fb5ac373069545fdd5da402c94d10b968168eb2c09893e9a1a1fd087b2b3b34b187876040516109bb929190918252602082015260400190565b60405180910390a27f32ea6f1ec4de7460c440fd872b28a1f5e867728132b4dcba7af1f3dfef775ba2600160006109f0610b94565b81526020019081526020016000205460016000610a0b610c8f565b815260200190815260200160002054604051610a31929190918252602082015260400190565b60405180910390a15095945050505050565b610a7e6040518060a0016040528060006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b506001600160a01b039081166000908152600460208181526040808420548452600380835293819020815160a081018352815490961686526001810154928601929092526002820154908501529182015460608401520154608082015290565b7f037db0999be9065c5f1306135b75a6ae2af104ed9f0d72669950e21e87a33f76610b098133610ca4565b610b1282610e02565b5050565b606081600003610b3c57604051637705f84560e01b815260048101839052602401610748565b600554821115610b6257604051637705f84560e01b815260048101839052602401610748565b6006610b6d83610f9c565b604051602001610b7e929190611632565b6040516020818303038152906040529050919050565b60006104ae426110a5565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0391906116b9565b6001600160a01b0316630c0450127f9a671f6643eb8ac838fd3c79576dd800fe704b35ad30189c60f0f14dab948f2a6040518263ffffffff1660e01b8152600401610c4e91906116d6565b602060405180830381865afa158015610c6b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061040891906116b9565b6000610c99610b94565b6104ae9060016114ad565b6001600160a01b038116610ccb5760405163d92e233d60e01b815260040160405180910390fd5b610cd582826110b4565b610b125760405163889a56bb60e01b81523060048201526001600160a01b0382166024820152604401610748565b60006001600160a01b038216610d2c57604051630a64406560e11b815260040160405180910390fd5b6001600160a01b0382166000908152600460205260409020548015610d515792915050565b60058054906000610d61836116eb565b9091555050600554600081815260036020819052604090912080546001600160a01b0319166001600160a01b038716178155429101559050610da1610b94565b60008281526003602090815260408083206004908101949094556001600160a01b0387168084529390915280822084905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a492915050565b6000610e0c610b94565b9050600254811115610e94576000600160006002546001610e2d91906114ad565b815260200190815260200160002054905060006002546002610e4f91906114ad565b90505b610e5d8360016114ad565b8111610e8657600081815260016020526040902082905580610e7e816116eb565b915050610e52565b50610e8f610b94565b600255505b6001600160a01b0382166000908152600460205260409020548015610f4457600081815260036020818152604092839020835160a08101855281546001600160a01b0316815260018201549281019290925260028101549382019390935290820154606082015260049091015460808201819052831115610f4257604080820151600084815260036020529190912060010155610f2f610b94565b6000838152600360205260409020600401555b505b7fde5ae8a37da230f7df39b8ea385fa1ab48e7caa55f1c25eaaef1ed8690f3699860016000610f71610b94565b815260200190815260200160002054604051610f8f91815260200190565b60405180910390a1505050565b606081600003610fc35750506040805180820190915260018152600360fc1b602082015290565b8160005b8115610fed5780610fd7816116eb565b9150610fe69050600a8361171a565b9150610fc7565b60008167ffffffffffffffff811115611008576110086114c0565b6040519080825280601f01601f191660200182016040528015611032576020820181803683370190505b5090505b841561109d5761104760018361161f565b9150611054600a8661172e565b61105f9060306114ad565b60f81b81838151811061107457611074611742565b60200101906001600160f81b031916908160001a905350611096600a8661171a565b9450611036565b949350505050565b600061040862093a808361171a565b6000816001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa15801561111e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114291906116b9565b6001600160a01b0316630c045012856040518263ffffffff1660e01b815260040161116d91906116d6565b602060405180830381865afa15801561118a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ae91906116b9565b6001600160a01b0316149392505050565b6000602082840312156111d157600080fd5b81356001600160e01b03198116811461049a57600080fd5b60005b838110156112045781810151838201526020016111ec565b50506000910152565b602081526000825180602084015261122c8160408501602087016111e9565b601f01601f19169190910160400192915050565b60006020828403121561125257600080fd5b5035919050565b6001600160a01b038116811461079057600080fd5b6000806040838503121561128157600080fd5b823561128c81611259565b946020939093013593505050565b6000806000606084860312156112af57600080fd5b83356112ba81611259565b925060208401356112ca81611259565b929592945050506040919091013590565b6000602082840312156112ed57600080fd5b813561049a81611259565b60008083601f84011261130a57600080fd5b50813567ffffffffffffffff81111561132257600080fd5b60208301915083602082850101111561133a57600080fd5b9250929050565b6000806020838503121561135457600080fd5b823567ffffffffffffffff81111561136b57600080fd5b611377858286016112f8565b90969095509350505050565b6000806040838503121561139657600080fd5b82356113a181611259565b9150602083013580151581146113b657600080fd5b809150509250929050565b6000806000606084860312156113d657600080fd5b83356113e181611259565b95602085013595506040909401359392505050565b60008060008060006080868803121561140e57600080fd5b853561141981611259565b9450602086013561142981611259565b935060408601359250606086013567ffffffffffffffff81111561144c57600080fd5b611458888289016112f8565b969995985093965092949392505050565b6000806040838503121561147c57600080fd5b823561148781611259565b915060208301356113b681611259565b634e487b7160e01b600052601160045260246000fd5b8082018082111561040857610408611497565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806114ea57607f821691505b60208210810361150a57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561060d57600081815260208120601f850160051c810160208610156115375750805b601f850160051c820191505b8181101561155657828155600101611543565b505050505050565b67ffffffffffffffff831115611576576115766114c0565b61158a8361158483546114d6565b83611510565b6000601f8411600181146115be57600085156115a65750838201355b600019600387901b1c1916600186901b178355611618565b600083815260209020601f19861690835b828110156115ef57868501358255602094850194600190920191016115cf565b508682101561160c5760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b8181038181111561040857610408611497565b6000808454611640816114d6565b60018281168015611658576001811461166d5761169c565b60ff198416875282151583028701945061169c565b8860005260208060002060005b858110156116935781548a82015290840190820161167a565b50505082870194505b5050505083516116b08183602088016111e9565b01949350505050565b6000602082840312156116cb57600080fd5b815161049a81611259565b6001600160e01b031991909116815260200190565b6000600182016116fd576116fd611497565b5060010190565b634e487b7160e01b600052601260045260246000fd5b60008261172957611729611704565b500490565b60008261173d5761173d611704565b500690565b634e487b7160e01b600052603260045260246000fdfea2646970667358221220ef774c7db2d4ff1868e41276f302b861e7826ce08df1640db58558ea4ca4741464736f6c63430008100033000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c83

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101635760003560e01c80636352211e116100ce578063a22cb46511610087578063a22cb46514610300578063a350ed481461030e578063a5f2f66714610321578063a972985e1461037a578063b88d4fde1461038d578063c87b56dd1461039b578063e985e9c5146103ae57600080fd5b80636352211e1461027d5780636c0360eb146102a657806370a08231146102ae5780638129fc1c146102c15780639017a13c146102c957806395d89b41146102dc57600080fd5b80632f745c59116101205780632f745c59146102295780633da8bfc91461023c5780633de2d9721461024457806342842e0e1461021b5780634f6ccce71461025757806355f804b31461026a57600080fd5b806301ffc9a71461016857806306fdde0314610190578063081812fc146101c9578063095ea7b3146101f457806318160ddd1461020957806323b872dd1461021b575b600080fd5b61017b6101763660046111bf565b6103bc565b60405190151581526020015b60405180910390f35b6040805180820190915260148152730476f6c6466696e6368204d656d626572736869760641b60208201525b604051610187919061120d565b6101dc6101d7366004611240565b61040e565b6040516001600160a01b039091168152602001610187565b61020761020236600461126e565b610429565b005b6005545b604051908152602001610187565b61020761020236600461129a565b61020d61023736600461126e565b610442565b61020d6104a1565b61020d6102523660046112db565b6104b3565b61020d610265366004611240565b610537565b610207610278366004611341565b61056c565b6101dc61028b366004611240565b6000908152600360205260409020546001600160a01b031690565b6101bc610612565b61020d6102bc3660046112db565b6106a0565b6102076106d0565b61020d6102d7366004611240565b610793565b60408051808201909152600881526723a326a2a6a122a960c11b60208201526101bc565b610207610202366004611383565b61020d61031c3660046113c1565b610803565b61033461032f3660046112db565b610a43565b604051610187919081516001600160a01b031681526020808301519082015260408083015190820152606080830151908201526080918201519181019190915260a00190565b6102076103883660046112db565b610ade565b6102076102023660046113f6565b6101bc6103a9366004611240565b610b16565b61017b6101d7366004611469565b60006001600160e01b031982166380ac58cd60e01b14806103ed57506001600160e01b0319821663780e9d6360e01b145b8061040857506001600160e01b031982166301ffc9a760e01b145b92915050565b6000604051631a850ed760e31b815260040160405180910390fd5b604051631a850ed760e31b815260040160405180910390fd5b6001600160a01b03821660009081526004602052604081205480820361047b576040516334b5d06d60e21b815260040160405180910390fd5b821561049a57604051632316143b60e11b815260040160405180910390fd5b9392505050565b60006104ae6102d7610b94565b905090565b6001600160a01b0380821660009081526004602081815260408084205484526003808352818520825160a08101845281549097168752600181015493870193909352600283015491860191909152810154606085015201546080830181905290919061051d610b94565b111561052d576040015192915050565b6020015192915050565b600061054260055490565b821061056157604051633f7ee38f60e11b815260040160405180910390fd5b6104088260016114ad565b61059e7f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c836001600160a01b0316610b9f565b604051632a6e138b60e11b81523060048201523360248201526001600160a01b0391909116906354dc27169060440160006040518083038186803b1580156105e557600080fd5b505afa1580156105f9573d6000803e3d6000fd5b506006925061060d9150839050848361155e565b505050565b6006805461061f906114d6565b80601f016020809104026020016040519081016040528092919081815260200182805461064b906114d6565b80156106985780601f1061066d57610100808354040283529160200191610698565b820191906000526020600020905b81548152906001019060200180831161067b57829003601f168201915b505050505081565b6001600160a01b0381166000908152600460205260408120546106c45760006106c7565b60015b60ff1692915050565b600054610100900460ff16806106e9575060005460ff16155b6107515760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b600054610100900460ff16158015610773576000805461ffff19166101011790555b61077b610b94565b6002558015610790576000805461ff00191690555b50565b600061079d610c8f565b8211156107bd57604051630bb92ffd60e21b815260040160405180910390fd5b6002548211156107f0576001600060025460016107da91906114ad565b8152602001908152602001600020549050919050565b5060009081526001602052604090205490565b60007f037db0999be9065c5f1306135b75a6ae2af104ed9f0d72669950e21e87a33f766108308133610ca4565b8383101561085b576040516368caeba760e01b81526004810185905260248101849052604401610748565b600061086686610d03565b905061087186610e02565b6000818152600360208181526040808420815160a08101835281546001600160a01b031681526001808301805483870190815260028501805496850196909652848801546060850152600490940154608084015288885295909452938a905590889055519192889291906108e3610b94565b8152602001908152602001600020546108fc919061161f565b61090691906114ad565b60016000610912610b94565b81526020019081526020016000208190555084816040015160016000610936610c8f565b81526020019081526020016000205461094f919061161f565b61095991906114ad565b60016000610965610c8f565b815260200190815260200160002081905550866001600160a01b03167fb5ac373069545fdd5da402c94d10b968168eb2c09893e9a1a1fd087b2b3b34b187876040516109bb929190918252602082015260400190565b60405180910390a27f32ea6f1ec4de7460c440fd872b28a1f5e867728132b4dcba7af1f3dfef775ba2600160006109f0610b94565b81526020019081526020016000205460016000610a0b610c8f565b815260200190815260200160002054604051610a31929190918252602082015260400190565b60405180910390a15095945050505050565b610a7e6040518060a0016040528060006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b506001600160a01b039081166000908152600460208181526040808420548452600380835293819020815160a081018352815490961686526001810154928601929092526002820154908501529182015460608401520154608082015290565b7f037db0999be9065c5f1306135b75a6ae2af104ed9f0d72669950e21e87a33f76610b098133610ca4565b610b1282610e02565b5050565b606081600003610b3c57604051637705f84560e01b815260048101839052602401610748565b600554821115610b6257604051637705f84560e01b815260048101839052602401610748565b6006610b6d83610f9c565b604051602001610b7e929190611632565b6040516020818303038152906040529050919050565b60006104ae426110a5565b6000816001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0391906116b9565b6001600160a01b0316630c0450127f9a671f6643eb8ac838fd3c79576dd800fe704b35ad30189c60f0f14dab948f2a6040518263ffffffff1660e01b8152600401610c4e91906116d6565b602060405180830381865afa158015610c6b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061040891906116b9565b6000610c99610b94565b6104ae9060016114ad565b6001600160a01b038116610ccb5760405163d92e233d60e01b815260040160405180910390fd5b610cd582826110b4565b610b125760405163889a56bb60e01b81523060048201526001600160a01b0382166024820152604401610748565b60006001600160a01b038216610d2c57604051630a64406560e11b815260040160405180910390fd5b6001600160a01b0382166000908152600460205260409020548015610d515792915050565b60058054906000610d61836116eb565b9091555050600554600081815260036020819052604090912080546001600160a01b0319166001600160a01b038716178155429101559050610da1610b94565b60008281526003602090815260408083206004908101949094556001600160a01b0387168084529390915280822084905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a492915050565b6000610e0c610b94565b9050600254811115610e94576000600160006002546001610e2d91906114ad565b815260200190815260200160002054905060006002546002610e4f91906114ad565b90505b610e5d8360016114ad565b8111610e8657600081815260016020526040902082905580610e7e816116eb565b915050610e52565b50610e8f610b94565b600255505b6001600160a01b0382166000908152600460205260409020548015610f4457600081815260036020818152604092839020835160a08101855281546001600160a01b0316815260018201549281019290925260028101549382019390935290820154606082015260049091015460808201819052831115610f4257604080820151600084815260036020529190912060010155610f2f610b94565b6000838152600360205260409020600401555b505b7fde5ae8a37da230f7df39b8ea385fa1ab48e7caa55f1c25eaaef1ed8690f3699860016000610f71610b94565b815260200190815260200160002054604051610f8f91815260200190565b60405180910390a1505050565b606081600003610fc35750506040805180820190915260018152600360fc1b602082015290565b8160005b8115610fed5780610fd7816116eb565b9150610fe69050600a8361171a565b9150610fc7565b60008167ffffffffffffffff811115611008576110086114c0565b6040519080825280601f01601f191660200182016040528015611032576020820181803683370190505b5090505b841561109d5761104760018361161f565b9150611054600a8661172e565b61105f9060306114ad565b60f81b81838151811061107457611074611742565b60200101906001600160f81b031916908160001a905350611096600a8661171a565b9450611036565b949350505050565b600061040862093a808361171a565b6000816001600160a01b03167f000000000000000000000000d16bc944bf20c86c4ed47ce1a330a18538674c836001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa15801561111e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114291906116b9565b6001600160a01b0316630c045012856040518263ffffffff1660e01b815260040161116d91906116d6565b602060405180830381865afa15801561118a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ae91906116b9565b6001600160a01b0316149392505050565b6000602082840312156111d157600080fd5b81356001600160e01b03198116811461049a57600080fd5b60005b838110156112045781810151838201526020016111ec565b50506000910152565b602081526000825180602084015261122c8160408501602087016111e9565b601f01601f19169190910160400192915050565b60006020828403121561125257600080fd5b5035919050565b6001600160a01b038116811461079057600080fd5b6000806040838503121561128157600080fd5b823561128c81611259565b946020939093013593505050565b6000806000606084860312156112af57600080fd5b83356112ba81611259565b925060208401356112ca81611259565b929592945050506040919091013590565b6000602082840312156112ed57600080fd5b813561049a81611259565b60008083601f84011261130a57600080fd5b50813567ffffffffffffffff81111561132257600080fd5b60208301915083602082850101111561133a57600080fd5b9250929050565b6000806020838503121561135457600080fd5b823567ffffffffffffffff81111561136b57600080fd5b611377858286016112f8565b90969095509350505050565b6000806040838503121561139657600080fd5b82356113a181611259565b9150602083013580151581146113b657600080fd5b809150509250929050565b6000806000606084860312156113d657600080fd5b83356113e181611259565b95602085013595506040909401359392505050565b60008060008060006080868803121561140e57600080fd5b853561141981611259565b9450602086013561142981611259565b935060408601359250606086013567ffffffffffffffff81111561144c57600080fd5b611458888289016112f8565b969995985093965092949392505050565b6000806040838503121561147c57600080fd5b823561148781611259565b915060208301356113b681611259565b634e487b7160e01b600052601160045260246000fd5b8082018082111561040857610408611497565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806114ea57607f821691505b60208210810361150a57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561060d57600081815260208120601f850160051c810160208610156115375750805b601f850160051c820191505b8181101561155657828155600101611543565b505050505050565b67ffffffffffffffff831115611576576115766114c0565b61158a8361158483546114d6565b83611510565b6000601f8411600181146115be57600085156115a65750838201355b600019600387901b1c1916600186901b178355611618565b600083815260209020601f19861690835b828110156115ef57868501358255602094850194600190920191016115cf565b508682101561160c5760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b8181038181111561040857610408611497565b6000808454611640816114d6565b60018281168015611658576001811461166d5761169c565b60ff198416875282151583028701945061169c565b8860005260208060002060005b858110156116935781548a82015290840190820161167a565b50505082870194505b5050505083516116b08183602088016111e9565b01949350505050565b6000602082840312156116cb57600080fd5b815161049a81611259565b6001600160e01b031991909116815260200190565b6000600182016116fd576116fd611497565b5060010190565b634e487b7160e01b600052601260045260246000fd5b60008261172957611729611704565b500490565b60008261173d5761173d611704565b500690565b634e487b7160e01b600052603260045260246000fdfea2646970667358221220ef774c7db2d4ff1868e41276f302b861e7826ce08df1640db58558ea4ca4741464736f6c63430008100033

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.