NFTFI Collection contract
DirectLoanFixedCollectionOffer.sol

https://etherscan.io/address/0xE52Cec0E90115AbeB3304BaA36bc2655731f7934#code (opens in a new tab)

// SPDX-License-Identifier: BUSL-1.1
 
pragma solidity 0.8.4;
 
import "./DirectLoanFixedOffer.sol";
 
/**
 * @title  DirectLoanFixedCollectionOffer
 * @author NFTfi
 * @notice Main contract for NFTfi Direct Loans Fixed Collection Type.
 * This contract manages the ability to create reoccurring NFT-backed
 * peer-to-peer loans of type Fixed (agreed to be a fixed-repayment loan) where the borrower pays the
 * maximumRepaymentAmount regardless of whether they repay early or not.
 * In collection offer type loans the collateral can be any one item (id) of a given NFT collection (contract).
 *
 * To commence an NFT-backed loan:
 *
 * The borrower accepts a lender's offer by calling `acceptOffer`.
 *   1. the borrower calls nftContract.approveAll(NFTfi), approving the NFTfi contract to move their NFT's on their
 * be1alf.
 *   2. the lender calls erc20Contract.approve(NFTfi), allowing NFTfi to move the lender's ERC20 tokens on their
 * behalf.
 *   3. the lender signs a reusable off-chain message, proposing its collection offer terms.
 *   4. the borrower calls `acceptOffer` to accept these terms and enter into the loan. The NFT is stored in
 * the contract, the borrower receives the loan principal in the specified ERC20 currency, the lender receives an
 * NFTfi promissory note (in ERC721 form) that represents the rights to either the principal-plus-interest, or the
 * underlying NFT collateral if the borrower does not pay back in time, and the borrower receives obligation receipt
 * (in ERC721 form) that gives them the right to pay back the loan and get the collateral back.
 *  5. another borrower can also repeat step 4 until the original lender cancels or their
 * wallet runs out of funds with allowance to the contract
 *
 * The lender can freely transfer and trade this ERC721 promissory note as they wish, with the knowledge that
 * transferring the ERC721 promissory note tranfsers the rights to principal-plus-interest and/or collateral, and that
 * they will no longer have a claim on the loan. The ERC721 promissory note itself represents that claim.
 *
 * The borrower can freely transfer and trade this ERC721 obligaiton receipt as they wish, with the knowledge that
 * transferring the ERC721 obligaiton receipt tranfsers the rights right to pay back the loan and get the collateral
 * back.
 *
 *
 * A loan may end in one of two ways:
 * - First, a borrower may call NFTfi.payBackLoan() and pay back the loan plus interest at any time, in which case they
 * receive their NFT back in the same transaction.
 * - Second, if the loan's duration has passed and the loan has not been paid back yet, a lender can call
 * NFTfi.liquidateOverdueLoan(), in which case they receive the underlying NFT collateral and forfeit the rights to the
 * principal-plus-interest, which the borrower now keeps.
 */
contract DirectLoanFixedCollectionOffer is DirectLoanFixedOffer {
    /* *********** */
    /* CONSTRUCTOR */
    /* *********** */
 
    /**
     * @dev Sets `hub` and permitted erc20-s
     *
     * @param _admin - Initial admin of this contract.
     * @param  _nftfiHub - NFTfiHub address
     * @param  _permittedErc20s - list of permitted ERC20 token contract addresses
     */
    constructor(
        address _admin,
        address _nftfiHub,
        address[] memory _permittedErc20s
    ) DirectLoanFixedOffer(_admin, _nftfiHub, _permittedErc20s) {
        // solhint-disable-previous-line no-empty-blocks
    }
 
    /* ******************* */
    /* READ-ONLY FUNCTIONS */
    /* ******************* */
 
    /**
     * @notice This function returns a bytes32 value identifying the loan type for the coordinator
     */
    // all caps, because used to be a constant storage and the interface should be the same
    // solhint-disable-next-line func-name-mixedcase
    function LOAN_TYPE() public pure override returns (bytes32) {
        return bytes32("DIRECT_LOAN_FIXED_COLLECTION");
    }
 
    /* ****************** */
    /* INTERNAL FUNCTIONS */
    /* ****************** */
 
    /**
     * @notice This function is called by the borrower when accepting a lender's offer to begin a loan.
     *
     * @param _loanTerms - The main Loan Terms struct. This data is saved upon loan creation on loanIdToLoan.
     * @param _loanExtras - The main Loan Terms struct. This data is saved upon loan creation on loanIdToLoanExtras.
     * @param _offer - The offer made by the lender.
     * @param _signature - The components of the lender's signature.
     */
    function _acceptOffer(
        LoanTerms memory _loanTerms,
        LoanExtras memory _loanExtras,
        Offer memory _offer,
        Signature memory _signature
    ) internal override {
        // still checking the nonce for possible cancellations
        require(!_nonceHasBeenUsedForUser[_signature.signer][_signature.nonce], "Lender nonce invalid");
        // Note that we are not invalidating the nonce as part of acceptOffer (as is the case for loan types in general)
        // since the nonce that the lender signed with remains valid for all loans for the collection offer
 
        Offer memory offerToCheck = _offer;
 
        offerToCheck.nftCollateralId = 0;
 
        require(NFTfiSigningUtils.isValidLenderSignature(offerToCheck, _signature), "Lender signature is invalid");
 
        address bundle = hub.getContract(ContractKeys.NFTFI_BUNDLER);
        require(_loanTerms.nftCollateralContract != bundle, "Collateral cannot be bundle");
 
        uint32 loanId = _createLoan(
            LOAN_TYPE(),
            _loanTerms,
            _loanExtras,
            msg.sender,
            _signature.signer,
            _offer.referrer
        );
 
        // Emit an event with all relevant details from this transaction.
        emit LoanStarted(loanId, msg.sender, _signature.signer, _loanTerms, _loanExtras);
    }
}