编译器
0.8.24+commit.e11b9ed9
文件 1 的 16:Address.sol
pragma solidity ^0.8.20;
library Address {
error AddressInsufficientBalance(address account);
error AddressEmptyCode(address target);
error FailedInnerCall();
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
文件 2 的 16:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 3 的 16:ICryptoVault.sol
pragma solidity ^0.8.24;
interface ICryptoVault {
function depositNftToEscrowAndERC20ToBorrower(
address nftContract,
uint256 loanId,
uint256[] calldata tokenIds,
address currencyERC20,
address lender,
address borrower,
uint256 loanAmount
) external;
function withdrawNftFromEscrowAndERC20ToLender(
address nftContract,
uint256 loanId,
uint256[] calldata tokenIds,
address borrower,
address lender,
uint256 rePaymentAmount,
uint256 computeAdminFee,
address currencyERC20,
address adminWallet
) external;
function withdrawNftFromEscrow(
address nftContract,
uint256 loanId,
uint256[] calldata tokenIds,
address lender
) external;
function AssetStoredOwner(
address tokenAddress,
uint256 tokenId
) external view returns (address);
}
文件 4 的 16:IERC165.sol
pragma solidity ^0.8.20;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 5 的 16:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 6 的 16:IERC20Permit.sol
pragma solidity ^0.8.20;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 7 的 16:IERC721.sol
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
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);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 8 的 16:ILoanManager.sol
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface ILoanManager {
struct Loan {
address nftContract;
uint256[] tokenIds;
address borrower;
address lender;
uint256 loanAmount;
uint256 aprBasisPoints;
uint256 loanDuration;
address currencyERC20;
uint256 loanInitialTime;
uint256 lenderReceiptId;
uint256 borrowerReceiptId;
bool isPaid;
bool isDefault;
bool isApproved;
}
struct LoanData {
address nftContract;
uint256[] tokenIds;
address borrower;
address lender;
uint256 loanAmount;
uint256 aprBasisPoints;
uint256 loanDuration;
address currencyERC20;
}
function createLoan(
LoanData calldata loanData,
uint256 lenderReceiptId,
uint256 borrowerReceiptId,
uint256 _nonce
) external;
function updateLoan(Loan memory loan, uint256 loanId) external returns(bool);
function getLoan(
address _contract,
uint256[] calldata _tokenIds,
address _borrower,
uint256 _nonce
) external view returns (Loan memory loan, uint256 loanId);
function updateIsPaid(uint256 loanId, bool state) external;
function updateIsDefault(uint256 loanId, bool state) external;
function updateIsApproved(uint256 loanId, bool state) external;
function setLoanId(uint256 loanReceiptID, uint256 loanId) external;
function getPayoffAmount(uint256 loanId) external view returns(uint256, uint256);
function getLoanById(uint256 loanId) external view returns (Loan memory loan);
function getLoanId(uint256 loanReceiptID) external view returns(uint256 loanID);
}
文件 9 的 16:IReceipts.sol
pragma solidity ^0.8.24;
interface ReceiptInterface {
function open() external view returns (bool);
function burnReceipt(uint256 tokenId) external;
function ownerOf(uint256) external view returns (address);
function tokenExist(uint256) external view returns (bool);
function generateReceipt(uint256 loanId, address holder) external returns (uint256);
function getReceiptId(uint256 loanId) external view returns(uint256 holderReceiptId, address holderAddress);
}
文件 10 的 16:Initializable.sol
pragma solidity ^0.8.20;
abstract contract Initializable {
struct InitializableStorage {
uint64 _initialized;
bool _initializing;
}
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
error InvalidInitialization();
error NotInitializing();
event Initialized(uint64 version);
modifier initializer() {
InitializableStorage storage $ = _getInitializableStorage();
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
modifier reinitializer(uint64 version) {
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
modifier onlyInitializing() {
_checkInitializing();
_;
}
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
function _disableInitializers() internal virtual {
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}
文件 11 的 16:IwhiteListCollection.sol
pragma solidity ^0.8.24;
interface IwhiteListCollection {
function blackListErc20Token(address[] memory _Erc20Addresses) external;
function whiteListErc20Token(address[] memory _Erc20Addresses) external;
function whiteListCollection(address[] memory _collectionAddresses) external;
function blackListCollection(address[] memory _collectionAddresses) external;
function isWhiteListErc20Token(address _Erc20Address) external view returns(bool);
function isWhiteListCollection(address _collectionAddress) external view returns(bool);
}
文件 12 的 16:NettyWorthProxy.sol
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/IwhiteListCollection.sol";
import "./interfaces/ICryptoVault.sol";
import "./interfaces/ILoanManager.sol";
import "./library/SignatureUtils.sol";
import "./interfaces/IReceipts.sol";
contract NettyWorthProxy is ReentrancyGuard, Initializable,Ownable {
using SafeERC20 for IERC20;
using Address for address;
address public vault;
address public loanManager;
address public lenderReceiptContract;
address public borrowerReceiptContract;
address public whiteListContract;
uint256 public adminFeeInBasisPoints = 400;
uint256 private proposeAdminFeeInBasisPoints;
uint256 public constant BPS = 10000;
address public adminWallet;
address private _updateAdminWallet;
ICryptoVault _icryptoVault;
ILoanManager _iloanManager;
ReceiptInterface _ireceiptLender;
ReceiptInterface _ireceiptBorrower;
IwhiteListCollection _iwhiteListCollection;
event LoanRepaid(
uint256 indexed loanId,
address indexed nftContract,
uint256[] tokenIds,
address borrower,
address lender,
uint256 repayment,
address erc20Address,
bool isPaid
);
event LoanForClosed(
uint256 indexed loanId,
address indexed nftContract,
uint256[] tokenIds,
address borrower,
address lender,
bool isDefault
);
event UpdatedAdminFee(uint256 oldAdminFee, uint256 newAdminFee);
event UpdatedAdminWallet(address oldAdminWallet, address newAdminWallet);
constructor() Ownable(msg.sender){}
function proposeAdminWallet(address _adminWallet) public onlyOwner {
require(_adminWallet != address(0), "Invalid Address");
_updateAdminWallet = _adminWallet;
}
function setAdminWallet() public onlyOwner {
adminWallet = _updateAdminWallet;
_updateAdminWallet = address(0);
emit UpdatedAdminWallet(msg.sender,adminWallet);
}
function proposeUpdateAdminFee(uint256 _newAdminFee) public onlyOwner {
require(
_newAdminFee <= 500,
"By definition, basis points cannot exceed 500(5%)."
);
proposeAdminFeeInBasisPoints = _newAdminFee;
}
function updateAdminFee() public onlyOwner {
uint256 oldAdminFee = adminFeeInBasisPoints;
adminFeeInBasisPoints = proposeAdminFeeInBasisPoints;
proposeAdminFeeInBasisPoints = 0;
emit UpdatedAdminFee(oldAdminFee,adminFeeInBasisPoints);
}
function initialize(
address _vault,
address _loanManager,
address _lenderReceiptContract,
address _borrowerReceiptContract,
address _iwhiteListContract,
address _adminWallet
) external initializer {
setVault(_vault);
setLoanManager(_loanManager);
setReceiptContractLender(_lenderReceiptContract);
setReceiptContractBorrower(_borrowerReceiptContract);
setWhiteListContract(_iwhiteListContract);
proposeAdminWallet(_adminWallet);
setAdminWallet();
}
function setWhiteListContract(address _whiteList) public onlyOwner {
require(_whiteList != address(0), "Invalid address");
whiteListContract = _whiteList;
_iwhiteListCollection = IwhiteListCollection(whiteListContract);
}
function setVault(address _vault) public onlyOwner {
require(_vault != address(0), "Invalid address");
vault = _vault;
_icryptoVault = ICryptoVault(_vault);
}
function setLoanManager(address _loanManager) public onlyOwner {
require(_loanManager != address(0), "Invalid address");
loanManager = _loanManager;
_iloanManager = ILoanManager(loanManager);
}
function setReceiptContractLender(address _lenderReceiptContract) public onlyOwner {
require(_lenderReceiptContract != address(0), "Invalid address");
lenderReceiptContract = _lenderReceiptContract;
_ireceiptLender = ReceiptInterface(lenderReceiptContract);
}
function setReceiptContractBorrower(address _borrowerReceiptContract) public onlyOwner {
require(_borrowerReceiptContract != address(0), "Invalid address");
borrowerReceiptContract = _borrowerReceiptContract;
_ireceiptBorrower = ReceiptInterface(borrowerReceiptContract);
}
function acceptLoanRequest(
bytes calldata acceptRequestSignature,
SignatureUtils.LoanRequest calldata loanRequest
)
external
nonReentrant
returns (uint256 receiptIdBorrower, uint256 receiptIdLender)
{
_sanityCheckAcceptOffer(
loanRequest.nftContractAddress,
loanRequest.erc20TokenAddress
);
require(
SignatureUtils._validateRequestLoanSignature(
acceptRequestSignature,
loanRequest
),
"Invalid borrower signature"
);
require(msg.sender != loanRequest.borrower, "Unauthorized sender");
ILoanManager.LoanData memory loandata = ILoanManager.LoanData(
loanRequest.nftContractAddress,
loanRequest.tokenIds,
loanRequest.borrower,
msg.sender,
loanRequest.loanAmount,
loanRequest.aprBasisPoints,
loanRequest.loanDuration + block.timestamp,
loanRequest.erc20TokenAddress);
(receiptIdBorrower, receiptIdLender) = _acceptOffer(
loandata,
loanRequest.nonce
);
return (receiptIdBorrower, receiptIdLender);
}
function acceptLoanOffer(
bytes calldata acceptOfferSignature,
SignatureUtils.LoanOffer calldata loanOffer
)
external
nonReentrant
returns (uint256 receiptIdBorrower, uint256 receiptIdLender)
{
require(msg.sender == loanOffer.borrower, "Unauthorized sender");
_sanityCheckAcceptOffer(
loanOffer.nftContractAddress,
loanOffer.erc20TokenAddress
);
require(
SignatureUtils._validateSignatureApprovalOffer(
acceptOfferSignature,
loanOffer
),
"Invalid lender signature"
);
ILoanManager.LoanData memory loandata = ILoanManager.LoanData(
loanOffer.nftContractAddress,
loanOffer.tokenIds,
loanOffer.borrower,
loanOffer.lender,
loanOffer.loanAmount,
loanOffer.aprBasisPoints,
loanOffer.loanDuration + block.timestamp,
loanOffer.erc20TokenAddress);
(receiptIdBorrower, receiptIdLender) = _acceptOffer(
loandata,
loanOffer.nonce
);
return (receiptIdBorrower, receiptIdLender);
}
function acceptLoanCollectionOffer(
bytes calldata acceptOfferSignature,
SignatureUtils.LoanCollectionOffer calldata loanCollectionOffer,
uint256[] calldata tokenIds
)
external
nonReentrant
returns (uint256 receiptIdBorrower, uint256 receiptIdLender)
{
_sanityCheckAcceptOffer(
loanCollectionOffer.collectionAddress,
loanCollectionOffer.erc20TokenAddress
);
require(
SignatureUtils._validateLoanCollectionOfferSignature(
acceptOfferSignature,
loanCollectionOffer
),
"Invalid lender signature"
);
ILoanManager.LoanData memory loandata = ILoanManager.LoanData(loanCollectionOffer.collectionAddress,
tokenIds,
msg.sender,
loanCollectionOffer.lender,
loanCollectionOffer.loanAmount,
loanCollectionOffer.aprBasisPoints,
loanCollectionOffer.loanDuration + block.timestamp,
loanCollectionOffer.erc20TokenAddress);
(receiptIdBorrower, receiptIdLender) = _acceptOffer(
loandata,
loanCollectionOffer.nonce
);
return (receiptIdBorrower, receiptIdLender);
}
function _acceptOffer(
ILoanManager.LoanData memory loandata,
uint256 nonce
) internal returns (uint256 _receiptIdBorrower, uint256 _receiptIdLender) {
(ILoanManager.Loan memory loan, uint256 _loanId) = _iloanManager.getLoan(
loandata.nftContract,
loandata.tokenIds,
loandata.borrower,
nonce
);
require(!loan.isPaid, "Loan offer is closed");
require(!loan.isApproved, "Loan offer is already approved");
_receiptIdBorrower = _ireceiptBorrower.generateReceipt(
_loanId,
loandata.borrower
);
_receiptIdLender = _ireceiptLender.generateReceipt(
_loanId,
loandata.lender
);
_iloanManager.setLoanId(_receiptIdLender,_loanId);
_iloanManager.createLoan(
loandata,
_receiptIdLender,
_receiptIdBorrower,
nonce
);
_iloanManager.updateIsApproved(_loanId, true);
_deposit(
loandata.nftContract,
_loanId,
loandata.tokenIds,
loandata.currencyERC20,
loandata.lender,
loandata.borrower,
loandata.loanAmount
);
return (_receiptIdBorrower, _receiptIdLender);
}
function _deposit(
address collectionAddress,
uint256 loanid,
uint256[] memory tokenIds,
address erc20TokenAddress,
address lender,
address borrower,
uint256 loanAmount
) internal {
_icryptoVault.depositNftToEscrowAndERC20ToBorrower(
collectionAddress,
loanid,
tokenIds,
erc20TokenAddress,
lender,
borrower,
loanAmount
);
}
function payBackLoan(
uint256 _loanId,
address erc20Token
) external nonReentrant returns (bool) {
ILoanManager.Loan memory loan = _iloanManager.getLoanById(_loanId);
require(loan.currencyERC20 == erc20Token, "Currency Invalid");
(uint256 rePaymentAmount, uint256 interestAmount) = _iloanManager.getPayoffAmount(_loanId);
uint256 computeAdminFee = _computeAdminFee(interestAmount, adminFeeInBasisPoints);
(address currentBorrower, address currentLender) = _sanityCheckPayBack(loan);
_icryptoVault.withdrawNftFromEscrowAndERC20ToLender(
loan.nftContract,
_loanId,
loan.tokenIds,
currentBorrower,
currentLender,
rePaymentAmount,
computeAdminFee,
loan.currencyERC20,
adminWallet
);
_ireceiptLender.burnReceipt(loan.lenderReceiptId);
_ireceiptBorrower.burnReceipt(loan.borrowerReceiptId);
_iloanManager.updateIsPaid(_loanId, true);
emit LoanRepaid(
_loanId,
loan.nftContract,
loan.tokenIds,
currentBorrower,
currentLender,
rePaymentAmount,
loan.currencyERC20,
loan.isPaid
);
return true;
}
function forCloseLoan(uint256 _loanId) external nonReentrant returns (bool){
ILoanManager.Loan memory loan = _iloanManager.getLoanById(_loanId);
require(
block.timestamp > loan.loanDuration,
"User is not default yet::"
);
require(!loan.isPaid, "Loan Paid");
require(!loan.isDefault, "Already Claimed");
uint256 _borrowerReceiptId = loan.borrowerReceiptId;
uint256 _lenderReceiptId = loan.lenderReceiptId;
require(
_ireceiptLender.tokenExist(_lenderReceiptId),
"Receipt does not exist"
);
require(
_ireceiptBorrower.tokenExist(_borrowerReceiptId),
"Receipt does not exist"
);
address lender = _ireceiptLender.ownerOf(_lenderReceiptId);
address borrower = _ireceiptBorrower.ownerOf(_borrowerReceiptId);
require(
lender == msg.sender,
"You are not the lender"
);
_iloanManager.updateIsDefault(_loanId, true);
_icryptoVault.withdrawNftFromEscrow(
loan.nftContract,
_loanId,
loan.tokenIds,
lender
);
_ireceiptLender.burnReceipt(_lenderReceiptId);
_ireceiptBorrower.burnReceipt(_borrowerReceiptId);
emit LoanForClosed(
_loanId,
loan.nftContract,
loan.tokenIds,
borrower,
loan.lender,
loan.isDefault
);
return true;
}
function getLenderReceiptId(uint256 loanId) external view returns(uint256 holderReceiptId, address holderAddress){
require(loanId != 0, "200:ZERO_LoanID");
(holderReceiptId, holderAddress) = _ireceiptLender.getReceiptId(loanId);
return (holderReceiptId, holderAddress);
}
function getBorrowerReceiptId(uint256 loanId) external view returns(uint256 holderReceiptId, address holderAddress){
require(loanId != 0, "200:ZERO_LoanID");
(holderReceiptId, holderAddress) = _ireceiptBorrower.getReceiptId(loanId);
return (holderReceiptId, holderAddress);
}
function getLoanId(uint256 _LoanReceiptId) external view returns(uint256 loanId){
require(_ireceiptLender.tokenExist(_LoanReceiptId), "Receipt does not exist");
loanId = _iloanManager.getLoanId(_LoanReceiptId);
return loanId;
}
function _computeAdminFee(
uint256 _interest,
uint256 _adminFee
) internal pure returns (uint256) {
return (_interest * (_adminFee)) / BPS;
}
function _sanityCheckPayBack(ILoanManager.Loan memory loan) internal view returns (address _borrower, address _lender){
require(
block.timestamp <= loan.loanDuration,
"Loan repayment period has expired"
);
require(loan.isApproved, "Loan offer not approved");
require(!loan.isDefault, "borrower is defaulter now");
require(!loan.isPaid, "Loan is Paid");
require(loan.lender != address(0), "Loan is not assigned to a lender");
require(
_ireceiptLender.tokenExist(loan.lenderReceiptId),
"Receipt does not exist"
);
require(
_ireceiptBorrower.tokenExist(loan.borrowerReceiptId),
"Receipt does not exist"
);
address lender = _ireceiptLender.ownerOf(loan.lenderReceiptId);
address borrower = _ireceiptBorrower.ownerOf(loan.borrowerReceiptId);
require(borrower == msg.sender, "caller is not borrower");
return (borrower,lender);
}
function _sanityCheckAcceptOffer(
address nftContractAddress,
address erc20Address
) internal view {
require(vault != address(0), "Vault address not set");
require(
_iwhiteListCollection.isWhiteListCollection(nftContractAddress),
"Collection is not White Listed"
);
require(
_iwhiteListCollection.isWhiteListErc20Token(erc20Address),
"Token is not White Listed"
);
require(loanManager != address(0), "Loan manager address not set");
require(
lenderReceiptContract != address(0),
"Receipt Lender contract address not set"
);
require(
borrowerReceiptContract != address(0),
"Receipt Borrower contract address not set"
);
}
function renounceOwnership() public view override onlyOwner {
}
}
文件 13 的 16:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 14 的 16:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
文件 15 的 16:SafeERC20.sol
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
error SafeERC20FailedOperation(address token);
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
文件 16 的 16:SignatureUtils.sol
pragma solidity ^0.8.24;
library SignatureUtils {
struct LoanRequest {
uint256[] tokenIds;
address nftContractAddress;
address erc20TokenAddress;
address borrower;
uint256 loanAmount;
uint256 aprBasisPoints;
uint256 loanDuration;
uint256 nonce;
}
struct LoanOffer {
uint256[] tokenIds;
address nftContractAddress;
address erc20TokenAddress;
address lender;
address borrower;
uint256 loanAmount;
uint256 aprBasisPoints;
uint256 loanDuration;
uint256 nonce;
}
struct LoanCollectionOffer {
address collectionAddress;
address erc20TokenAddress;
address lender;
uint256 loanAmount;
uint256 aprBasisPoints;
uint256 loanDuration;
uint256 nonce;
}
function _validateLoanCollectionOfferSignature(
bytes calldata signature,
LoanCollectionOffer calldata collectionOffer
) internal pure returns (bool) {
bytes32 freshHash = keccak256(
abi.encode(
collectionOffer.collectionAddress,
collectionOffer.erc20TokenAddress,
collectionOffer.lender,
collectionOffer.loanAmount,
collectionOffer.aprBasisPoints,
collectionOffer.loanDuration,
collectionOffer.nonce
)
);
bytes32 candidateHash = keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", freshHash)
);
return _verifyHashSignature(collectionOffer.lender, candidateHash, signature);
}
function _validateRequestLoanSignature(
bytes calldata signature,
LoanRequest calldata loanRequest
) internal pure returns (bool) {
bytes32 freshHash = keccak256(
abi.encode(
loanRequest.tokenIds,
loanRequest.nftContractAddress,
loanRequest.erc20TokenAddress,
loanRequest.borrower,
loanRequest.loanAmount,
loanRequest.aprBasisPoints,
loanRequest.loanDuration,
loanRequest.nonce
)
);
bytes32 candidateHash = keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", freshHash)
);
return _verifyHashSignature(loanRequest.borrower, candidateHash, signature);
}
function _validateSignatureApprovalOffer(
bytes calldata signature,
LoanOffer calldata loanOffer
) internal pure returns (bool) {
return _verifyHashSignature(loanOffer.lender, keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", keccak256(
abi.encode(
loanOffer.tokenIds,
loanOffer.nftContractAddress,
loanOffer.erc20TokenAddress,
loanOffer.lender,
loanOffer.borrower,
loanOffer.loanAmount,
loanOffer.aprBasisPoints,
loanOffer.loanDuration,
loanOffer.nonce
)
))
), signature);
}
function _verifyHashSignature(
address secret,
bytes32 hash,
bytes memory signature
) internal pure returns (bool) {
bytes32 r;
bytes32 s;
uint8 v;
if (signature.length != 65) {
return false;
}
assembly {
r := mload(add(signature, 32))
s := mload(add(signature, 64))
v := byte(0, mload(add(signature, 96)))
}
if (v < 27) {
v += 27;
}
address _signer = address(0);
if (v == 27 || v == 28) {
_signer = ecrecover(hash, v, r, s);
}
return secret == _signer;
}
}
{
"compilationTarget": {
"src/contracts/NettyWorthProxy.sol": "NettyWorthProxy"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"bool","name":"isDefault","type":"bool"}],"name":"LoanForClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayment","type":"uint256"},{"indexed":false,"internalType":"address","name":"erc20Address","type":"address"},{"indexed":false,"internalType":"bool","name":"isPaid","type":"bool"}],"name":"LoanRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldAdminFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newAdminFee","type":"uint256"}],"name":"UpdatedAdminFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdminWallet","type":"address"},{"indexed":false,"internalType":"address","name":"newAdminWallet","type":"address"}],"name":"UpdatedAdminWallet","type":"event"},{"inputs":[],"name":"BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"acceptOfferSignature","type":"bytes"},{"components":[{"internalType":"address","name":"collectionAddress","type":"address"},{"internalType":"address","name":"erc20TokenAddress","type":"address"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"loanAmount","type":"uint256"},{"internalType":"uint256","name":"aprBasisPoints","type":"uint256"},{"internalType":"uint256","name":"loanDuration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"internalType":"struct SignatureUtils.LoanCollectionOffer","name":"loanCollectionOffer","type":"tuple"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"acceptLoanCollectionOffer","outputs":[{"internalType":"uint256","name":"receiptIdBorrower","type":"uint256"},{"internalType":"uint256","name":"receiptIdLender","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"acceptOfferSignature","type":"bytes"},{"components":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address","name":"nftContractAddress","type":"address"},{"internalType":"address","name":"erc20TokenAddress","type":"address"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"loanAmount","type":"uint256"},{"internalType":"uint256","name":"aprBasisPoints","type":"uint256"},{"internalType":"uint256","name":"loanDuration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"internalType":"struct SignatureUtils.LoanOffer","name":"loanOffer","type":"tuple"}],"name":"acceptLoanOffer","outputs":[{"internalType":"uint256","name":"receiptIdBorrower","type":"uint256"},{"internalType":"uint256","name":"receiptIdLender","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"acceptRequestSignature","type":"bytes"},{"components":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address","name":"nftContractAddress","type":"address"},{"internalType":"address","name":"erc20TokenAddress","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"loanAmount","type":"uint256"},{"internalType":"uint256","name":"aprBasisPoints","type":"uint256"},{"internalType":"uint256","name":"loanDuration","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"internalType":"struct SignatureUtils.LoanRequest","name":"loanRequest","type":"tuple"}],"name":"acceptLoanRequest","outputs":[{"internalType":"uint256","name":"receiptIdBorrower","type":"uint256"},{"internalType":"uint256","name":"receiptIdLender","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"adminFeeInBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adminWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerReceiptContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"}],"name":"forCloseLoan","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"getBorrowerReceiptId","outputs":[{"internalType":"uint256","name":"holderReceiptId","type":"uint256"},{"internalType":"address","name":"holderAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"getLenderReceiptId","outputs":[{"internalType":"uint256","name":"holderReceiptId","type":"uint256"},{"internalType":"address","name":"holderAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_LoanReceiptId","type":"uint256"}],"name":"getLoanId","outputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_loanManager","type":"address"},{"internalType":"address","name":"_lenderReceiptContract","type":"address"},{"internalType":"address","name":"_borrowerReceiptContract","type":"address"},{"internalType":"address","name":"_iwhiteListContract","type":"address"},{"internalType":"address","name":"_adminWallet","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lenderReceiptContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"loanManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"internalType":"address","name":"erc20Token","type":"address"}],"name":"payBackLoan","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_adminWallet","type":"address"}],"name":"proposeAdminWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newAdminFee","type":"uint256"}],"name":"proposeUpdateAdminFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"setAdminWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_loanManager","type":"address"}],"name":"setLoanManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrowerReceiptContract","type":"address"}],"name":"setReceiptContractBorrower","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_lenderReceiptContract","type":"address"}],"name":"setReceiptContractLender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"setVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_whiteList","type":"address"}],"name":"setWhiteListContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateAdminFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whiteListContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]