编译器
0.8.21+commit.d9974bed
文件 1 的 29:AddressManager.sol
pragma solidity ^0.8.20;
import "@solmate/auth/Owned.sol";
import "@solmate/utils/ReentrancyGuard.sol";
import "./InputChecker.sol";
contract AddressManager is InputChecker, Owned, ReentrancyGuard {
event AddressAdded(address address_added);
event AddressRemovedFromWhitelist(address address_removed);
event AddressWhitelisted(address address_whitelisted);
error AddressAlreadyAddedError(address _address);
error AddressNotAddedError(address _address);
mapping(address => uint16) private _directory;
mapping(uint16 => address) private _inverseDirectory;
mapping(address => bool) private _whitelist;
uint16 private _lastAdded;
constructor(address[] memory _original) Owned(tx.origin) {
uint256 total = _original.length;
for (uint256 i; i < total;) {
_add(_original[i]);
unchecked {
++i;
}
}
}
function add(address _entry) external onlyOwner returns (uint16) {
return _add(_entry);
}
function addToWhitelist(address _entry) external onlyOwner {
if (_directory[_entry] == 0) {
revert AddressNotAddedError(_entry);
}
_whitelist[_entry] = true;
emit AddressWhitelisted(_entry);
}
function removeFromWhitelist(address _entry) external onlyOwner {
_whitelist[_entry] = false;
emit AddressRemovedFromWhitelist(_entry);
}
function addressToIndex(address _address) external view returns (uint16) {
return _directory[_address];
}
function indexToAddress(uint16 _index) external view returns (address) {
return _inverseDirectory[_index];
}
function isWhitelisted(address _entry) external view returns (bool) {
return _whitelist[_entry];
}
function _add(address _entry) private returns (uint16) {
_checkAddressNotZero(_entry);
if (_directory[_entry] != 0) {
revert AddressAlreadyAddedError(_entry);
}
unchecked {
++_lastAdded;
}
_directory[_entry] = _lastAdded;
_inverseDirectory[_lastAdded] = _entry;
_whitelist[_entry] = true;
emit AddressAdded(_entry);
return _lastAdded;
}
}
文件 2 的 29:BaseLoan.sol
pragma solidity ^0.8.20;
import "@openzeppelin/utils/cryptography/ECDSA.sol";
import "@openzeppelin/utils/cryptography/MessageHashUtils.sol";
import "@openzeppelin/interfaces/IERC1271.sol";
import "@solmate/tokens/ERC721.sol";
import "@solmate/utils/FixedPointMathLib.sol";
import "../AddressManager.sol";
import "../utils/Hash.sol";
import "../../interfaces/loans/IBaseLoan.sol";
import "../../interfaces/validators/IBaseOfferValidator.sol";
import "../InputChecker.sol";
abstract contract BaseLoan is ERC721TokenReceiver, IBaseLoan, InputChecker, Owned {
using FixedPointMathLib for uint256;
using ECDSA for bytes32;
using MessageHashUtils for bytes32;
using Hash for LoanOffer;
using Hash for ExecutionData;
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 public immutable INITIAL_DOMAIN_SEPARATOR;
uint256 public constant MAX_PROTOCOL_FEE = 2500;
uint256 public constant FEE_UPDATE_NOTICE = 30 days;
uint48 public constant MIN_AUCTION_DURATION = 1 days;
bytes4 private constant MAGICVALUE_1271 = 0x1626ba7e;
uint256 internal constant _PRECISION = 10000;
ImprovementMinimum internal _minimum = ImprovementMinimum(500, 100, 100);
string public name;
uint48 internal _liquidationAuctionDuration = 3 days;
ILoanLiquidator internal _loanLiquidator;
ProtocolFee internal _protocolFee;
ProtocolFee internal _pendingProtocolFee;
uint256 internal _pendingProtocolFeeSetTime;
uint256 public override getTotalLoansIssued;
mapping(address => mapping(uint256 => uint256)) internal _used;
mapping(address => mapping(uint256 => bool)) public isOfferCancelled;
mapping(address => uint256) public minOfferId;
mapping(address => mapping(uint256 => bool)) public isRenegotiationOfferCancelled;
mapping(address => uint256) public lenderMinRenegotiationOfferId;
AddressManager internal immutable _currencyManager;
AddressManager internal immutable _collectionManager;
mapping(address => bool) internal _isWhitelistedCallbackContract;
event OfferCancelled(address lender, uint256 offerId);
event BorrowerOfferCancelled(address borrower, uint256 offerId);
event AllOffersCancelled(address lender, uint256 minOfferId);
event RenegotiationOfferCancelled(address lender, uint256 renegotiationId);
event AllRenegotiationOffersCancelled(address lender, uint256 minRenegotiationId);
event ProtocolFeeUpdated(ProtocolFee fee);
event ProtocolFeePendingUpdate(ProtocolFee fee);
event LoanSentToLiquidator(uint256 loanId, address liquidator);
event LoanLiquidated(uint256 loanId);
event LoanForeclosed(uint256 loanId);
event ImprovementMinimumUpdated(ImprovementMinimum minimum);
event LiquidationContractUpdated(address liquidator);
event LiquidationAuctionDurationUpdated(uint256 newDuration);
error InvalidValueError();
error LiquidatorOnlyError(address _liquidator);
error CancelledOrExecutedOfferError(address _lender, uint256 _offerId);
error CancelledRenegotiationOfferError(address _lender, uint256 _renegotiationId);
error ExpiredOfferError(uint256 _expirationTime);
error ExpiredRenegotiationOfferError(uint256 _expirationTime);
error LowOfferIdError(address _lender, uint256 _newMinOfferId, uint256 _minOfferId);
error LowRenegotiationOfferIdError(address _lender, uint256 _newMinRenegotiationOfferId, uint256 _minOfferId);
error CannotLiquidateError();
error LoanNotDueError(uint256 _expirationTime);
error InvalidLenderError();
error InvalidBorrowerError();
error ZeroDurationError();
error ZeroInterestError();
error InvalidSignatureError();
error InvalidLiquidationError();
error CurrencyNotWhitelistedError();
error CollectionNotWhitelistedError();
error InvalidProtocolFeeError(uint256 _fraction);
error TooEarlyError(uint256 _pendingProtocolFeeSetTime);
error MaxCapacityExceededError();
error InvalidLoanError(uint256 _loanId);
error InvalidCollateralIdError();
error OnlyLenderOrBorrowerCallableError();
error OnlyBorrowerCallableError();
error OnlyLenderCallableError();
error NotStrictlyImprovedError();
error InvalidAmountError(uint256 _amount, uint256 _principalAmount);
error InvalidDurationError();
constructor(string memory _name, address currencyManager, address collectionManager) Owned(tx.origin) {
name = _name;
_checkAddressNotZero(currencyManager);
_checkAddressNotZero(collectionManager);
_currencyManager = AddressManager(currencyManager);
_collectionManager = AddressManager(collectionManager);
_pendingProtocolFeeSetTime = type(uint256).max;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
}
modifier onlyLiquidator() {
if (msg.sender != address(_loanLiquidator)) {
revert LiquidatorOnlyError(address(_loanLiquidator));
}
_;
}
function DOMAIN_SEPARATOR() public view returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : _computeDomainSeparator();
}
function getImprovementMinimum() external view returns (ImprovementMinimum memory) {
return _minimum;
}
function updateImprovementMinimum(ImprovementMinimum calldata _newMinimum) external onlyOwner {
_minimum = _newMinimum;
emit ImprovementMinimumUpdated(_newMinimum);
}
function getCurrencyManager() external view returns (address) {
return address(_currencyManager);
}
function getCollectionManager() external view returns (address) {
return address(_collectionManager);
}
function cancelOffer(uint256 _offerId) external {
address user = msg.sender;
isOfferCancelled[user][_offerId] = true;
emit OfferCancelled(user, _offerId);
}
function cancelOffers(uint256[] calldata _offerIds) external virtual {
address user = msg.sender;
uint256 total = _offerIds.length;
for (uint256 i = 0; i < total;) {
uint256 offerId = _offerIds[i];
isOfferCancelled[user][offerId] = true;
emit OfferCancelled(user, offerId);
unchecked {
++i;
}
}
}
function cancelAllOffers(uint256 _minOfferId) external virtual {
address user = msg.sender;
uint256 currentMinOfferId = minOfferId[user];
if (currentMinOfferId >= _minOfferId) {
revert LowOfferIdError(user, _minOfferId, currentMinOfferId);
}
minOfferId[user] = _minOfferId;
emit AllOffersCancelled(user, _minOfferId);
}
function cancelRenegotiationOffer(uint256 _renegotiationId) external virtual {
address lender = msg.sender;
isRenegotiationOfferCancelled[lender][_renegotiationId] = true;
emit RenegotiationOfferCancelled(lender, _renegotiationId);
}
function cancelRenegotiationOffers(uint256[] calldata _renegotiationIds) external virtual {
address lender = msg.sender;
uint256 total = _renegotiationIds.length;
for (uint256 i = 0; i < total;) {
uint256 renegotiationId = _renegotiationIds[i];
isRenegotiationOfferCancelled[lender][renegotiationId] = true;
emit RenegotiationOfferCancelled(lender, renegotiationId);
unchecked {
++i;
}
}
}
function cancelAllRenegotiationOffers(uint256 _minRenegotiationId) external virtual {
address lender = msg.sender;
uint256 currentMinRenegotiationOfferId = lenderMinRenegotiationOfferId[lender];
if (currentMinRenegotiationOfferId >= _minRenegotiationId) {
revert LowRenegotiationOfferIdError(lender, _minRenegotiationId, currentMinRenegotiationOfferId);
}
lenderMinRenegotiationOfferId[lender] = _minRenegotiationId;
emit AllRenegotiationOffersCancelled(lender, _minRenegotiationId);
}
function getUsedCapacity(address _lender, uint256 _offerId) external view returns (uint256) {
return _used[_lender][_offerId];
}
function getProtocolFee() external view returns (ProtocolFee memory) {
return _protocolFee;
}
function getPendingProtocolFee() external view returns (ProtocolFee memory) {
return _pendingProtocolFee;
}
function getPendingProtocolFeeSetTime() external view returns (uint256) {
return _pendingProtocolFeeSetTime;
}
function setProtocolFee() external onlyOwner {
if (block.timestamp < _pendingProtocolFeeSetTime + FEE_UPDATE_NOTICE) {
revert TooEarlyError(_pendingProtocolFeeSetTime);
}
_protocolFee = _pendingProtocolFee;
emit ProtocolFeeUpdated(_pendingProtocolFee);
}
function updateProtocolFee(ProtocolFee calldata _newProtocolFee) external onlyOwner {
if (_newProtocolFee.fraction > MAX_PROTOCOL_FEE) {
revert InvalidProtocolFeeError(_newProtocolFee.fraction);
}
_checkAddressNotZero(_newProtocolFee.recipient);
_pendingProtocolFee = _newProtocolFee;
_pendingProtocolFeeSetTime = block.timestamp;
emit ProtocolFeePendingUpdate(_pendingProtocolFee);
}
function getLiquidator() external view returns (address) {
return address(_loanLiquidator);
}
function updateLiquidationContract(ILoanLiquidator loanLiquidator) external onlyOwner {
_checkAddressNotZero(address(loanLiquidator));
_loanLiquidator = loanLiquidator;
emit LiquidationContractUpdated(address(loanLiquidator));
}
function updateLiquidationAuctionDuration(uint48 _newDuration) external onlyOwner {
if (_newDuration < MIN_AUCTION_DURATION) {
revert InvalidDurationError();
}
_liquidationAuctionDuration = _newDuration;
emit LiquidationAuctionDurationUpdated(_newDuration);
}
function getLiquidationAuctionDuration() external view returns (uint48) {
return _liquidationAuctionDuration;
}
function _getAndSetNewLoanId() internal returns (uint256) {
unchecked {
return ++getTotalLoansIssued;
}
}
function _validateExecutionData(
ExecutionData calldata _executionData,
address _lender,
address _borrower,
address _offerer,
bytes calldata _lenderOfferSignature,
bytes calldata _borrowerOfferSignature
) internal {
address lender = _executionData.offer.lender;
address borrower = _executionData.offer.borrower;
LoanOffer calldata offer = _executionData.offer;
uint256 offerId = offer.offerId;
if (msg.sender != _lender) {
_checkSignature(lender, offer.hash(), _lenderOfferSignature);
}
if (msg.sender != _borrower) {
_checkSignature(_borrower, _executionData.hash(), _borrowerOfferSignature);
}
if (block.timestamp > offer.expirationTime) {
revert ExpiredOfferError(offer.expirationTime);
}
if (block.timestamp > _executionData.expirationTime) {
revert ExpiredOfferError(_executionData.expirationTime);
}
if (isOfferCancelled[_offerer][offerId] || (offerId <= minOfferId[_offerer])) {
revert CancelledOrExecutedOfferError(_offerer, offerId);
}
if (_executionData.amount > offer.principalAmount) {
revert InvalidAmountError(_executionData.amount, offer.principalAmount);
}
if (!_currencyManager.isWhitelisted(offer.principalAddress)) {
revert CurrencyNotWhitelistedError();
}
if (!_collectionManager.isWhitelisted(offer.nftCollateralAddress)) {
revert CollectionNotWhitelistedError();
}
if (lender != address(0) && (lender != _lender)) {
revert InvalidLenderError();
}
if (borrower != address(0) && (borrower != _borrower)) {
revert InvalidBorrowerError();
}
if (offer.duration == 0) {
revert ZeroDurationError();
}
if (offer.aprBps == 0) {
revert ZeroInterestError();
}
if ((offer.capacity > 0) && (_used[_offerer][offer.offerId] + _executionData.amount > offer.capacity)) {
revert MaxCapacityExceededError();
}
_checkValidators(offer, _executionData.tokenId);
}
function _checkValidators(LoanOffer calldata _loanOffer, uint256 _tokenId) internal {
uint256 offerTokenId = _loanOffer.nftCollateralTokenId;
if (_loanOffer.nftCollateralTokenId != 0) {
if (offerTokenId != _tokenId) {
revert InvalidCollateralIdError();
}
} else {
uint256 totalValidators = _loanOffer.validators.length;
if (totalValidators == 0 && _tokenId != 0) {
revert InvalidCollateralIdError();
} else if ((totalValidators == 1) && (_loanOffer.validators[0].validator == address(0))) {
return;
}
for (uint256 i = 0; i < totalValidators;) {
OfferValidator memory thisValidator = _loanOffer.validators[i];
IBaseOfferValidator(thisValidator.validator).validateOffer(
_loanOffer, _tokenId, thisValidator.arguments
);
unchecked {
++i;
}
}
}
}
function _checkSignature(address _signer, bytes32 _hash, bytes calldata _signature) internal view {
bytes32 offerHash = DOMAIN_SEPARATOR().toTypedDataHash(_hash);
if (_signer.code.length > 0) {
if (IERC1271(_signer).isValidSignature(offerHash, _signature) != MAGICVALUE_1271) {
revert InvalidSignatureError();
}
} else {
address recovered = offerHash.recover(_signature);
if (_signer != recovered) {
revert InvalidSignatureError();
}
}
}
function _checkStrictlyBetter(
uint256 _offerPrincipalAmount,
uint256 _loanPrincipalAmount,
uint256 _offerEndTime,
uint256 _loanEndTime,
uint256 _offerAprBps,
uint256 _loanAprBps,
uint256 _offerFee
) internal view {
ImprovementMinimum memory minimum = _minimum;
if (
(
(_offerPrincipalAmount - _loanPrincipalAmount > 0)
&& (
(_loanAprBps * _loanPrincipalAmount - _offerAprBps * _offerPrincipalAmount).mulDivDown(
_PRECISION, _loanAprBps * _loanPrincipalAmount
) < minimum.interest
)
) || (_offerFee > 0) || (_offerEndTime < _loanEndTime)
) {
revert NotStrictlyImprovedError();
}
}
function _computeDomainSeparator() private view returns (bytes32) {
return keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("2"),
block.chainid,
address(this)
)
);
}
}
文件 3 的 29:ECDSA.sol
pragma solidity ^0.8.20;
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
error ECDSAInvalidSignature();
error ECDSAInvalidSignatureLength(uint256 length);
error ECDSAInvalidSignatureS(bytes32 s);
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return;
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
文件 4 的 29:ERC20.sol
pragma solidity >=0.8.0;
abstract contract ERC20 {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
文件 5 的 29:ERC721.sol
pragma solidity >=0.8.0;
abstract contract ERC721 {
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
mapping(uint256 => address) internal _ownerOf;
mapping(address => uint256) internal _balanceOf;
function ownerOf(uint256 id) public view virtual returns (address owner) {
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
}
function balanceOf(address owner) public view virtual returns (uint256) {
require(owner != address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
function approve(address spender, uint256 id) public virtual {
address owner = _ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == _ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
"NOT_AUTHORIZED"
);
unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes calldata data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 ||
interfaceId == 0x80ac58cd ||
interfaceId == 0x5b5e139f;
}
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = _ownerOf[id];
require(owner != address(0), "NOT_MINTED");
unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}
文件 6 的 29:FixedPointMathLib.sol
pragma solidity >=0.8.0;
library FixedPointMathLib {
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18;
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD);
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD);
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y);
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y);
}
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
assembly {
switch x
case 0 {
switch n
case 0 {
z := scalar
}
default {
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
z := scalar
}
default {
z := x
}
let half := shr(1, scalar)
for {
n := shr(1, n)
} n {
n := shr(1, n)
} {
if shr(128, x) {
revert(0, 0)
}
let xx := mul(x, x)
let xxRound := add(xx, half)
if lt(xxRound, xx) {
revert(0, 0)
}
x := div(xxRound, scalar)
if mod(n, 2) {
let zx := mul(z, x)
if iszero(eq(div(zx, x), z)) {
if iszero(iszero(x)) {
revert(0, 0)
}
}
let zxRound := add(zx, half)
if lt(zxRound, zx) {
revert(0, 0)
}
z := div(zxRound, scalar)
}
}
}
}
}
function sqrt(uint256 x) internal pure returns (uint256 z) {
assembly {
let y := x
z := 181
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
z := shr(18, mul(z, add(y, 65536)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
assembly {
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}
文件 7 的 29:Hash.sol
pragma solidity ^0.8.20;
import "../../interfaces/loans/IMultiSourceLoan.sol";
import "../../interfaces/loans/IBaseLoan.sol";
import "../../interfaces/IAuctionLoanLiquidator.sol";
library Hash {
bytes32 private constant _VALIDATOR_HASH = 0x4def3e04bd42194484d5f8a5b268ec0df03b9d9d0402606fe3100023c5d79ac4;
bytes32 private constant _LOAN_OFFER_HASH = 0x891e530ed2768a9decac48f4b7beec447f755ce23feeeeb952e429145b44ba91;
bytes32 private constant _EXECUTION_DATA_HASH = 0x7e90717662b6dd110797922ef6d6701d92bfd4164783966933e092ea21a74c5a;
bytes32 private constant _SIGNABLE_REPAYMENT_DATA_HASH =
0x41277b3c1cbe08ea7bbdd10a13f24dc956f3936bf46526f904c73697d9958e0c;
bytes32 private constant _MULTI_SOURCE_LOAN_HASH =
0x35f73c5cb07b3fa605378d4f576769166fed212ec3813ac1f1d73ef1c537eb0e;
bytes32 private constant _SOURCE_HASH = 0x8ca047c2f10359bf4a27bd2c623674be3801153b6b2646ba08593dc96ad7bb44;
bytes32 private constant _MULTI_RENEGOTIATION_OFFER_HASH =
0xdb613ea3383336cd787d929ccfc21ab7cd87bf1d588780c80ce5f970dd79c348;
bytes32 private constant _AUCTION_HASH = 0xd1912299766a3d3ca1ad2e2135d884e08d798009860146382d22f8c389905b34;
function hash(IBaseLoan.LoanOffer memory _loanOffer) internal pure returns (bytes32) {
bytes memory encodedValidators;
for (uint256 i = 0; i < _loanOffer.validators.length;) {
encodedValidators = abi.encodePacked(encodedValidators, _hashValidator(_loanOffer.validators[i]));
unchecked {
++i;
}
}
return keccak256(
abi.encode(
_LOAN_OFFER_HASH,
_loanOffer.offerId,
_loanOffer.lender,
_loanOffer.fee,
_loanOffer.borrower,
_loanOffer.capacity,
_loanOffer.nftCollateralAddress,
_loanOffer.nftCollateralTokenId,
_loanOffer.principalAddress,
_loanOffer.principalAmount,
_loanOffer.aprBps,
_loanOffer.expirationTime,
_loanOffer.duration,
keccak256(encodedValidators)
)
);
}
function hash(IBaseLoan.ExecutionData memory _executionData) internal pure returns (bytes32) {
return keccak256(
abi.encode(
_EXECUTION_DATA_HASH,
hash(_executionData.offer),
_executionData.tokenId,
_executionData.amount,
_executionData.expirationTime,
keccak256(_executionData.callbackData)
)
);
}
function hash(IMultiSourceLoan.SignableRepaymentData memory _repaymentData) internal pure returns (bytes32) {
return keccak256(
abi.encode(
_SIGNABLE_REPAYMENT_DATA_HASH,
_repaymentData.loanId,
keccak256(_repaymentData.callbackData),
_repaymentData.shouldDelegate
)
);
}
function hash(IMultiSourceLoan.Loan memory _loan) internal pure returns (bytes32) {
bytes memory sourceHashes;
for (uint256 i = 0; i < _loan.source.length;) {
sourceHashes = abi.encodePacked(sourceHashes, _hashSource(_loan.source[i]));
unchecked {
++i;
}
}
return keccak256(
abi.encode(
_MULTI_SOURCE_LOAN_HASH,
_loan.borrower,
_loan.nftCollateralTokenId,
_loan.nftCollateralAddress,
_loan.principalAddress,
_loan.principalAmount,
_loan.startTime,
_loan.duration,
keccak256(sourceHashes)
)
);
}
function hash(IMultiSourceLoan.RenegotiationOffer memory _refinanceOffer) internal pure returns (bytes32) {
bytes memory encodedPrincipals;
for (uint256 i = 0; i < _refinanceOffer.targetPrincipal.length;) {
encodedPrincipals = abi.encodePacked(encodedPrincipals, _refinanceOffer.targetPrincipal[i]);
unchecked {
++i;
}
}
return keccak256(
abi.encode(
_MULTI_RENEGOTIATION_OFFER_HASH,
_refinanceOffer.renegotiationId,
_refinanceOffer.loanId,
_refinanceOffer.lender,
_refinanceOffer.fee,
keccak256(encodedPrincipals),
_refinanceOffer.principalAmount,
_refinanceOffer.aprBps,
_refinanceOffer.expirationTime,
_refinanceOffer.duration
)
);
}
function hash(IAuctionLoanLiquidator.Auction memory _auction) internal pure returns (bytes32) {
return keccak256(
abi.encode(
_AUCTION_HASH,
_auction.loanAddress,
_auction.loanId,
_auction.highestBid,
_auction.triggerFee,
_auction.highestBidder,
_auction.duration,
_auction.asset,
_auction.startTime,
_auction.originator,
_auction.lastBidTime
)
);
}
function _hashSource(IMultiSourceLoan.Source memory _source) private pure returns (bytes32) {
return keccak256(
abi.encode(
_SOURCE_HASH,
_source.lender,
_source.principalAmount,
_source.accruedInterest,
_source.startTime,
_source.aprBps
)
);
}
function _hashValidator(IBaseLoan.OfferValidator memory _validator) private pure returns (bytes32) {
return keccak256(abi.encode(_VALIDATOR_HASH, _validator.validator, keccak256(_validator.arguments)));
}
}
文件 8 的 29:IAuctionLoanLiquidator.sol
pragma solidity ^0.8.20;
import "./loans/IMultiSourceLoan.sol";
interface IAuctionLoanLiquidator {
struct Auction {
address loanAddress;
uint256 loanId;
uint256 highestBid;
uint256 triggerFee;
address highestBidder;
uint96 duration;
address asset;
uint96 startTime;
address originator;
uint96 lastBidTime;
}
function addLoanContract(address _loanContract) external;
function removeLoanContract(address _loanContract) external;
function getValidLoanContracts() external view returns (address[] memory);
function updateLiquidationDistributor(address _liquidationDistributor) external;
function getLiquidationDistributor() external view returns (address);
function updateTriggerFee(uint256 triggerFee) external;
function getTriggerFee() external view returns (uint256);
function placeBid(address _contract, uint256 _tokenId, Auction memory _auction, uint256 _bid)
external
returns (Auction memory);
function settleAuction(Auction calldata _auction, IMultiSourceLoan.Loan calldata _loan) external;
function getAuctionHash(address _contract, uint256 _tokenId) external view returns (bytes32);
}
文件 9 的 29:IBaseLoan.sol
pragma solidity ^0.8.20;
import "../../interfaces/ILoanLiquidator.sol";
interface IBaseLoan {
struct ImprovementMinimum {
uint256 principalAmount;
uint256 interest;
uint256 duration;
}
struct OfferValidator {
address validator;
bytes arguments;
}
struct LoanOffer {
uint256 offerId;
address lender;
uint256 fee;
address borrower;
uint256 capacity;
address nftCollateralAddress;
uint256 nftCollateralTokenId;
address principalAddress;
uint256 principalAmount;
uint256 aprBps;
uint256 expirationTime;
uint256 duration;
OfferValidator[] validators;
}
struct ExecutionData {
LoanOffer offer;
uint256 tokenId;
uint256 amount;
uint256 expirationTime;
bytes callbackData;
}
struct ProtocolFee {
address recipient;
uint256 fraction;
}
function getTotalLoansIssued() external view returns (uint256);
function cancelOffer(uint256 _offerId) external;
function cancelOffers(uint256[] calldata _offerIds) external;
function cancelAllOffers(uint256 _minOfferId) external;
function cancelRenegotiationOffer(uint256 _renegotiationId) external;
function cancelRenegotiationOffers(uint256[] calldata _renegotiationIds) external;
function cancelAllRenegotiationOffers(uint256 _minRenegotiationId) external;
function getProtocolFee() external view returns (ProtocolFee memory);
function getPendingProtocolFee() external view returns (ProtocolFee memory);
function getPendingProtocolFeeSetTime() external view returns (uint256);
function updateProtocolFee(ProtocolFee calldata _newProtocolFee) external;
function setProtocolFee() external;
function getLiquidator() external returns (address);
function updateLiquidationContract(ILoanLiquidator loanLiquidator) external;
function updateLiquidationAuctionDuration(uint48 _newDuration) external;
function getLiquidationAuctionDuration() external returns (uint48);
}
文件 10 的 29:IBaseOfferValidator.sol
pragma solidity ^0.8.20;
import "../loans/IBaseLoan.sol";
interface IBaseOfferValidator {
function validateOffer(IBaseLoan.LoanOffer calldata _offer, uint256 _tokenId, bytes calldata _validatorData)
external;
}
文件 11 的 29:IDelegateRegistry.sol
pragma solidity >=0.8.13;
interface IDelegateRegistry {
enum DelegationType {
NONE,
ALL,
CONTRACT,
ERC721,
ERC20,
ERC1155
}
struct Delegation {
DelegationType type_;
address to;
address from;
bytes32 rights;
address contract_;
uint256 tokenId;
uint256 amount;
}
event DelegateAll(address indexed from, address indexed to, bytes32 rights, bool enable);
event DelegateContract(address indexed from, address indexed to, address indexed contract_, bytes32 rights, bool enable);
event DelegateERC721(address indexed from, address indexed to, address indexed contract_, uint256 tokenId, bytes32 rights, bool enable);
event DelegateERC20(address indexed from, address indexed to, address indexed contract_, bytes32 rights, uint256 amount);
event DelegateERC1155(address indexed from, address indexed to, address indexed contract_, uint256 tokenId, bytes32 rights, uint256 amount);
error MulticallFailed();
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
function delegateAll(address to, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);
function delegateContract(address to, address contract_, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);
function delegateERC721(address to, address contract_, uint256 tokenId, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);
function delegateERC20(address to, address contract_, bytes32 rights, uint256 amount) external payable returns (bytes32 delegationHash);
function delegateERC1155(address to, address contract_, uint256 tokenId, bytes32 rights, uint256 amount) external payable returns (bytes32 delegationHash);
function checkDelegateForAll(address to, address from, bytes32 rights) external view returns (bool);
function checkDelegateForContract(address to, address from, address contract_, bytes32 rights) external view returns (bool);
function checkDelegateForERC721(address to, address from, address contract_, uint256 tokenId, bytes32 rights) external view returns (bool);
function checkDelegateForERC20(address to, address from, address contract_, bytes32 rights) external view returns (uint256);
function checkDelegateForERC1155(address to, address from, address contract_, uint256 tokenId, bytes32 rights) external view returns (uint256);
function getIncomingDelegations(address to) external view returns (Delegation[] memory delegations);
function getOutgoingDelegations(address from) external view returns (Delegation[] memory delegations);
function getIncomingDelegationHashes(address to) external view returns (bytes32[] memory delegationHashes);
function getOutgoingDelegationHashes(address from) external view returns (bytes32[] memory delegationHashes);
function getDelegationsFromHashes(bytes32[] calldata delegationHashes) external view returns (Delegation[] memory delegations);
function readSlot(bytes32 location) external view returns (bytes32);
function readSlots(bytes32[] calldata locations) external view returns (bytes32[] memory);
}
文件 12 的 29:IERC1271.sol
pragma solidity ^0.8.20;
interface IERC1271 {
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
文件 13 的 29:ILoanCallback.sol
pragma solidity ^0.8.20;
import "../loans/IMultiSourceLoan.sol";
interface ILoanCallback {
error InvalidCallbackError();
function afterPrincipalTransfer(IMultiSourceLoan.Loan memory _loan, uint256 _fee, bytes calldata _executionData)
external
returns (bytes4);
function afterNFTTransfer(IMultiSourceLoan.Loan memory _loan, bytes calldata _executionData)
external
returns (bytes4);
}
文件 14 的 29:ILoanLiquidator.sol
pragma solidity ^0.8.20;
interface ILoanLiquidator {
function liquidateLoan(
uint256 _loanId,
address _contract,
uint256 _tokenId,
address _asset,
uint96 _duration,
address _originator
) external returns (bytes memory);
}
文件 15 的 29:IMultiSourceLoan.sol
pragma solidity ^0.8.20;
import "./IBaseLoan.sol";
interface IMultiSourceLoan {
struct LoanExecutionData {
IBaseLoan.ExecutionData executionData;
address lender;
address borrower;
bytes lenderOfferSignature;
bytes borrowerOfferSignature;
}
struct SignableRepaymentData {
uint256 loanId;
bytes callbackData;
bool shouldDelegate;
}
struct LoanRepaymentData {
SignableRepaymentData data;
Loan loan;
bytes borrowerSignature;
}
struct Source {
uint256 loanId;
address lender;
uint256 principalAmount;
uint256 accruedInterest;
uint256 startTime;
uint256 aprBps;
}
struct Loan {
address borrower;
uint256 nftCollateralTokenId;
address nftCollateralAddress;
address principalAddress;
uint256 principalAmount;
uint256 startTime;
uint256 duration;
Source[] source;
}
struct RenegotiationOffer {
uint256 renegotiationId;
uint256 loanId;
address lender;
uint256 fee;
uint256[] targetPrincipal;
uint256 principalAmount;
uint256 aprBps;
uint256 expirationTime;
uint256 duration;
}
function emitLoan(LoanExecutionData calldata _executionData) external returns (uint256, Loan memory);
function refinanceFull(
RenegotiationOffer calldata _renegotiationOffer,
Loan memory _loan,
bytes calldata _renegotiationOfferSignature
) external returns (uint256, Loan memory);
function refinancePartial(RenegotiationOffer calldata _renegotiationOffer, Loan memory _loan)
external
returns (uint256, Loan memory);
function repayLoan(LoanRepaymentData calldata _repaymentData) external;
function liquidateLoan(uint256 _loanId, Loan calldata _loan) external returns (bytes memory);
function getMaxSources() external view returns (uint256);
function setMaxSources(uint256 maxSources) external;
function setMinLockPeriod(uint256 _minLockPeriod) external;
function getMinLockPeriod() external view returns (uint256);
function getDelegateRegistry() external view returns (address);
function setDelegateRegistry(address _newDelegationRegistry) external;
function delegate(uint256 _loanId, Loan calldata _loan, address _delegate, bytes32 _rights, bool _value) external;
function revokeDelegate(address _delegate, address _collection, uint256 _tokenId) external;
function getFlashActionContract() external view returns (address);
function setFlashActionContract(address _newFlashActionContract) external;
function getLoanHash(uint256 _loanId) external view returns (bytes32);
function executeFlashAction(uint256 _loanId, Loan calldata _loan, address _target, bytes calldata _data) external;
function extendLoan(uint256 _loanId, Loan memory _loan, uint256 _extension)
external
returns (uint256, Loan memory);
function loanLiquidated(uint256 _loanId, Loan calldata _loan) external;
}
文件 16 的 29:IMulticall.sol
pragma solidity ^0.8.20;
interface IMulticall {
error MulticallFailed(uint256 i, bytes returndata);
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
}
文件 17 的 29:INFTFlashAction.sol
pragma solidity ^0.8.20;
interface INFTFlashAction {
error InvalidOwnerError();
function execute(address _collection, uint256 _tokenId, address _target, bytes calldata _data) external;
}
文件 18 的 29:InputChecker.sol
pragma solidity ^0.8.20;
abstract contract InputChecker {
error AddressZeroError();
function _checkAddressNotZero(address _address) internal pure {
if (_address == address(0)) {
revert AddressZeroError();
}
}
}
文件 19 的 29:Interest.sol
pragma solidity ^0.8.20;
import "@solmate/utils/FixedPointMathLib.sol";
import "../../interfaces/loans/IMultiSourceLoan.sol";
import "../../interfaces/loans/IBaseLoan.sol";
library Interest {
using FixedPointMathLib for uint256;
uint256 private constant _PRECISION = 10000;
uint256 private constant _SECONDS_PER_YEAR = 31536000;
function getInterest(IBaseLoan.LoanOffer memory _loanOffer) internal pure returns (uint256) {
return _getInterest(_loanOffer.principalAmount, _loanOffer.aprBps, _loanOffer.duration);
}
function getInterest(uint256 _amount, uint256 _aprBps, uint256 _duration) internal pure returns (uint256) {
return _getInterest(_amount, _aprBps, _duration);
}
function getTotalOwed(IMultiSourceLoan.Loan memory _loan, uint256 _timestamp) internal pure returns (uint256) {
uint256 owed = 0;
for (uint256 i = 0; i < _loan.source.length;) {
IMultiSourceLoan.Source memory source = _loan.source[i];
owed += source.principalAmount + source.accruedInterest
+ _getInterest(source.principalAmount, source.aprBps, _timestamp - source.startTime);
unchecked {
++i;
}
}
return owed;
}
function _getInterest(uint256 _amount, uint256 _aprBps, uint256 _duration) private pure returns (uint256) {
return _amount.mulDivUp(_aprBps * _duration, _PRECISION * _SECONDS_PER_YEAR);
}
}
文件 20 的 29:Math.sol
pragma solidity ^0.8.20;
library Math {
error MathOverflowedMulDiv();
enum Rounding {
Floor,
Ceil,
Trunc,
Expand
}
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
return a / b;
}
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0 = x * y;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (0 - denominator);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
文件 21 的 29:MessageHashUtils.sol
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
library MessageHashUtils {
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, messageHash)
digest := keccak256(0x00, 0x3c)
}
}
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}
文件 22 的 29:MultiSourceLoan.sol
pragma solidity ^0.8.20;
import "@delegate/IDelegateRegistry.sol";
import "@solmate/tokens/ERC20.sol";
import "@solmate/tokens/ERC721.sol";
import "@solmate/utils/FixedPointMathLib.sol";
import "@solmate/utils/ReentrancyGuard.sol";
import "@solmate/utils/SafeTransferLib.sol";
import "../../interfaces/INFTFlashAction.sol";
import "../../interfaces/loans/IMultiSourceLoan.sol";
import "../../interfaces/ILoanLiquidator.sol";
import "../utils/Hash.sol";
import "../utils/Interest.sol";
import "../Multicall.sol";
import "./WithCallbacks.sol";
contract MultiSourceLoan is IMultiSourceLoan, Multicall, ReentrancyGuard, WithCallbacks {
using FixedPointMathLib for uint256;
using Hash for Loan;
using Hash for SignableRepaymentData;
using Hash for RenegotiationOffer;
using Interest for uint256;
using MessageHashUtils for bytes32;
using SafeTransferLib for ERC20;
mapping(uint256 => bytes32) private _loans;
uint256 private _maxSources;
uint256 private _minLockPeriod;
uint256 private constant _MAX_RATIO_SOURCE_MIN_PRINCIPAL = 2;
IDelegateRegistry private _delegateRegistry;
INFTFlashAction private _flashActionContract;
event MaxSourcesUpdated(uint256 newMax);
event LoanEmitted(uint256 loanId, uint256 offerId, Loan loan, address lender, address borrower, uint256 fee);
event LoanRefinanced(uint256 renegotiationId, uint256 oldLoanId, uint256 newLoanId, Loan loan, uint256 fee);
event LoanRepaid(uint256 loanId, uint256 totalRepayment, uint256 fee);
event DelegateRegistryUpdated(address newdelegateRegistry);
event Delegated(uint256 loanId, address delegate, bool value);
event FlashActionContractUpdated(address newFlashActionContract);
event FlashActionExecuted(uint256 loanId, address target, bytes data);
event RevokeDelegate(address delegate, address collection, uint256 tokenId);
event LoanExtended(uint256 oldLoanId, uint256 newLoanId, Loan loan, uint256 _extension);
event MinLockPeriodUpdated(uint256 minLockPeriod);
error InvalidMethodError();
error InvalidRenegotiationOfferError();
error TooManySourcesError(uint256 sources);
error MinLockPeriodTooHighError(uint256 minLockPeriod);
error PartialOfferCannotChangeDurationError();
error PartialOfferCannotHaveFeeError();
error LoanExpiredError();
error RefinanceFullError();
error LengthMismatchError();
error TargetPrincipalTooLowError(uint256 sourcePrincipal, uint256 loanPrincipal);
error NFTNotReturnedError();
error ExtensionNotAvailableError();
error SourceCannotBeRefinancedError(uint256 minTimestamp);
constructor(
address loanLiquidator,
ProtocolFee memory protocolFee,
address currencyManager,
address collectionManager,
uint256 maxSources,
uint256 minLockPeriod,
address delegateRegistry,
address flashActionContract
) WithCallbacks("GONDI_MULTI_SOURCE_LOAN", currencyManager, collectionManager) {
_checkAddressNotZero(loanLiquidator);
_loanLiquidator = ILoanLiquidator(loanLiquidator);
_protocolFee = protocolFee;
_maxSources = maxSources;
_minLockPeriod = minLockPeriod;
_delegateRegistry = IDelegateRegistry(delegateRegistry);
_flashActionContract = INFTFlashAction(flashActionContract);
}
function emitLoan(LoanExecutionData calldata _executionData) external nonReentrant returns (uint256, Loan memory) {
address lender = _executionData.lender;
address borrower = _executionData.borrower;
LoanOffer calldata offer = _executionData.executionData.offer;
address offerer = offer.lender == address(0) ? borrower : lender;
_validateExecutionData(
_executionData.executionData,
lender,
borrower,
offerer,
_executionData.lenderOfferSignature,
_executionData.borrowerOfferSignature
);
uint256 loanId = _getAndSetNewLoanId();
uint256 amount = _executionData.executionData.amount;
Source[] memory source = new Source[](1);
source[0] = Source(loanId, lender, amount, 0, block.timestamp, offer.aprBps);
Loan memory loan = Loan(
borrower,
_executionData.executionData.tokenId,
offer.nftCollateralAddress,
offer.principalAddress,
amount,
block.timestamp,
offer.duration,
source
);
_loans[loanId] = loan.hash();
uint256 fee = offer.fee.mulDivUp(amount, offer.principalAmount);
ProtocolFee memory protocolFee = _protocolFee;
_handleProtocolFeeForFee(
offer.principalAddress, lender, fee.mulDivUp(protocolFee.fraction, _PRECISION), protocolFee
);
ERC20(offer.principalAddress).safeTransferFrom(lender, borrower, amount - fee);
uint128 tax = _handleAfterPrincipalTransferCallback(loan, _executionData.executionData.callbackData, fee);
if (tax > 0) {
uint256 taxCost = amount.mulDivUp(tax, _PRECISION);
uint256 feeTax = taxCost.mulDivUp(protocolFee.fraction, _PRECISION);
ERC20(offer.principalAddress).safeTransferFrom(borrower, lender, taxCost - feeTax);
if (feeTax > 0) {
ERC20(offer.principalAddress).safeTransferFrom(borrower, protocolFee.recipient, feeTax);
}
}
ERC721(offer.nftCollateralAddress).transferFrom(borrower, address(this), _executionData.executionData.tokenId);
emit LoanEmitted(loanId, offer.offerId, loan, lender, borrower, offer.fee);
if (offer.capacity > 0) {
_used[offerer][offer.offerId] += amount;
} else {
isOfferCancelled[offerer][offer.offerId] = true;
}
return (loanId, loan);
}
function refinanceFull(
RenegotiationOffer calldata _renegotiationOffer,
Loan memory _loan,
bytes calldata _renegotiationOfferSignature
) external returns (uint256, Loan memory) {
uint256 loanId = _renegotiationOffer.loanId;
address sender = msg.sender;
bool clearsInterest = false;
_baseLoanChecks(loanId, _loan);
_baseRenegotiationChecks(_renegotiationOffer, _loan);
bool strictImprovement = msg.sender == _renegotiationOffer.lender;
(uint256 totalDelta, uint256 totalAccruedInterest, uint256 totalNewSources, uint256 totalAnnualInterest) =
_processOldSources(_renegotiationOffer, _loan, strictImprovement);
if (totalNewSources > 1) {
revert RefinanceFullError();
}
if (strictImprovement) {
_checkStrictlyBetter(
_renegotiationOffer.principalAmount,
totalDelta,
_renegotiationOffer.duration + block.timestamp,
_loan.duration + _loan.startTime,
_renegotiationOffer.aprBps,
totalAnnualInterest / _loan.principalAmount,
_renegotiationOffer.fee
);
} else if (sender != _loan.borrower) {
revert OnlyLenderOrBorrowerCallableError();
} else {
clearsInterest = true;
_checkSignature(_renegotiationOffer.lender, _renegotiationOffer.hash(), _renegotiationOfferSignature);
}
uint256 netNewLender = _renegotiationOffer.principalAmount - _renegotiationOffer.fee;
if (clearsInterest) {
netNewLender -= totalAccruedInterest;
totalAccruedInterest = 0;
}
if (totalDelta > netNewLender) {
ERC20(_loan.principalAddress).safeTransferFrom(
_loan.borrower, _renegotiationOffer.lender, totalDelta - netNewLender
);
} else if (totalDelta < netNewLender) {
ERC20(_loan.principalAddress).safeTransferFrom(
_renegotiationOffer.lender, _loan.borrower, netNewLender - totalDelta
);
}
uint256 newLoanId = _getAndSetNewLoanId();
Source[] memory newSources = new Source[](1);
newSources[0] = _getSourceFromOffer(_renegotiationOffer, totalAccruedInterest, newLoanId);
_loan.source = newSources;
_loan.duration = (block.timestamp - _loan.startTime) + _renegotiationOffer.duration;
_loan.principalAmount = _renegotiationOffer.principalAmount;
_loans[newLoanId] = _loan.hash();
delete _loans[loanId];
emit LoanRefinanced(_renegotiationOffer.renegotiationId, loanId, newLoanId, _loan, _renegotiationOffer.fee);
return (newLoanId, _loan);
}
function refinancePartial(RenegotiationOffer calldata _renegotiationOffer, Loan memory _loan)
external
returns (uint256, Loan memory)
{
uint256 loanId = _renegotiationOffer.loanId;
if (_renegotiationOffer.principalAmount < _getMinSourcePrincipal(_loan.principalAmount)) {
revert TargetPrincipalTooLowError(_renegotiationOffer.principalAmount, _loan.principalAmount);
}
if (msg.sender != _renegotiationOffer.lender) {
revert OnlyLenderCallableError();
}
_baseLoanChecks(loanId, _loan);
_baseRenegotiationChecks(_renegotiationOffer, _loan);
if (_renegotiationOffer.duration > 0) {
revert PartialOfferCannotChangeDurationError();
}
if (_renegotiationOffer.fee > 0) {
revert PartialOfferCannotHaveFeeError();
}
(uint256 totalDelta, uint256 totalAccruedInterest, uint256 totalNewSources,) =
_processOldSources(_renegotiationOffer, _loan, true);
if (totalDelta != _renegotiationOffer.principalAmount) {
revert InvalidRenegotiationOfferError();
}
if (totalNewSources > _maxSources) {
revert TooManySourcesError(totalNewSources);
}
uint256 newLoanId = _getAndSetNewLoanId();
Source[] memory newSources = new Source[](totalNewSources);
newSources[0] = _getSourceFromOffer(_renegotiationOffer, totalAccruedInterest, newLoanId);
uint256 j = 1;
for (uint256 i = 0; i < _renegotiationOffer.targetPrincipal.length;) {
if (_renegotiationOffer.targetPrincipal[i] > 0) {
newSources[j] = _loan.source[i];
newSources[j].principalAmount = _renegotiationOffer.targetPrincipal[i];
unchecked {
++j;
}
}
unchecked {
++i;
}
}
_loan.source = newSources;
_loans[newLoanId] = _loan.hash();
delete _loans[loanId];
emit LoanRefinanced(_renegotiationOffer.renegotiationId, loanId, newLoanId, _loan, 0);
return (newLoanId, _loan);
}
function extendLoan(uint256 _loanId, Loan memory _loan, uint256 _extension)
external
returns (uint256, Loan memory)
{
_baseLoanChecks(_loanId, _loan);
if (_loan.source.length > 1) {
revert ExtensionNotAvailableError();
}
uint256 unlockedTime = _getUnlockedTime(_loan.source[0].startTime, _loan.startTime + _loan.duration);
if (unlockedTime > block.timestamp) {
revert SourceCannotBeRefinancedError(unlockedTime);
}
if (_loan.source[0].lender != msg.sender) {
revert OnlyLenderCallableError();
}
_loan.duration += _extension;
uint256 newLoanId = _getAndSetNewLoanId();
_loans[newLoanId] = _loan.hash();
delete _loans[_loanId];
emit LoanExtended(_loanId, newLoanId, _loan, _extension);
return (newLoanId, _loan);
}
function repayLoan(LoanRepaymentData calldata _repaymentData) external override nonReentrant {
uint256 loanId = _repaymentData.data.loanId;
Loan calldata loan = _repaymentData.loan;
if (msg.sender != loan.borrower) {
_checkSignature(loan.borrower, _repaymentData.data.hash(), _repaymentData.borrowerSignature);
}
_baseLoanChecks(loanId, loan);
if (_repaymentData.data.shouldDelegate) {
_delegateRegistry.delegateERC721(
loan.borrower, loan.nftCollateralAddress, loan.nftCollateralTokenId, bytes32(""), true
);
}
ERC721(loan.nftCollateralAddress).transferFrom(address(this), loan.borrower, loan.nftCollateralTokenId);
uint128 taxBps = _handleAfterNFTTransferCallback(loan, _repaymentData.data.callbackData);
ProtocolFee memory protocolFee = _protocolFee;
bool withProtocolFee = protocolFee.fraction > 0;
uint256 totalProtocolFee = 0;
ERC20 asset = ERC20(loan.principalAddress);
uint256 totalRepayment = 0;
for (uint256 i = 0; i < loan.source.length;) {
Source memory source = loan.source[i];
uint256 newInterest = source.principalAmount.getInterest(source.aprBps, block.timestamp - source.startTime);
uint256 tax = source.principalAmount.mulDivUp(taxBps, _PRECISION);
uint256 thisProtocolFee = 0;
uint256 thisTaxFee = 0;
if (withProtocolFee) {
thisProtocolFee = newInterest.mulDivUp(protocolFee.fraction, _PRECISION);
thisTaxFee = tax.mulDivUp(protocolFee.fraction, _PRECISION);
totalProtocolFee += thisProtocolFee + thisTaxFee;
}
uint256 repayment =
source.principalAmount + source.accruedInterest + newInterest - thisProtocolFee + tax - thisTaxFee;
asset.safeTransferFrom(loan.borrower, source.lender, repayment);
totalRepayment += repayment;
unchecked {
++i;
}
}
emit LoanRepaid(loanId, totalRepayment, totalProtocolFee);
if (withProtocolFee) {
asset.safeTransferFrom(loan.borrower, protocolFee.recipient, totalProtocolFee);
}
delete _loans[loanId];
}
function liquidateLoan(uint256 _loanId, Loan calldata _loan)
external
override
nonReentrant
returns (bytes memory)
{
if (_loan.hash() != _loans[_loanId]) {
revert InvalidLoanError(_loanId);
}
uint256 expirationTime = _loan.startTime + _loan.duration;
address collateralAddress = _loan.nftCollateralAddress;
ERC721 collateralCollection = ERC721(collateralAddress);
if (expirationTime > block.timestamp) {
revert LoanNotDueError(expirationTime);
}
bytes memory liquidation;
if (_loan.source.length == 1) {
collateralCollection.transferFrom(address(this), _loan.source[0].lender, _loan.nftCollateralTokenId);
emit LoanForeclosed(_loanId);
delete _loans[_loanId];
} else {
collateralCollection.transferFrom(address(this), address(_loanLiquidator), _loan.nftCollateralTokenId);
liquidation = _loanLiquidator.liquidateLoan(
_loanId,
collateralAddress,
_loan.nftCollateralTokenId,
_loan.principalAddress,
_liquidationAuctionDuration,
msg.sender
);
emit LoanSentToLiquidator(_loanId, address(_loanLiquidator));
}
return liquidation;
}
function loanLiquidated(uint256 _loanId, Loan calldata _loan) external override onlyLiquidator {
if (_loan.hash() != _loans[_loanId]) {
revert InvalidLoanError(_loanId);
}
emit LoanLiquidated(_loanId);
delete _loans[_loanId];
}
function getMinSourcePrincipal(uint256 _loanPrincipal) external view returns (uint256) {
return _getMinSourcePrincipal(_loanPrincipal);
}
function delegate(uint256 _loanId, Loan calldata loan, address _delegate, bytes32 _rights, bool _value) external {
if (loan.hash() != _loans[_loanId]) {
revert InvalidLoanError(_loanId);
}
if (msg.sender != loan.borrower) {
revert OnlyBorrowerCallableError();
}
_delegateRegistry.delegateERC721(
_delegate, loan.nftCollateralAddress, loan.nftCollateralTokenId, _rights, _value
);
emit Delegated(_loanId, _delegate, _value);
}
function revokeDelegate(address _delegate, address _collection, uint256 _tokenId) external {
if (ERC721(_collection).ownerOf(_tokenId) == address(this)) {
revert InvalidMethodError();
}
_delegateRegistry.delegateERC721(_delegate, _collection, _tokenId, "", false);
emit RevokeDelegate(_delegate, _collection, _tokenId);
}
function getDelegateRegistry() external view returns (address) {
return address(_delegateRegistry);
}
function setDelegateRegistry(address _newDelegateRegistry) external onlyOwner {
_delegateRegistry = IDelegateRegistry(_newDelegateRegistry);
emit DelegateRegistryUpdated(_newDelegateRegistry);
}
function getMaxSources() external view returns (uint256) {
return _maxSources;
}
function setMaxSources(uint256 __maxSources) external onlyOwner {
_maxSources = __maxSources;
emit MaxSourcesUpdated(__maxSources);
}
function getMinLockPeriod() external view returns (uint256) {
return _minLockPeriod;
}
function setMinLockPeriod(uint256 __minLockPeriod) external onlyOwner {
_minLockPeriod = __minLockPeriod;
emit MinLockPeriodUpdated(__minLockPeriod);
}
function getLoanHash(uint256 _loanId) external view returns (bytes32) {
return _loans[_loanId];
}
function executeFlashAction(uint256 _loanId, Loan calldata _loan, address _target, bytes calldata _data) external {
if (_loan.hash() != _loans[_loanId]) {
revert InvalidLoanError(_loanId);
}
if (msg.sender != _loan.borrower) {
revert OnlyBorrowerCallableError();
}
ERC721(_loan.nftCollateralAddress).transferFrom(
address(this), address(_flashActionContract), _loan.nftCollateralTokenId
);
_flashActionContract.execute(_loan.nftCollateralAddress, _loan.nftCollateralTokenId, _target, _data);
if (ERC721(_loan.nftCollateralAddress).ownerOf(_loan.nftCollateralTokenId) != address(this)) {
revert NFTNotReturnedError();
}
emit FlashActionExecuted(_loanId, _target, _data);
}
function getFlashActionContract() external view returns (address) {
return address(_flashActionContract);
}
function setFlashActionContract(address _newFlashActionContract) external onlyOwner {
_flashActionContract = INFTFlashAction(_newFlashActionContract);
emit FlashActionContractUpdated(_newFlashActionContract);
}
function _processOldSources(
RenegotiationOffer calldata _renegotiationOffer,
Loan memory _loan,
bool _isStrictlyBetter
)
private
returns (uint256 totalDelta, uint256 totalAccruedInterest, uint256 totalNewSources, uint256 totalAnnualInterest)
{
ProtocolFee memory protocolFee = _protocolFee;
uint256 totalProtocolFee = 0;
if (protocolFee.fraction > 0 && _renegotiationOffer.fee > 0) {
totalProtocolFee = _renegotiationOffer.fee.mulDivUp(protocolFee.fraction, _PRECISION);
}
totalNewSources = 1;
for (uint256 i = 0; i < _renegotiationOffer.targetPrincipal.length;) {
Source memory source = _loan.source[i];
uint256 targetPrincipal = _renegotiationOffer.targetPrincipal[i];
(
uint256 delta,
uint256 accruedInterest,
uint256 isNewSource,
uint256 annualInterest,
uint256 thisProtocolFee
) = _processOldSource(
_renegotiationOffer.lender,
_loan.principalAddress,
source,
_loan.startTime + _loan.duration,
targetPrincipal,
protocolFee
);
_checkSourceStrictly(_isStrictlyBetter, delta, source.aprBps, _renegotiationOffer.aprBps, _minimum.interest);
totalAnnualInterest += annualInterest;
totalDelta += delta;
totalAccruedInterest += accruedInterest;
totalProtocolFee += thisProtocolFee;
totalNewSources += isNewSource;
unchecked {
++i;
}
}
_handleProtocolFeeForFee(_loan.principalAddress, _renegotiationOffer.lender, totalProtocolFee, protocolFee);
}
function _processOldSource(
address _lender,
address _principalAddress,
Source memory _source,
uint256 _endTime,
uint256 _targetPrincipal,
ProtocolFee memory protocolFee
)
private
returns (
uint256 delta,
uint256 accruedInterest,
uint256 isNewSource,
uint256 annualInterest,
uint256 thisProtocolFee
)
{
uint256 unlockedTime = _getUnlockedTime(_source.startTime, _endTime);
if (unlockedTime > block.timestamp) {
revert SourceCannotBeRefinancedError(unlockedTime);
}
delta = _source.principalAmount - _targetPrincipal;
annualInterest = _source.principalAmount * _source.aprBps;
if (delta == 0) {
return (0, 0, 1, annualInterest, 0);
}
accruedInterest = delta.getInterest(_source.aprBps, block.timestamp - _source.startTime);
if (protocolFee.fraction > 0) {
thisProtocolFee = accruedInterest.mulDivUp(protocolFee.fraction, _PRECISION);
}
uint256 proportionalAccrued = _source.accruedInterest.mulDivDown(delta, _source.principalAmount);
if (_targetPrincipal > 0) {
_source.accruedInterest -= proportionalAccrued;
isNewSource = 1;
}
accruedInterest += proportionalAccrued;
ERC20(_principalAddress).safeTransferFrom(_lender, _source.lender, delta + accruedInterest - thisProtocolFee);
}
function _baseLoanChecks(uint256 _loanId, Loan memory _loan) private view {
if (_loan.hash() != _loans[_loanId]) {
revert InvalidLoanError(_loanId);
}
if (_loan.startTime + _loan.duration < block.timestamp) {
revert LoanExpiredError();
}
}
function _baseRenegotiationChecks(RenegotiationOffer calldata _renegotiationOffer, Loan memory _loan)
private
view
{
if (
(_renegotiationOffer.principalAmount == 0)
|| (_loan.source.length != _renegotiationOffer.targetPrincipal.length)
) {
revert InvalidRenegotiationOfferError();
}
if (block.timestamp > _renegotiationOffer.expirationTime) {
revert ExpiredRenegotiationOfferError(_renegotiationOffer.expirationTime);
}
uint256 renegotiationId = _renegotiationOffer.renegotiationId;
address lender = _renegotiationOffer.lender;
if (
isRenegotiationOfferCancelled[lender][renegotiationId]
|| lenderMinRenegotiationOfferId[lender] >= renegotiationId
) {
revert CancelledRenegotiationOfferError(lender, renegotiationId);
}
}
function _getSourceFromOffer(
RenegotiationOffer memory _renegotiationOffer,
uint256 _accruedInterest,
uint256 _loanId
) private view returns (Source memory) {
return Source({
loanId: _loanId,
lender: _renegotiationOffer.lender,
principalAmount: _renegotiationOffer.principalAmount,
accruedInterest: _accruedInterest,
startTime: block.timestamp,
aprBps: _renegotiationOffer.aprBps
});
}
function _getMinSourcePrincipal(uint256 _loanPrincipal) private view returns (uint256) {
return _loanPrincipal / (_MAX_RATIO_SOURCE_MIN_PRINCIPAL * _maxSources);
}
function _handleProtocolFeeForFee(
address _principalAddress,
address _lender,
uint256 _fee,
ProtocolFee memory protocolFee
) private {
if (protocolFee.fraction > 0 && _fee > 0) {
ERC20(_principalAddress).safeTransferFrom(_lender, protocolFee.recipient, _fee);
}
}
function _checkSourceStrictly(
bool _isStrictlyBetter,
uint256 _delta,
uint256 _currentAprBps,
uint256 _targetAprBps,
uint256 _minImprovement
) private pure {
if (
_isStrictlyBetter && _delta > 0
&& ((_currentAprBps - _targetAprBps).mulDivDown(_PRECISION, _currentAprBps) < _minImprovement)
) {
revert InvalidRenegotiationOfferError();
}
}
function _getUnlockedTime(uint256 _sourceStartTime, uint256 _loanEndTime) private view returns (uint256) {
return _sourceStartTime + (_loanEndTime - _sourceStartTime).mulDivUp(_minLockPeriod, _PRECISION);
}
}
文件 23 的 29:Multicall.sol
pragma solidity ^0.8.20;
import "../interfaces/IMulticall.sol";
abstract contract Multicall is IMulticall {
function multicall(bytes[] calldata data) external payable override returns (bytes[] memory results) {
results = new bytes[](data.length);
bool success;
for (uint256 i = 0; i < data.length;) {
(success, results[i]) = address(this).delegatecall(data[i]);
if (!success) revert MulticallFailed(i, results[i]);
unchecked {
++i;
}
}
}
}
文件 24 的 29:Owned.sol
pragma solidity >=0.8.0;
abstract contract Owned {
event OwnershipTransferred(address indexed user, address indexed newOwner);
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
文件 25 的 29:ReentrancyGuard.sol
pragma solidity >=0.8.0;
abstract contract ReentrancyGuard {
uint256 private locked = 1;
modifier nonReentrant() virtual {
require(locked == 1, "REENTRANCY");
locked = 2;
_;
locked = 1;
}
}
文件 26 的 29:SafeTransferLib.sol
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
library SafeTransferLib {
function safeTransferETH(address to, uint256 amount) internal {
bool success;
assembly {
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 68), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
文件 27 的 29:SignedMath.sol
pragma solidity ^0.8.20;
library SignedMath {
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
function average(int256 a, int256 b) internal pure returns (int256) {
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
function abs(int256 n) internal pure returns (uint256) {
unchecked {
return uint256(n >= 0 ? n : -n);
}
}
}
文件 28 的 29:Strings.sol
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
error StringsInsufficientHexLength(uint256 value, uint256 length);
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
文件 29 的 29:WithCallbacks.sol
pragma solidity ^0.8.20;
import "../../interfaces/callbacks/ILoanCallback.sol";
import "./BaseLoan.sol";
abstract contract WithCallbacks is BaseLoan {
struct Taxes {
uint128 buyTax;
uint128 sellTax;
}
event WhitelistedCallbackContractAdded(address contractAdded, Taxes tax);
event WhitelistedCallbackContractRemoved(address contractRemoved);
mapping(address => Taxes) private _callbackTaxes;
constructor(string memory _name, address __currencyManager, address __collectionManager)
BaseLoan(_name, __currencyManager, __collectionManager)
{}
function addWhitelistedCallbackContract(address _contract, Taxes calldata _tax) external onlyOwner {
_checkAddressNotZero(_contract);
if (_tax.buyTax > _PRECISION || _tax.sellTax > _PRECISION) {
revert InvalidValueError();
}
_isWhitelistedCallbackContract[_contract] = true;
_callbackTaxes[_contract] = _tax;
emit WhitelistedCallbackContractAdded(_contract, _tax);
}
function removeWhitelistedCallbackContract(address _contract) external onlyOwner {
_isWhitelistedCallbackContract[_contract] = false;
delete _callbackTaxes[_contract];
emit WhitelistedCallbackContractRemoved(_contract);
}
function isWhitelistedCallbackContract(address _contract) external view returns (bool) {
return _isWhitelistedCallbackContract[_contract];
}
function _handleAfterPrincipalTransferCallback(
IMultiSourceLoan.Loan memory _loan,
bytes memory _callbackData,
uint256 _fee
) internal returns (uint128) {
if (_noCallback(_callbackData)) {
return 0;
}
if (
!_isWhitelistedCallbackContract[msg.sender]
|| ILoanCallback(msg.sender).afterPrincipalTransfer(_loan, _fee, _callbackData)
!= ILoanCallback.afterPrincipalTransfer.selector
) {
revert ILoanCallback.InvalidCallbackError();
}
return _callbackTaxes[msg.sender].buyTax;
}
function _handleAfterNFTTransferCallback(IMultiSourceLoan.Loan memory _loan, bytes calldata _callbackData)
internal
returns (uint128)
{
if (_noCallback(_callbackData)) {
return 0;
}
if (
!_isWhitelistedCallbackContract[msg.sender]
|| ILoanCallback(msg.sender).afterNFTTransfer(_loan, _callbackData)
!= ILoanCallback.afterNFTTransfer.selector
) {
revert ILoanCallback.InvalidCallbackError();
}
return _callbackTaxes[msg.sender].sellTax;
}
function _noCallback(bytes memory _callbackData) private pure returns (bool) {
return _callbackData.length == 0;
}
}
{
"compilationTarget": {
"src/lib/loans/MultiSourceLoan.sol": "MultiSourceLoan"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 2000
},
"remappings": [
":@chainlink/=lib/chainlink/contracts/src/v0.8/",
":@delegate/=lib/delegate-registry/src/",
":@forge-std/=lib/forge-std/src/",
":@manifoldxyz/=lib/v3/node_modules/@manifoldxyz/",
":@openzeppelin/=lib/openzeppelin-contracts/contracts/",
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":@rari-capital/=lib/v3/node_modules/@rari-capital/",
":@solmate/=lib/solmate/src/",
":@zora/=lib/v3/contracts/",
":chainlink/=lib/chainlink/",
":delegate-registry/=lib/delegate-registry/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":murky/=lib/delegate-registry/lib/murky/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin/=lib/delegate-registry/lib/openzeppelin-contracts/contracts/",
":solmate/=lib/solmate/src/",
":test/=test/",
":v3/=lib/v3/contracts/"
],
"viaIR": true
}
[{"inputs":[{"internalType":"address","name":"loanLiquidator","type":"address"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct IBaseLoan.ProtocolFee","name":"protocolFee","type":"tuple"},{"internalType":"address","name":"currencyManager","type":"address"},{"internalType":"address","name":"collectionManager","type":"address"},{"internalType":"uint256","name":"maxSources","type":"uint256"},{"internalType":"uint256","name":"minLockPeriod","type":"uint256"},{"internalType":"address","name":"delegateRegistry","type":"address"},{"internalType":"address","name":"flashActionContract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressZeroError","type":"error"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_offerId","type":"uint256"}],"name":"CancelledOrExecutedOfferError","type":"error"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_renegotiationId","type":"uint256"}],"name":"CancelledRenegotiationOfferError","type":"error"},{"inputs":[],"name":"CannotLiquidateError","type":"error"},{"inputs":[],"name":"CollectionNotWhitelistedError","type":"error"},{"inputs":[],"name":"CurrencyNotWhitelistedError","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[{"internalType":"uint256","name":"_expirationTime","type":"uint256"}],"name":"ExpiredOfferError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_expirationTime","type":"uint256"}],"name":"ExpiredRenegotiationOfferError","type":"error"},{"inputs":[],"name":"ExtensionNotAvailableError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_principalAmount","type":"uint256"}],"name":"InvalidAmountError","type":"error"},{"inputs":[],"name":"InvalidBorrowerError","type":"error"},{"inputs":[],"name":"InvalidCallbackError","type":"error"},{"inputs":[],"name":"InvalidCollateralIdError","type":"error"},{"inputs":[],"name":"InvalidDurationError","type":"error"},{"inputs":[],"name":"InvalidLenderError","type":"error"},{"inputs":[],"name":"InvalidLiquidationError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"}],"name":"InvalidLoanError","type":"error"},{"inputs":[],"name":"InvalidMethodError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_fraction","type":"uint256"}],"name":"InvalidProtocolFeeError","type":"error"},{"inputs":[],"name":"InvalidRenegotiationOfferError","type":"error"},{"inputs":[],"name":"InvalidSignatureError","type":"error"},{"inputs":[],"name":"InvalidValueError","type":"error"},{"inputs":[],"name":"LengthMismatchError","type":"error"},{"inputs":[{"internalType":"address","name":"_liquidator","type":"address"}],"name":"LiquidatorOnlyError","type":"error"},{"inputs":[],"name":"LoanExpiredError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_expirationTime","type":"uint256"}],"name":"LoanNotDueError","type":"error"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_newMinOfferId","type":"uint256"},{"internalType":"uint256","name":"_minOfferId","type":"uint256"}],"name":"LowOfferIdError","type":"error"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_newMinRenegotiationOfferId","type":"uint256"},{"internalType":"uint256","name":"_minOfferId","type":"uint256"}],"name":"LowRenegotiationOfferIdError","type":"error"},{"inputs":[],"name":"MaxCapacityExceededError","type":"error"},{"inputs":[{"internalType":"uint256","name":"minLockPeriod","type":"uint256"}],"name":"MinLockPeriodTooHighError","type":"error"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"bytes","name":"returndata","type":"bytes"}],"name":"MulticallFailed","type":"error"},{"inputs":[],"name":"NFTNotReturnedError","type":"error"},{"inputs":[],"name":"NotStrictlyImprovedError","type":"error"},{"inputs":[],"name":"OnlyBorrowerCallableError","type":"error"},{"inputs":[],"name":"OnlyLenderCallableError","type":"error"},{"inputs":[],"name":"OnlyLenderOrBorrowerCallableError","type":"error"},{"inputs":[],"name":"PartialOfferCannotChangeDurationError","type":"error"},{"inputs":[],"name":"PartialOfferCannotHaveFeeError","type":"error"},{"inputs":[],"name":"RefinanceFullError","type":"error"},{"inputs":[{"internalType":"uint256","name":"minTimestamp","type":"uint256"}],"name":"SourceCannotBeRefinancedError","type":"error"},{"inputs":[{"internalType":"uint256","name":"sourcePrincipal","type":"uint256"},{"internalType":"uint256","name":"loanPrincipal","type":"uint256"}],"name":"TargetPrincipalTooLowError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_pendingProtocolFeeSetTime","type":"uint256"}],"name":"TooEarlyError","type":"error"},{"inputs":[{"internalType":"uint256","name":"sources","type":"uint256"}],"name":"TooManySourcesError","type":"error"},{"inputs":[],"name":"ZeroDurationError","type":"error"},{"inputs":[],"name":"ZeroInterestError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"minOfferId","type":"uint256"}],"name":"AllOffersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"minRenegotiationId","type":"uint256"}],"name":"AllRenegotiationOffersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"BorrowerOfferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newdelegateRegistry","type":"address"}],"name":"DelegateRegistryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"Delegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newFlashActionContract","type":"address"}],"name":"FlashActionContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"FlashActionExecuted","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"indexed":false,"internalType":"struct IBaseLoan.ImprovementMinimum","name":"minimum","type":"tuple"}],"name":"ImprovementMinimumUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"LiquidationAuctionDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"}],"name":"LiquidationContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"offerId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"indexed":false,"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"LoanEmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldLoanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLoanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"indexed":false,"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"_extension","type":"uint256"}],"name":"LoanExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"LoanForeclosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"LoanLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"renegotiationId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldLoanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLoanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"indexed":false,"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"LoanRefinanced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalRepayment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"LoanRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"address","name":"liquidator","type":"address"}],"name":"LoanSentToLiquidator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMax","type":"uint256"}],"name":"MaxSourcesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minLockPeriod","type":"uint256"}],"name":"MinLockPeriodUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"OfferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"indexed":false,"internalType":"struct IBaseLoan.ProtocolFee","name":"fee","type":"tuple"}],"name":"ProtocolFeePendingUpdate","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"indexed":false,"internalType":"struct IBaseLoan.ProtocolFee","name":"fee","type":"tuple"}],"name":"ProtocolFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"renegotiationId","type":"uint256"}],"name":"RenegotiationOfferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"RevokeDelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contractAdded","type":"address"},{"components":[{"internalType":"uint128","name":"buyTax","type":"uint128"},{"internalType":"uint128","name":"sellTax","type":"uint128"}],"indexed":false,"internalType":"struct WithCallbacks.Taxes","name":"tax","type":"tuple"}],"name":"WhitelistedCallbackContractAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contractRemoved","type":"address"}],"name":"WhitelistedCallbackContractRemoved","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_UPDATE_NOTICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIAL_DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PROTOCOL_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_AUCTION_DURATION","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"components":[{"internalType":"uint128","name":"buyTax","type":"uint128"},{"internalType":"uint128","name":"sellTax","type":"uint128"}],"internalType":"struct WithCallbacks.Taxes","name":"_tax","type":"tuple"}],"name":"addWhitelistedCallbackContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minOfferId","type":"uint256"}],"name":"cancelAllOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minRenegotiationId","type":"uint256"}],"name":"cancelAllRenegotiationOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_offerId","type":"uint256"}],"name":"cancelOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_offerIds","type":"uint256[]"}],"name":"cancelOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_renegotiationId","type":"uint256"}],"name":"cancelRenegotiationOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_renegotiationIds","type":"uint256[]"}],"name":"cancelRenegotiationOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"bytes32","name":"_rights","type":"bytes32"},{"internalType":"bool","name":"_value","type":"bool"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"components":[{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"capacity","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"address","name":"validator","type":"address"},{"internalType":"bytes","name":"arguments","type":"bytes"}],"internalType":"struct IBaseLoan.OfferValidator[]","name":"validators","type":"tuple[]"}],"internalType":"struct IBaseLoan.LoanOffer","name":"offer","type":"tuple"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"internalType":"struct IBaseLoan.ExecutionData","name":"executionData","type":"tuple"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"bytes","name":"lenderOfferSignature","type":"bytes"},{"internalType":"bytes","name":"borrowerOfferSignature","type":"bytes"}],"internalType":"struct IMultiSourceLoan.LoanExecutionData","name":"_executionData","type":"tuple"}],"name":"emitLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"},{"internalType":"address","name":"_target","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"executeFlashAction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"},{"internalType":"uint256","name":"_extension","type":"uint256"}],"name":"extendLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getCollectionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrencyManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDelegateRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFlashActionContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getImprovementMinimum","outputs":[{"components":[{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct IBaseLoan.ImprovementMinimum","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidationAuctionDuration","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"}],"name":"getLoanHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxSources","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMinLockPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanPrincipal","type":"uint256"}],"name":"getMinSourcePrincipal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingProtocolFee","outputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct IBaseLoan.ProtocolFee","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingProtocolFeeSetTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProtocolFee","outputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct IBaseLoan.ProtocolFee","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalLoansIssued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_offerId","type":"uint256"}],"name":"getUsedCapacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"isOfferCancelled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"isRenegotiationOfferCancelled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"isWhitelistedCallbackContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lenderMinRenegotiationOfferId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"}],"name":"liquidateLoan","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"}],"name":"loanLiquidated","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"minOfferId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"renegotiationId","type":"uint256"},{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256[]","name":"targetPrincipal","type":"uint256[]"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct IMultiSourceLoan.RenegotiationOffer","name":"_renegotiationOffer","type":"tuple"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"},{"internalType":"bytes","name":"_renegotiationOfferSignature","type":"bytes"}],"name":"refinanceFull","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"renegotiationId","type":"uint256"},{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256[]","name":"targetPrincipal","type":"uint256[]"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct IMultiSourceLoan.RenegotiationOffer","name":"_renegotiationOffer","type":"tuple"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"}],"name":"refinancePartial","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"removeWhitelistedCallbackContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"bytes","name":"callbackData","type":"bytes"},{"internalType":"bool","name":"shouldDelegate","type":"bool"}],"internalType":"struct IMultiSourceLoan.SignableRepaymentData","name":"data","type":"tuple"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"internalType":"bytes","name":"borrowerSignature","type":"bytes"}],"internalType":"struct IMultiSourceLoan.LoanRepaymentData","name":"_repaymentData","type":"tuple"}],"name":"repayLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"address","name":"_collection","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"revokeDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newDelegateRegistry","type":"address"}],"name":"setDelegateRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newFlashActionContract","type":"address"}],"name":"setFlashActionContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"__maxSources","type":"uint256"}],"name":"setMaxSources","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"__minLockPeriod","type":"uint256"}],"name":"setMinLockPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct IBaseLoan.ImprovementMinimum","name":"_newMinimum","type":"tuple"}],"name":"updateImprovementMinimum","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"_newDuration","type":"uint48"}],"name":"updateLiquidationAuctionDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ILoanLiquidator","name":"loanLiquidator","type":"address"}],"name":"updateLiquidationContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct IBaseLoan.ProtocolFee","name":"_newProtocolFee","type":"tuple"}],"name":"updateProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"}]