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": []
}
}}
Submitted on: 2025-11-05 14:32:20
Comments
Log in to comment.
No comments yet.