NftFiBidder

Description:

Non-Fungible Token (NFT) contract following ERC721 standard.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/nft/NftFiBidder.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.20;

import {Ownable} from "../utils/Ownable.sol";
import {INftFiLoan, LoanDataOffer, LoanDataSignature} from "../meta/INftFiLoan.sol";
import {IWETH, IERC721, IERC721Receiver} from "../meta/IToken.sol";

/**
 * @title nft 自动接受NftFi的出价
 *
 */
contract NftFiBidder is Ownable, IERC721Receiver {
    /*
     *设置只允许合约创建者调用
     */
    constructor() Ownable(_msgSender()) {}

    /*
     *  接收原生代币时打印日志
     */
    receive() external payable {}

    fallback() external payable {}

    /***
     *获利失败
     * @param buyAmount 购买花费金额
     * @param sellAmount 出价卖出金额
     */
    error EarnFail(uint256 buyAmount, uint256 sellAmount);

    //nftFi NFT授权给哪个合约
    address private _nftFiOperator = 0x2ae3e46290AdE43593eabd15642eBD67157f5351;

    //nftFi WETH授权给哪个合约
    address private _nftFiRepayOperator =
        0x6730697f33d6D2490029b32899E7865c0d902Ca0;

    //nftFi 借贷router
    address private _nftFiLoanRouter =
        0xB6adEc2ACc851d30d5fB64f3137234BCDCBBad0D;
    address private _wethToken = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    // solhint-disable-next-line func-mutability
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4) {
        return this.onERC721Received.selector;
    }

    /**
     *   提现NFT
     *   @param ca NFT合约地址
     *   @param tokenIds 出价接受哪些nft tokenId
     *
     */
    function withdrawNFT(address ca, uint256[] calldata tokenIds)
        external
        onlyOwner
    {
        for (uint256 i = 0; i < tokenIds.length; i++) {
            // 转移NFT
            IERC721(ca).safeTransferFrom(address(this), owner(), tokenIds[i]);
        }
    }

    /**
     *   提现WETH,将合约中的weth提现到owner
     *
     */
    function withdrawWETH() public onlyOwner {
        IWETH _weth = IWETH(_wethToken);
        uint256 _wethAmount = _weth.balanceOf(address(this));
        //3.判断获利金额是否大于购买,小于就是亏损了,直接退出
        if (_wethAmount > 0) {
            //将weth转换为eth
            _weth.withdraw(_wethAmount);
            //6.余额提现到管理员账户
            payable(owner()).transfer(address(this).balance);
        }
    }

    /**
     *   批量偿还,提现NFT
     *   @param ca NFT合约地址
     *   @param loadIds 偿还的nft 对应的loanId
     *   @param tokenIds 偿还的nft tokenIds
     */
    function batchRepay(
        address ca,
        uint32[] calldata loadIds,
        uint256[] calldata tokenIds
    ) external payable onlyOwner {
        require(msg.value > 0, "Amount must be greater than 0");

        //1.传入要偿还的eth转换weth,剩余的在结束后会返还账户
        IWETH _weth = IWETH(_wethToken);
        _weth.deposit{value: msg.value}();

        //2.将weth授权给NftFi合约
        if (_weth.allowance(address(this), _nftFiRepayOperator) == 0) {
            _weth.approve(_nftFiRepayOperator, type(uint256).max);
        }

        //3.偿还
        INftFiLoan _loan = INftFiLoan(_nftFiLoanRouter);
        IERC721 nft = IERC721(ca);
        for (uint256 i = 0; i < tokenIds.length; i++) {
            _loan.payBackLoan(loadIds[i]);
            //4.将nft token转移到合约内身上对应的
            nft.safeTransferFrom(address(this), owner(), tokenIds[i]);
        }
        //5.提现剩余的WETH
        withdrawWETH();
    }

    /*
     *  批量提接受出价。注意:提前必须给合约授权NFT的提取权限
     *   @param tokenIds 出价接受哪些nft tokenId
     *   @param buyAmount 购买一共花费了多少金额,出价获利结果必须大于该值,否则相当于亏钱了
     *   @param toCoinbase 给节点支付多少小费(优先费))
     */
    function batchAcceptOffer(
        LoanDataOffer memory offer,
        LoanDataSignature memory signature,
        uint256[] calldata tokenIds,
        uint256 buyAmount,
        uint256 toCoinbase
    ) external onlyOwner {
        IERC721 nft = IERC721(offer.nftCollateralContract);
        //检查授权,没授权要给nftFi合约授权
        if (!nft.isApprovedForAll(address(this), _nftFiOperator)) {
            nft.setApprovalForAll(_nftFiOperator, true);
        }
        INftFiLoan _loan = INftFiLoan(_nftFiLoanRouter);
        for (uint256 i = 0; i < tokenIds.length; i++) {
            //1.将用户身上对应的nft token转移到合约内
            nft.safeTransferFrom(owner(), address(this), tokenIds[i]);
            //指定要接受出价的tokenID,并且接受出价
            offer.nftCollateralId = tokenIds[i];
            //2.接受出价
            _loan.acceptCollectionOffer(offer, signature);
        }

        IWETH _weth = IWETH(_wethToken);
        uint256 _offerGetAmount = _weth.balanceOf(address(this));

        //3.判断获利金额是否大于购买,小于就是亏损了,直接退出
        if (buyAmount >= _offerGetAmount) {
            revert EarnFail(buyAmount, _offerGetAmount);
        }

        //4.将收到的weth转换为eth
        _weth.withdraw(_offerGetAmount);
        //5.支付小费给节点
        if (toCoinbase > 0) {
            block.coinbase.transfer(toCoinbase);
        }
        //6.余额提现到管理员账户
        payable(owner()).transfer(address(this).balance);
    }
}
"
    },
    "contracts/meta/IToken.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity >=0.8.2;

/**
 * @dev Interface of the ERC20 standard as defined in the native wrap token.
 */
interface IWETH {
    function deposit() external payable;

    function withdraw(uint256) external;

    function balanceOf(address account) external view returns (uint256);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 value) external returns (bool);
}

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of 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 value) 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 Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value
    ) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens 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 value) external returns (bool);
}

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

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

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

interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}
"
    },
    "contracts/meta/INftFiLoan.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
// !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.8.0. SEE SOURCE BELOW. !!
pragma solidity >=0.8.20;

struct LoanDataOffer {
    uint256 loanPrincipalAmount;
    uint256 maximumRepaymentAmount;
    uint256 nftCollateralId;
    address nftCollateralContract;
    uint32 loanDuration;
    address loanERC20Denomination;
    bool isProRata;
    uint256 originationFee;
}

struct LoanDataSignature {
    uint256 nonce;
    uint256 expiry;
    address signer;
    bytes signature;
}

interface INftFiLoan {
    function acceptCollectionOffer(
        LoanDataOffer memory _offer,
        LoanDataSignature memory _signature
    ) external returns (uint32);

    function payBackLoan(uint32 _loanId) external;
}
"
    },
    "contracts/utils/Ownable.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2;

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

/**
 * Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * The caller account is not authorized to perform an operation.
     */
    error UnauthorizedAccount(address account);

    /**
     * Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        _owner = initialOwner;
    }

    /**
     * Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert UnauthorizedAccount(_msgSender());
        }
    }
}
"
    },
    "contracts/utils/Context.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity >=0.8.2;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    // function _msgData() internal view virtual returns (bytes calldata) {
    //     return msg.data;
    // }
}
"
    }
  },
  "settings": {
    "viaIR": true,
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "remappings": []
  }
}}

Tags:
ERC20, ERC721, NFT, Non-Fungible, Factory|addr:0x7b9d130ca223a05dd688283a102014f90d46f9e8|verified:true|block:23732651|tx:0x67bec78d72e81c47cad9227ef6808ad46e536743bc6db3e8b5036a292b415b35|first_check:1762349536

Submitted on: 2025-11-05 14:32:18

Comments

Log in to comment.

No comments yet.