ArcadeRefinancingAdapter

Description:

Decentralized Finance (DeFi) protocol contract providing Non-Fungible, Factory functionality.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}
"
    },
    "@openzeppelin/contracts/token/ERC721/IERC721.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the 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 have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

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

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

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

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

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}
"
    },
    "@openzeppelin/contracts/utils/introspection/IERC165.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

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 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);
}
"
    },
    "contracts/refinancing/refinancingAdapters/arcade/IArcadeLoanCore.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

/**
 * @title IArcadeLoanCore
 * @author
 * @dev
 */
interface IArcadeLoanCore {
    /**
     * @dev Enum describing the current state of a loan.
     * State change flow:
     * Created -> Active -> Repaid
     *                   -> Defaulted
     */
    enum LoanState {
        // We need a default that is not 'Created' - this is the zero value
        DUMMY_DO_NOT_USE,
        // The loan has been initialized, funds have been delivered to the borrower and the collateral is held.
        Active,
        // The loan has been repaid, and the collateral has been returned to the borrower. This is a terminal state.
        Repaid,
        // The loan was delinquent and collateral claimed by the lender. This is a terminal state.
        Defaulted
    }

    /**
     * @dev The raw terms of a loan.
     */
    struct LoanTerms {
        // Interest expressed as a rate, unlike V1 gross value.
        // Input conversion: 0.01% = (1 * 10**18) ,  10.00% = (1000 * 10**18)
        // This represents the rate over the lifetime of the loan, not APR.
        // 0.01% is the minimum interest rate allowed by the protocol.
        uint256 proratedInterestRate;
        /// @dev Full-slot variables
        // The amount of principal in terms of the payableCurrency.
        uint256 principal;
        // The token ID of the address holding the collateral.
        /// @dev Can be an AssetVault, or the NFT contract for unbundled collateral
        address collateralAddress;
        /// @dev Packed variables
        // The number of seconds representing relative due date of the loan.
        /// @dev Max is 94,608,000, fits in 96 bits
        uint96 durationSecs;
        // The token ID of the collateral.
        uint256 collateralId;
        // The payable currency for the loan principal and interest.
        address payableCurrency;
        // Timestamp for when signature for terms expires
        uint96 deadline;
        // Affiliate code used to start the loan.
        bytes32 affiliateCode;
    }

    /**
     * @dev Predicate for item-based verifications
     */
    struct Predicate {
        // The encoded predicate, to decoded and parsed by the verifier contract.
        bytes data;
        // The verifier contract.
        address verifier;
    }

    /**
     * @dev Snapshot of lending fees at the time of loan creation.
     */
    struct FeeSnapshot {
        // The fee taken when lender claims defaulted collateral.
        uint16 lenderDefaultFee;
        // The fee taken from the borrower's interest repayment.
        uint16 lenderInterestFee;
        // The fee taken from the borrower's principal repayment.
        uint16 lenderPrincipalFee;
    }

    /**
     * @dev The data of a loan. This is stored once the loan is Active
     */
    struct LoanData {
        /// @dev Packed variables
        // The current state of the loan.
        LoanState state;
        // Start date of the loan, using block.timestamp.
        uint160 startDate;
        /// @dev Full-slot variables
        // The raw terms of the loan.
        LoanTerms terms;
        // Record of lending fees at the time of loan creation.
        FeeSnapshot feeSnapshot;
    }

    function borrowerNote() external view returns (address);
    function getLoan(uint256 loanId) external view returns (LoanData memory loanData);
}
"
    },
    "contracts/refinancing/refinancingAdapters/arcade/InterestCalculator.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity 0.8.19;

/**
 * @title InterestCalculator
 * @author Non-Fungible Technologies, Inc.
 *
 * Interface for calculating the interest amount
 * given an interest rate and principal amount. Assumes
 * that the interestRate is already expressed over the desired
 * time period.
 */
abstract contract InterestCalculator {
    // ============================================ STATE ==============================================

    /// @dev The units of precision equal to the minimum interest of 1 basis point.
    uint256 public constant INTEREST_RATE_DENOMINATOR = 1e18;

    uint256 public constant BASIS_POINTS_DENOMINATOR = 1e4;

    // ======================================== CALCULATIONS ===========================================

    /**
     * @notice Calculate the interest due over a full term.
     *
     * @dev Interest and principal must be entered with 18 units of
     *      precision from the basis point unit (e.g. 1e18 == 0.01%)
     *
     * @param principal                             Principal amount in the loan terms.
     * @param proratedInterestRate                  Interest rate in the loan terms, prorated over loan duration.
     *
     * @return interest                             The amount of interest due.
     */
    function getInterestAmount(uint256 principal, uint256 proratedInterestRate) public pure returns (uint256) {
        return (principal * proratedInterestRate) / (INTEREST_RATE_DENOMINATOR * BASIS_POINTS_DENOMINATOR);
    }
}
"
    },
    "contracts/refinancing/refinancingAdapters/arcade/IRepaymentController.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

/**
 * @title IRepaymentController
 * @author
 * @dev
 */
interface IRepaymentController {
    function repay(uint256 loanId) external;
}
"
    },
    "contracts/refinancing/refinancingAdapters/ArcadeRefinancingAdapter.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.19;

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

import {IArcadeLoanCore} from "./arcade/IArcadeLoanCore.sol";
import {IRepaymentController} from "./arcade/IRepaymentController.sol";
import {InterestCalculator} from "./arcade/InterestCalculator.sol";

/**
 * @title ArcadeRefinancingAdapter
 * @author NFTfi
 * @dev This contract is an implementation of the IRefinancingAdapter for the Arcade platform.
 * It handles operations related to refinancing Arcade loans such as transferring the borrower role,
 * paying off loans, and retrieving loan and collateral details.
 */
contract ArcadeRefinancingAdapter is IRefinancingAdapter, InterestCalculator {
    address public constant repaymentController = 0x74241e1A9c021643289476426B9B70229Ab40D53;

    error transferBorrowerRoleFailed();

    /**
     * @dev Gets the address of the borrower for a specific Arcade loan.
     * @param _loanContract The address of the contract containing the Arcade loan.
     * @param _loanIdentifier The unique identifier for the Arcade loan.
     * @return The address of the borrower.
     */
    function getBorrowerAddress(
        address _loanContract,
        uint256 _loanIdentifier,
        bytes calldata
    ) external view override returns (address) {
        return IERC721(IArcadeLoanCore(_loanContract).borrowerNote()).ownerOf(_loanIdentifier);
    }

    /**
     * @dev Transfers the borrower role to this contract for a specific Arcade loan.
     * @param _loanContract The address of the contract containing the Arcade loan.
     * @param _loanIdentifier The unique identifier for the Arcade loan.
     * @return A boolean value indicating whether the operation was successful.
     */
    function transferBorrowerRole(
        address _loanContract,
        uint256 _loanIdentifier,
        bytes calldata
    ) external override returns (bool) {
        IERC721 borrowerNote = IERC721(IArcadeLoanCore(_loanContract).borrowerNote());
        address borrower = borrowerNote.ownerOf(_loanIdentifier);
        borrowerNote.transferFrom(borrower, address(this), _loanIdentifier);
        if (borrowerNote.ownerOf(_loanIdentifier) != address(this)) revert transferBorrowerRoleFailed();
        return (true);
    }

    /**
     * @dev Pays off an Arcade loan with a specified amount of a specified token.
     * @param _loanContract The address of the contract containing the Arcade loan.
     * @param _loanIdentifier The unique identifier for the Arcade loan.
     * @param _payBackToken The token used to pay back the Arcade loan.
     * @param _payBackAmount The amount of tokens used to pay back the Arcade loan.
     * @return A boolean value indicating whether the operation was successful.
     */
    function payOffRefinancable(
        address _loanContract,
        uint256 _loanIdentifier,
        address _payBackToken,
        uint256 _payBackAmount,
        bytes calldata
    ) external override returns (bool) {
        IERC20(_payBackToken).approve(_loanContract, _payBackAmount);
        IRepaymentController(repaymentController).repay(_loanIdentifier);
        return (true);
    }

    /**
     * @dev Gets the collateral information for a specific Arcade loan.
     * @param _loanContract The address of the contract containing the Arcade loan.
     * @param _loanIdentifier The unique identifier for the Arcade loan.
     * @return nftCollateralContract nftCollateralId
     * The address of the collateral token contract and the ID of the collateral.
     */
    function getCollateral(
        address _loanContract,
        uint256 _loanIdentifier,
        bytes calldata
    ) external view override returns (address, uint256) {
        // get loan data
        IArcadeLoanCore.LoanData memory data = IArcadeLoanCore(_loanContract).getLoan(_loanIdentifier);
        return (data.terms.collateralAddress, data.terms.collateralId);
    }

    /**
     * @dev Retrieves the loan coordinator from a specific Arcade loan contract.
     * @param _loanContract The address of the contract containing the Arcade loan.
     * @return  The loan coordinator contract.
     */

    /**
     * @dev Gets the collateral information for a specific Arcade loan.
     * @param _loanContract The address of the contract containing the Arcade loan.
     * @param _loanIdentifier The unique identifier for the Arcade loan.
     * @return loanERC20Denomination maximumRepaymentAmount
     *  The address of the payoff token and the required payoff amount.
     */
    function getPayoffDetails(
        address _loanContract,
        uint256 _loanIdentifier,
        bytes calldata
    ) external view override returns (address, uint256) {
        // get loan data
        IArcadeLoanCore.LoanData memory data = IArcadeLoanCore(_loanContract).getLoan(_loanIdentifier);
        // get interest amount due
        uint256 interestAmount = getInterestAmount(data.terms.principal, data.terms.proratedInterestRate);
        uint256 payOffAmount = interestAmount + data.terms.principal;
        return (data.terms.payableCurrency, payOffAmount);
    }
}
"
    },
    "contracts/refinancing/refinancingAdapters/IRefinancingAdapter.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.19;

/**
 * @title IRefinancingAdapter
 * @author NFTfi
 *
 * @dev This is the interface for Refinancing Adapters. It provides several methods for managing and retrieving
 * information about contracts that are eligible for refinancing.
 *
 * Adapters should implement this interface
 */
interface IRefinancingAdapter {
    /**
     * @dev Returns the borrower's address for a specific refinancable
     *
     * @param _refinanceableContract Address of the contract containing the refinanceable
     * @param _refinancableIdentifier Unique identifier for the refinanceable.
     *
     * @return Address of the borrower.
     */
    function getBorrowerAddress(
        address _refinanceableContract,
        uint256 _refinancableIdentifier,
        bytes memory _extraData
    ) external returns (address);

    /**
     * @dev Transfers the role of borrower to refinancing contract for a specific refinanceable.
     *
     * @param _refinanceableContract Address of the contract containing the refinanceable
     * @param _refinancableIdentifier Unique identifier for the loan.
     *
     * @return True if the operation was successful.
     */
    function transferBorrowerRole(
        address _refinanceableContract,
        uint256 _refinancableIdentifier,
        bytes memory _extraData
    ) external returns (bool);

    /**
     * @dev Pays off a refinanceable with a specified amount of a specified token.
     *
     * @param _refinanceableContract Address of the contract containing the refinanceable
     * @param _refinancableIdentifier Unique identifier for the refinanceable.
     * @param _payBackToken Token used to pay back the refinanceable.
     * @param _payBackAmount Amount of tokens used to pay back the refinanceable.
     *
     * @return True if the operation was successful.
     */
    function payOffRefinancable(
        address _refinanceableContract,
        uint256 _refinancableIdentifier,
        address _payBackToken,
        uint256 _payBackAmount,
        bytes memory _extraData
    ) external returns (bool);

    /**
     * @dev Returns the collateral information for a specific refinancable.
     *
     * @param _refinanceableContract Address of the contract containing the refinanceable
     * @param _refinancableIdentifier Unique identifier for the refinanceable.
     *
     * @return The address of the collateral token and the amount of collateral.
     */
    function getCollateral(
        address _refinanceableContract,
        uint256 _refinancableIdentifier,
        bytes memory _extraData
    ) external view returns (address, uint256);

    /**
     * @dev Returns the payoff details for a specific refinancable.
     *
     * @param _refinanceableContract Address of the contract containing the refinanceable
     * @param _refinancableIdentifier Unique identifier for the loan.
     *
     * @return The address of the payoff token and the required payoff amount.
     */
    function getPayoffDetails(
        address _refinanceableContract,
        uint256 _refinancableIdentifier,
        bytes memory _extraData
    ) external view returns (address, uint256);
}
"
    }
  },
  "settings": {
    "metadata": {
      "bytecodeHash": "none",
      "useLiteralContent": true
    },
    "optimizer": {
      "enabled": true,
      "runs": 900
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    }
  }
}}

Tags:
ERC20, ERC721, ERC165, DeFi, Non-Fungible, Factory|addr:0xb1bc99a61b262d8db88288dbdf6414f7825b7b35|verified:true|block:23496524|tx:0x67e085f059241972e1bdbd6017da94b3415a7353319703fe9aeb50233b46be28|first_check:1759496532

Submitted on: 2025-10-03 15:02:13

Comments

Log in to comment.

No comments yet.