编译器
0.8.19+commit.7dd6d404
文件 1 的 14:Constants.sol
pragma solidity 0.8.19;
library Constants {
uint256 internal constant YEAR_IN_SECONDS = 365 days;
uint256 internal constant BASE = 1e18;
uint256 internal constant MAX_FEE_PER_ANNUM = 0.05e18;
uint256 internal constant MAX_SWAP_PROTOCOL_FEE = 0.01e18;
uint256 internal constant MAX_TOTAL_PROTOCOL_FEE = 0.05e18;
uint256 internal constant MAX_P2POOL_PROTOCOL_FEE = 0.05e18;
uint256 internal constant MIN_TIME_BETWEEN_EARLIEST_REPAY_AND_EXPIRY =
1 days;
uint256 internal constant MAX_PRICE_UPDATE_TIMESTAMP_DIVERGENCE = 1 days;
uint256 internal constant SEQUENCER_GRACE_PERIOD = 1 hours;
uint256 internal constant MIN_UNSUBSCRIBE_GRACE_PERIOD = 1 days;
uint256 internal constant MAX_UNSUBSCRIBE_GRACE_PERIOD = 14 days;
uint256 internal constant MIN_CONVERSION_GRACE_PERIOD = 1 days;
uint256 internal constant MIN_REPAYMENT_GRACE_PERIOD = 1 days;
uint256 internal constant LOAN_EXECUTION_GRACE_PERIOD = 1 days;
uint256 internal constant MAX_CONVERSION_AND_REPAYMENT_GRACE_PERIOD =
30 days;
uint256 internal constant MIN_TIME_UNTIL_FIRST_DUE_DATE = 1 days;
uint256 internal constant MIN_TIME_BETWEEN_DUE_DATES = 7 days;
uint256 internal constant MIN_WAIT_UNTIL_EARLIEST_UNSUBSCRIBE = 60 seconds;
uint256 internal constant MAX_ARRANGER_FEE = 0.5e18;
uint256 internal constant LOAN_TERMS_UPDATE_COOL_OFF_PERIOD = 15 minutes;
uint256 internal constant MAX_REPAYMENT_SCHEDULE_LENGTH = 20;
uint256 internal constant SINGLE_WRAPPER_MIN_MINT = 1000;
}
文件 2 的 14:DataTypesPeerToPeer.sol
pragma solidity 0.8.19;
library DataTypesPeerToPeer {
struct Loan {
address borrower;
address collToken;
address loanToken;
uint40 expiry;
uint40 earliestRepay;
uint128 initCollAmount;
uint128 initLoanAmount;
uint128 initRepayAmount;
uint128 amountRepaidSoFar;
uint128 amountReclaimedSoFar;
bool collUnlocked;
address collTokenCompartmentAddr;
}
struct QuoteTuple {
uint256 loanPerCollUnitOrLtv;
int256 interestRatePctInBase;
uint256 upfrontFeePctInBase;
uint256 tenor;
}
struct GeneralQuoteInfo {
address collToken;
address loanToken;
address oracleAddr;
uint256 minLoan;
uint256 maxLoan;
uint256 validUntil;
uint256 earliestRepayTenor;
address borrowerCompartmentImplementation;
bool isSingleUse;
address whitelistAddr;
bool isWhitelistAddrSingleBorrower;
}
struct OnChainQuote {
GeneralQuoteInfo generalQuoteInfo;
QuoteTuple[] quoteTuples;
bytes32 salt;
}
struct OffChainQuote {
GeneralQuoteInfo generalQuoteInfo;
bytes32 quoteTuplesRoot;
bytes32 salt;
uint256 nonce;
bytes[] compactSigs;
}
struct LoanRepayInstructions {
uint256 targetLoanId;
uint128 targetRepayAmount;
uint128 expectedTransferFee;
uint256 deadline;
address callbackAddr;
bytes callbackData;
}
struct BorrowTransferInstructions {
uint256 collSendAmount;
uint256 expectedProtocolAndVaultTransferFee;
uint256 expectedCompartmentTransferFee;
uint256 deadline;
uint256 minLoanAmount;
address callbackAddr;
bytes callbackData;
bytes mysoTokenManagerData;
}
struct TransferInstructions {
address collReceiver;
uint256 upfrontFee;
}
struct WrappedERC721TokenInfo {
address tokenAddr;
uint256[] tokenIds;
}
struct WrappedERC20TokenInfo {
address tokenAddr;
uint256 tokenAmount;
}
struct OnChainQuoteInfo {
bytes32 quoteHash;
uint256 validUntil;
}
enum WhitelistState {
NOT_WHITELISTED,
ERC20_TOKEN,
ORACLE,
COMPARTMENT,
CALLBACK,
ERC20_TOKEN_REQUIRING_COMPARTMENT,
ERC721_TOKEN,
ERC721WRAPPER,
ERC20WRAPPER,
MYSO_TOKEN_MANAGER,
QUOTE_POLICY_MANAGER
}
}
文件 3 的 14:ECDSA.sol
pragma solidity ^0.8.0;
import "../Strings.sol";
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return;
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
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);
}
}
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
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) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}
文件 4 的 14:Errors.sol
pragma solidity 0.8.19;
library Errors {
error UnregisteredVault();
error InvalidDelegatee();
error InvalidSender();
error InvalidFee();
error InsufficientSendAmount();
error NoOracle();
error InvalidOracleAnswer();
error InvalidOracleDecimals();
error InvalidOracleVersion();
error InvalidAddress();
error InvalidArrayLength();
error InvalidQuote();
error OutdatedQuote();
error InvalidOffChainSignature();
error InvalidOffChainMerkleProof();
error InvalidCollUnlock();
error InvalidAmount();
error UnknownOnChainQuote();
error NeitherTokenIsGOHM();
error NoLpTokens();
error ZeroReserve();
error IncorrectGaugeForLpToken();
error InvalidGaugeIndex();
error AlreadyStaked();
error InvalidWithdrawAmount();
error InvalidBorrower();
error OutsideValidRepayWindow();
error InvalidRepayAmount();
error ReclaimAmountIsZero();
error UnregisteredGateway();
error NonWhitelistedOracle();
error NonWhitelistedCompartment();
error NonWhitelistedCallback();
error NonWhitelistedToken();
error LtvHigherThanMax();
error InsufficientVaultFunds();
error InvalidInterestRateFactor();
error InconsistentUnlockTokenAddresses();
error InvalidEarliestRepay();
error InvalidNewMinNumOfSigners();
error AlreadySigner();
error InvalidArrayIndex();
error InvalidSignerRemoveInfo();
error InvalidSendAmount();
error TooSmallLoanAmount();
error DeadlinePassed();
error WithdrawEntered();
error DuplicateAddresses();
error OnChainQuoteAlreadyAdded();
error OffChainQuoteHasBeenInvalidated();
error Uninitialized();
error InvalidRepaymentScheduleLength();
error FirstDueDateTooCloseOrPassed();
error InvalidGracePeriod();
error UnregisteredLoanProposal();
error NotInSubscriptionPhase();
error NotInUnsubscriptionPhase();
error InsufficientBalance();
error InsufficientFreeSubscriptionSpace();
error BeforeEarliestUnsubscribe();
error InconsistentLastLoanTermsUpdateTime();
error InvalidActionForCurrentStatus();
error FellShortOfTotalSubscriptionTarget();
error InvalidRollBackRequest();
error UnsubscriptionAmountTooLarge();
error InvalidSubscriptionRange();
error InvalidMaxTotalSubscriptions();
error OutsideConversionTimeWindow();
error OutsideRepaymentTimeWindow();
error NoDefault();
error LoanIsFullyRepaid();
error RepaymentIdxTooLarge();
error AlreadyClaimed();
error AlreadyConverted();
error InvalidDueDates();
error LoanTokenDueIsZero();
error WaitForLoanTermsCoolOffPeriod();
error ZeroConversionAmount();
error InvalidNewOwnerProposal();
error CollateralMustBeCompartmentalized();
error InvalidCompartmentForToken();
error InvalidSignature();
error InvalidUpdate();
error CannotClaimOutdatedStatus();
error DelegateReducedBalance();
error FundingPoolAlreadyExists();
error InvalidLender();
error NonIncreasingTokenAddrs();
error NonIncreasingNonFungibleTokenIds();
error TransferToWrappedTokenFailed();
error TransferFromWrappedTokenFailed();
error StateAlreadySet();
error ReclaimableCollateralAmountZero();
error InvalidSwap();
error InvalidUpfrontFee();
error InvalidOracleTolerance();
error ReserveRatiosSkewedFromOraclePrice();
error SequencerDown();
error GracePeriodNotOver();
error LoanExpired();
error NoDsEth();
error TooShortTwapInterval();
error TooLongTwapInterval();
error TwapExceedsThreshold();
error Reentrancy();
error TokenNotStuck();
error InconsistentExpTransferFee();
error InconsistentExpVaultBalIncrease();
error DepositLockActive();
error DisallowedSubscriptionLockup();
error IncorrectLoanAmount();
error Disabled();
error CannotRemintUnlessZeroSupply();
error TokensStillMissingFromWrapper();
error OnlyMintFromSingleTokenWrapper();
error NonMintableTokenState();
error NoTokensTransferred();
error TokenAlreadyCountedInWrapper();
error TokenNotOwnedByWrapper();
error TokenDoesNotBelongInWrapper(address tokenAddr, uint256 tokenId);
error InvalidMintAmount();
error QuoteViolatesPolicy();
error AlreadyPublished();
error PolicyAlreadySet();
error NoPolicyToDelete();
error InvalidTenorBounds();
error InvalidLtvBounds();
error InvalidLoanPerCollBounds();
error InvalidMinApr();
error NoPolicy();
error InvalidMinFee();
}
文件 5 的 14:Helpers.sol
pragma solidity 0.8.19;
library Helpers {
function splitSignature(
bytes memory sig
) internal pure returns (bytes32 r, bytes32 vs) {
require(sig.length == 64, "invalid signature length");
assembly {
r := mload(add(sig, 32))
vs := mload(add(sig, 64))
}
}
}
文件 6 的 14:IAddressRegistry.sol
pragma solidity 0.8.19;
import {DataTypesPeerToPeer} from "../DataTypesPeerToPeer.sol";
interface IAddressRegistry {
event WhitelistStateUpdated(
address[] indexed whitelistAddrs,
DataTypesPeerToPeer.WhitelistState indexed whitelistState
);
event AllowedTokensForCompartmentUpdated(
address indexed compartmentImpl,
address[] tokens,
bool isWhitelisted
);
event BorrowerWhitelistStatusClaimed(
address indexed whitelistAuthority,
address indexed borrower,
uint256 whitelistedUntil
);
event BorrowerWhitelistUpdated(
address indexed whitelistAuthority,
address[] borrowers,
uint256 whitelistedUntil
);
event CreatedWrappedTokenForERC721s(
DataTypesPeerToPeer.WrappedERC721TokenInfo[] wrappedTokensInfo,
string name,
string symbol,
address newErc20Addr
);
event CreatedWrappedTokenForERC20s(
DataTypesPeerToPeer.WrappedERC20TokenInfo[] wrappedTokensInfo,
string name,
string symbol,
address newERC20Addr
);
function initialize(
address _lenderVaultFactory,
address _borrowerGateway,
address _quoteHandler
) external;
function addLenderVault(
address addr
) external returns (uint256 numRegisteredVaults);
function claimBorrowerWhitelistStatus(
address whitelistAuthority,
uint256 whitelistedUntil,
bytes calldata compactSig,
bytes32 salt
) external;
function createWrappedTokenForERC721s(
DataTypesPeerToPeer.WrappedERC721TokenInfo[] calldata tokensToBeWrapped,
string calldata name,
string calldata symbol,
bytes calldata mysoTokenManagerData
) external;
function createWrappedTokenForERC20s(
DataTypesPeerToPeer.WrappedERC20TokenInfo[] calldata tokensToBeWrapped,
string calldata name,
string calldata symbol,
bytes calldata mysoTokenManagerData
) external;
function updateBorrowerWhitelist(
address[] calldata borrowers,
uint256 whitelistedUntil
) external;
function setWhitelistState(
address[] calldata addrs,
DataTypesPeerToPeer.WhitelistState whitelistState
) external;
function setAllowedTokensForCompartment(
address compartmentImpl,
address[] calldata tokens,
bool allowTokensForCompartment
) external;
function transferOwnership(address newOwner) external;
function isWhitelistedBorrower(
address whitelistAuthority,
address borrower
) external view returns (bool);
function isWhitelistedERC20(address token) external view returns (bool);
function lenderVaultFactory() external view returns (address);
function borrowerGateway() external view returns (address);
function quoteHandler() external view returns (address);
function mysoTokenManager() external view returns (address);
function isRegisteredVault(address addr) external view returns (bool);
function whitelistState(
address addr
) external view returns (DataTypesPeerToPeer.WhitelistState whitelistState);
function registeredVaults()
external
view
returns (address[] memory vaultAddrs);
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function isWhitelistedCompartment(
address compartmentImpl,
address token
) external view returns (bool isWhitelisted);
function numRegisteredVaults() external view returns (uint256 numVaults);
}
文件 7 的 14:ILenderVaultImpl.sol
pragma solidity ^0.8.19;
import {DataTypesPeerToPeer} from "../DataTypesPeerToPeer.sol";
interface ILenderVaultImpl {
event AddedSigners(address[] _signers);
event MinNumberOfSignersSet(uint256 minNumSigners);
event RemovedSigner(
address signerRemoved,
uint256 signerIdx,
address signerMovedFromEnd
);
event CollateralUnlocked(
address indexed vaultOwner,
address indexed collToken,
uint256[] loanIds,
uint256 amountUnlocked
);
event QuoteProcessed(
uint256 netPledgeAmount,
DataTypesPeerToPeer.TransferInstructions transferInstructions
);
event Withdrew(address indexed tokenAddr, uint256 withdrawAmount);
event CircuitBreakerUpdated(
address indexed newCircuitBreaker,
address indexed oldCircuitBreaker
);
event ReverseCircuitBreakerUpdated(
address indexed newReverseCircuitBreaker,
address indexed oldReverseCircuitBreaker
);
event OnChainQuotingDelegateUpdated(
address indexed newOnChainQuotingDelegate,
address indexed oldOnChainQuotingDelegate
);
function initialize(address vaultOwner, address addressRegistry) external;
function unlockCollateral(
address collToken,
uint256[] calldata _loanIds
) external;
function updateLoanInfo(
uint128 repayAmount,
uint256 loanId,
uint128 collAmount,
bool noCompartment,
address collToken
) external;
function processQuote(
address borrower,
DataTypesPeerToPeer.BorrowTransferInstructions
calldata borrowInstructions,
DataTypesPeerToPeer.GeneralQuoteInfo calldata generalQuoteInfo,
DataTypesPeerToPeer.QuoteTuple calldata quoteTuple
)
external
returns (
DataTypesPeerToPeer.Loan calldata loan,
uint256 loanId,
DataTypesPeerToPeer.TransferInstructions memory transferInstructions
);
function withdraw(address token, uint256 amount) external;
function transferTo(
address token,
address recipient,
uint256 amount
) external;
function transferCollFromCompartment(
uint256 repayAmount,
uint256 repayAmountLeft,
uint128 reclaimCollAmount,
address borrowerAddr,
address collTokenAddr,
address callbackAddr,
address collTokenCompartmentAddr
) external;
function setMinNumOfSigners(uint256 _minNumOfSigners) external;
function addSigners(address[] calldata _signers) external;
function removeSigner(address signer, uint256 signerIdx) external;
function setCircuitBreaker(address circuitBreaker) external;
function setReverseCircuitBreaker(address reverseCircuitBreaker) external;
function setOnChainQuotingDelegate(address onChainQuotingDelegate) external;
function pauseQuotes() external;
function unpauseQuotes() external;
function transferOwnership(address newOwner) external;
function loan(
uint256 index
) external view returns (DataTypesPeerToPeer.Loan memory loan);
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function totalNumSigners() external view returns (uint256);
function getTokenBalancesAndLockedAmounts(
address[] calldata tokens
)
external
view
returns (uint256[] memory balances, uint256[] memory _lockedAmounts);
function addressRegistry() external view returns (address);
function circuitBreaker() external view returns (address);
function reverseCircuitBreaker() external view returns (address);
function onChainQuotingDelegate() external view returns (address);
function signers(uint256 index) external view returns (address);
function minNumOfSigners() external view returns (uint256);
function isSigner(address signer) external view returns (bool);
function withdrawEntered() external view returns (bool);
function lockedAmounts(address token) external view returns (uint256);
function totalNumLoans() external view returns (uint256);
}
文件 8 的 14:IQuoteHandler.sol
pragma solidity ^0.8.19;
import {DataTypesPeerToPeer} from "../DataTypesPeerToPeer.sol";
interface IQuoteHandler {
event OnChainQuoteAdded(
address indexed lenderVault,
DataTypesPeerToPeer.OnChainQuote onChainQuote,
bytes32 indexed onChainQuoteHash
);
event OnChainQuoteDeleted(
address indexed lenderVault,
bytes32 indexed onChainQuoteHash
);
event OnChainQuoteInvalidated(
address indexed lenderVault,
bytes32 indexed onChainQuoteHash
);
event OffChainQuoteNonceIncremented(
address indexed lenderVault,
uint256 newNonce
);
event OffChainQuoteInvalidated(
address indexed lenderVault,
bytes32 indexed offChainQuoteHash
);
event OnChainQuoteUsed(
address indexed lenderVault,
bytes32 indexed onChainQuoteHash,
uint256 indexed toBeRegisteredLoanId,
uint256 quoteTupleIdx
);
event OffChainQuoteUsed(
address indexed lenderVault,
bytes32 indexed offChainQuoteHash,
uint256 indexed toBeRegisteredLoanId,
DataTypesPeerToPeer.QuoteTuple quoteTuple
);
event QuotePolicyManagerUpdated(
address indexed lenderVault,
address indexed newPolicyManagerAddress
);
event OnChainQuotePublished(
DataTypesPeerToPeer.OnChainQuote onChainQuote,
bytes32 indexed onChainQuoteHash,
address indexed proposer
);
event OnChainQuoteCopied(
address indexed lenderVault,
bytes32 indexed onChainQuoteHash
);
function addOnChainQuote(
address lenderVault,
DataTypesPeerToPeer.OnChainQuote calldata onChainQuote
) external;
function updateOnChainQuote(
address lenderVault,
bytes32 oldOnChainQuoteHash,
DataTypesPeerToPeer.OnChainQuote calldata newOnChainQuote
) external;
function deleteOnChainQuote(
address lenderVault,
bytes32 onChainQuoteHash
) external;
function copyPublishedOnChainQuote(
address lenderVault,
bytes32 onChainQuoteHash
) external;
function publishOnChainQuote(
DataTypesPeerToPeer.OnChainQuote calldata onChainQuote
) external;
function incrementOffChainQuoteNonce(address lenderVault) external;
function invalidateOffChainQuote(
address lenderVault,
bytes32 offChainQuoteHash
) external;
function checkAndRegisterOnChainQuote(
address borrower,
address lenderVault,
uint256 quoteTupleIdx,
DataTypesPeerToPeer.OnChainQuote memory onChainQuote
) external;
function checkAndRegisterOffChainQuote(
address borrower,
address lenderVault,
DataTypesPeerToPeer.OffChainQuote calldata offChainQuote,
DataTypesPeerToPeer.QuoteTuple calldata quoteTuple,
bytes32[] memory proof
) external;
function updateQuotePolicyManagerForVault(
address lenderVault,
address newPolicyManagerAddress
) external;
function addressRegistry() external view returns (address);
function offChainQuoteNonce(address lender) external view returns (uint256);
function offChainQuoteIsInvalidated(
address lenderVault,
bytes32 hashToCheck
) external view returns (bool);
function isOnChainQuote(
address lenderVault,
bytes32 hashToCheck
) external view returns (bool);
function isPublishedOnChainQuote(
bytes32 hashToCheck
) external view returns (bool);
function publishedOnChainQuoteValidUntil(
bytes32 hashToCheck
) external view returns (uint256);
function quotePolicyManagerForVault(
address lenderVault
) external view returns (address);
function getOnChainQuoteHistory(
address lenderVault,
uint256 idx
) external view returns (DataTypesPeerToPeer.OnChainQuoteInfo memory);
function getOnChainQuoteHistorySlice(
address lenderVault,
uint256 startIdx,
uint256 endIdx
) external view returns (DataTypesPeerToPeer.OnChainQuoteInfo[] memory);
function getOnChainQuoteHistoryLength(
address lenderVault
) external view returns (uint256);
}
文件 9 的 14:IQuotePolicyManager.sol
pragma solidity 0.8.19;
import {DataTypesPeerToPeer} from "../../DataTypesPeerToPeer.sol";
interface IQuotePolicyManager {
event PairPolicySet(
address indexed lenderVault,
address indexed collToken,
address indexed loanToken,
bytes singlePolicyData
);
event GlobalPolicySet(address indexed lenderVault, bytes globalPolicyData);
function setGlobalPolicy(
address lenderVault,
bytes calldata globalPolicyData
) external;
function setPairPolicy(
address lenderVault,
address collToken,
address loanToken,
bytes calldata pairPolicyData
) external;
function isAllowed(
address borrower,
address lenderVault,
DataTypesPeerToPeer.GeneralQuoteInfo calldata generalQuoteInfo,
DataTypesPeerToPeer.QuoteTuple calldata quoteTuple
) external view returns (bool _isAllowed, uint256 minNumOfSignersOverwrite);
function addressRegistry() external view returns (address);
}
文件 10 的 14:Math.sol
pragma solidity ^0.8.0;
library Math {
enum Rounding {
Down,
Up,
Zero
}
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) {
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
require(denominator > prod1, "Math: mulDiv overflow");
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (~denominator + 1);
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 (rounding == Rounding.Up && 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 + (rounding == Rounding.Up && 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 + (rounding == Rounding.Up && 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 + (rounding == Rounding.Up && 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 + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
文件 11 的 14:MerkleProof.sol
pragma solidity ^0.8.0;
library MerkleProof {
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
require(leavesLen + proofLen - 1 == totalHashes, "MerkleProof: invalid multiproof");
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
require(proofPos == proofLen, "MerkleProof: invalid multiproof");
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
require(leavesLen + proofLen - 1 == totalHashes, "MerkleProof: invalid multiproof");
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
require(proofPos == proofLen, "MerkleProof: invalid multiproof");
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
文件 12 的 14:QuoteHandler.sol
pragma solidity 0.8.19;
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {Constants} from "../Constants.sol";
import {DataTypesPeerToPeer} from "./DataTypesPeerToPeer.sol";
import {Errors} from "../Errors.sol";
import {Helpers} from "../Helpers.sol";
import {IAddressRegistry} from "./interfaces/IAddressRegistry.sol";
import {ILenderVaultImpl} from "./interfaces/ILenderVaultImpl.sol";
import {IQuoteHandler} from "./interfaces/IQuoteHandler.sol";
import {IQuotePolicyManager} from "./interfaces/policyManagers/IQuotePolicyManager.sol";
contract QuoteHandler is IQuoteHandler {
using ECDSA for bytes32;
address public immutable addressRegistry;
mapping(address => uint256) public offChainQuoteNonce;
mapping(address => mapping(bytes32 => bool))
public offChainQuoteIsInvalidated;
mapping(address => mapping(bytes32 => bool)) public isOnChainQuote;
mapping(bytes32 => bool) public isPublishedOnChainQuote;
mapping(bytes32 => uint256) public publishedOnChainQuoteValidUntil;
mapping(address => address) public quotePolicyManagerForVault;
mapping(address => DataTypesPeerToPeer.OnChainQuoteInfo[])
internal _onChainQuoteHistory;
constructor(address _addressRegistry) {
if (_addressRegistry == address(0)) {
revert Errors.InvalidAddress();
}
addressRegistry = _addressRegistry;
}
function addOnChainQuote(
address lenderVault,
DataTypesPeerToPeer.OnChainQuote calldata onChainQuote
) external {
_checkIsVaultAndSenderIsApproved(lenderVault, false);
if (!_isValidOnChainQuote(onChainQuote)) {
revert Errors.InvalidQuote();
}
mapping(bytes32 => bool)
storage isOnChainQuoteFromVault = isOnChainQuote[lenderVault];
bytes32 onChainQuoteHash = _hashOnChainQuote(onChainQuote);
if (isOnChainQuoteFromVault[onChainQuoteHash]) {
revert Errors.OnChainQuoteAlreadyAdded();
}
_onChainQuoteHistory[lenderVault].push(
DataTypesPeerToPeer.OnChainQuoteInfo({
quoteHash: onChainQuoteHash,
validUntil: onChainQuote.generalQuoteInfo.validUntil
})
);
isOnChainQuoteFromVault[onChainQuoteHash] = true;
emit OnChainQuoteAdded(lenderVault, onChainQuote, onChainQuoteHash);
}
function updateOnChainQuote(
address lenderVault,
bytes32 oldOnChainQuoteHash,
DataTypesPeerToPeer.OnChainQuote calldata newOnChainQuote
) external {
_checkIsVaultAndSenderIsApproved(lenderVault, false);
if (!_isValidOnChainQuote(newOnChainQuote)) {
revert Errors.InvalidQuote();
}
mapping(bytes32 => bool)
storage isOnChainQuoteFromVault = isOnChainQuote[lenderVault];
bytes32 newOnChainQuoteHash = _hashOnChainQuote(newOnChainQuote);
if (isOnChainQuoteFromVault[newOnChainQuoteHash]) {
revert Errors.OnChainQuoteAlreadyAdded();
}
if (!isOnChainQuoteFromVault[oldOnChainQuoteHash]) {
revert Errors.UnknownOnChainQuote();
}
_onChainQuoteHistory[lenderVault].push(
DataTypesPeerToPeer.OnChainQuoteInfo({
quoteHash: newOnChainQuoteHash,
validUntil: newOnChainQuote.generalQuoteInfo.validUntil
})
);
isOnChainQuoteFromVault[oldOnChainQuoteHash] = false;
emit OnChainQuoteDeleted(lenderVault, oldOnChainQuoteHash);
isOnChainQuoteFromVault[newOnChainQuoteHash] = true;
emit OnChainQuoteAdded(
lenderVault,
newOnChainQuote,
newOnChainQuoteHash
);
}
function deleteOnChainQuote(
address lenderVault,
bytes32 onChainQuoteHash
) external {
_checkIsVaultAndSenderIsApproved(lenderVault, false);
mapping(bytes32 => bool)
storage isOnChainQuoteFromVault = isOnChainQuote[lenderVault];
if (!isOnChainQuoteFromVault[onChainQuoteHash]) {
revert Errors.UnknownOnChainQuote();
}
isOnChainQuoteFromVault[onChainQuoteHash] = false;
emit OnChainQuoteDeleted(lenderVault, onChainQuoteHash);
}
function copyPublishedOnChainQuote(
address lenderVault,
bytes32 onChainQuoteHash
) external {
_checkIsVaultAndSenderIsApproved(lenderVault, false);
mapping(bytes32 => bool)
storage isOnChainQuoteFromVault = isOnChainQuote[lenderVault];
uint256 validUntil = publishedOnChainQuoteValidUntil[onChainQuoteHash];
if (
!isPublishedOnChainQuote[onChainQuoteHash] ||
isOnChainQuoteFromVault[onChainQuoteHash] ||
validUntil < block.timestamp
) {
revert Errors.InvalidQuote();
}
_onChainQuoteHistory[lenderVault].push(
DataTypesPeerToPeer.OnChainQuoteInfo({
quoteHash: onChainQuoteHash,
validUntil: validUntil
})
);
isOnChainQuoteFromVault[onChainQuoteHash] = true;
emit OnChainQuoteCopied(lenderVault, onChainQuoteHash);
}
function publishOnChainQuote(
DataTypesPeerToPeer.OnChainQuote calldata onChainQuote
) external {
if (!_isValidOnChainQuote(onChainQuote)) {
revert Errors.InvalidQuote();
}
bytes32 onChainQuoteHash = _hashOnChainQuote(onChainQuote);
if (isPublishedOnChainQuote[onChainQuoteHash]) {
revert Errors.AlreadyPublished();
}
isPublishedOnChainQuote[onChainQuoteHash] = true;
publishedOnChainQuoteValidUntil[onChainQuoteHash] = onChainQuote
.generalQuoteInfo
.validUntil;
emit OnChainQuotePublished(onChainQuote, onChainQuoteHash, msg.sender);
}
function incrementOffChainQuoteNonce(address lenderVault) external {
_checkIsVaultAndSenderIsApproved(lenderVault, true);
uint256 newNonce = offChainQuoteNonce[lenderVault] + 1;
offChainQuoteNonce[lenderVault] = newNonce;
emit OffChainQuoteNonceIncremented(lenderVault, newNonce);
}
function invalidateOffChainQuote(
address lenderVault,
bytes32 offChainQuoteHash
) external {
_checkIsVaultAndSenderIsApproved(lenderVault, true);
offChainQuoteIsInvalidated[lenderVault][offChainQuoteHash] = true;
emit OffChainQuoteInvalidated(lenderVault, offChainQuoteHash);
}
function checkAndRegisterOnChainQuote(
address borrower,
address lenderVault,
uint256 quoteTupleIdx,
DataTypesPeerToPeer.OnChainQuote calldata onChainQuote
) external {
if (quoteTupleIdx >= onChainQuote.quoteTuples.length) {
revert Errors.InvalidArrayIndex();
}
_checkSenderAndPolicyAndQuoteInfo(
borrower,
lenderVault,
onChainQuote.generalQuoteInfo,
onChainQuote.quoteTuples[quoteTupleIdx]
);
mapping(bytes32 => bool)
storage isOnChainQuoteFromVault = isOnChainQuote[lenderVault];
bytes32 onChainQuoteHash = _hashOnChainQuote(onChainQuote);
if (!isOnChainQuoteFromVault[onChainQuoteHash]) {
revert Errors.UnknownOnChainQuote();
}
if (onChainQuote.generalQuoteInfo.isSingleUse) {
isOnChainQuoteFromVault[onChainQuoteHash] = false;
emit OnChainQuoteInvalidated(lenderVault, onChainQuoteHash);
}
uint256 nextLoanIdx = ILenderVaultImpl(lenderVault).totalNumLoans();
emit OnChainQuoteUsed(
lenderVault,
onChainQuoteHash,
nextLoanIdx,
quoteTupleIdx
);
}
function checkAndRegisterOffChainQuote(
address borrower,
address lenderVault,
DataTypesPeerToPeer.OffChainQuote calldata offChainQuote,
DataTypesPeerToPeer.QuoteTuple calldata quoteTuple,
bytes32[] calldata proof
) external {
uint256 minNumOfSignersOverwrite = _checkSenderAndPolicyAndQuoteInfo(
borrower,
lenderVault,
offChainQuote.generalQuoteInfo,
quoteTuple
);
if (offChainQuote.nonce < offChainQuoteNonce[lenderVault]) {
revert Errors.InvalidQuote();
}
mapping(bytes32 => bool)
storage offChainQuoteFromVaultIsInvalidated = offChainQuoteIsInvalidated[
lenderVault
];
bytes32 offChainQuoteHash = _hashOffChainQuote(
offChainQuote,
lenderVault
);
if (offChainQuoteFromVaultIsInvalidated[offChainQuoteHash]) {
revert Errors.OffChainQuoteHasBeenInvalidated();
}
if (
!_areValidSignatures(
lenderVault,
offChainQuoteHash,
minNumOfSignersOverwrite,
offChainQuote.compactSigs
)
) {
revert Errors.InvalidOffChainSignature();
}
bytes32 leaf = keccak256(
bytes.concat(
keccak256(
abi.encode(
quoteTuple.loanPerCollUnitOrLtv,
quoteTuple.interestRatePctInBase,
quoteTuple.upfrontFeePctInBase,
quoteTuple.tenor
)
)
)
);
if (!MerkleProof.verify(proof, offChainQuote.quoteTuplesRoot, leaf)) {
revert Errors.InvalidOffChainMerkleProof();
}
if (offChainQuote.generalQuoteInfo.isSingleUse) {
offChainQuoteFromVaultIsInvalidated[offChainQuoteHash] = true;
emit OffChainQuoteInvalidated(lenderVault, offChainQuoteHash);
}
uint256 toBeRegisteredLoanId = ILenderVaultImpl(lenderVault)
.totalNumLoans();
emit OffChainQuoteUsed(
lenderVault,
offChainQuoteHash,
toBeRegisteredLoanId,
quoteTuple
);
}
function updateQuotePolicyManagerForVault(
address lenderVault,
address newPolicyManagerAddress
) external {
_checkIsVaultAndSenderIsApproved(lenderVault, true);
if (newPolicyManagerAddress == address(0)) {
delete quotePolicyManagerForVault[lenderVault];
} else {
if (
IAddressRegistry(addressRegistry).whitelistState(
newPolicyManagerAddress
) !=
DataTypesPeerToPeer.WhitelistState.QUOTE_POLICY_MANAGER ||
newPolicyManagerAddress ==
quotePolicyManagerForVault[lenderVault]
) {
revert Errors.InvalidAddress();
}
quotePolicyManagerForVault[lenderVault] = newPolicyManagerAddress;
}
emit QuotePolicyManagerUpdated(lenderVault, newPolicyManagerAddress);
}
function getOnChainQuoteHistory(
address lenderVault,
uint256 idx
) external view returns (DataTypesPeerToPeer.OnChainQuoteInfo memory) {
if (idx < _onChainQuoteHistory[lenderVault].length) {
return _onChainQuoteHistory[lenderVault][idx];
} else {
revert Errors.InvalidArrayIndex();
}
}
function getOnChainQuoteHistorySlice(
address lenderVault,
uint256 startIdx,
uint256 endIdx
) external view returns (DataTypesPeerToPeer.OnChainQuoteInfo[] memory) {
uint256 onChainQuoteHistoryLen = _onChainQuoteHistory[lenderVault]
.length;
if (startIdx > endIdx || startIdx >= onChainQuoteHistoryLen) {
revert Errors.InvalidArrayIndex();
}
endIdx = endIdx < onChainQuoteHistoryLen
? endIdx
: onChainQuoteHistoryLen;
if (startIdx == 0 && endIdx == onChainQuoteHistoryLen) {
return _onChainQuoteHistory[lenderVault];
}
DataTypesPeerToPeer.OnChainQuoteInfo[]
memory onChainQuoteHistoryRequested = new DataTypesPeerToPeer.OnChainQuoteInfo[](
endIdx - startIdx
);
for (uint256 i = startIdx; i < endIdx; ) {
onChainQuoteHistoryRequested[i - startIdx] = _onChainQuoteHistory[
lenderVault
][i];
unchecked {
++i;
}
}
return onChainQuoteHistoryRequested;
}
function getOnChainQuoteHistoryLength(
address lenderVault
) external view returns (uint256) {
return _onChainQuoteHistory[lenderVault].length;
}
function _areValidSignatures(
address lenderVault,
bytes32 offChainQuoteHash,
uint256 minNumOfSignersOverwrite,
bytes[] calldata compactSigs
) internal view returns (bool) {
uint256 compactSigsLength = compactSigs.length;
uint256 minNumOfSigners = minNumOfSignersOverwrite == 0
? ILenderVaultImpl(lenderVault).minNumOfSigners()
: minNumOfSignersOverwrite;
if (compactSigsLength < minNumOfSigners) {
return false;
}
bytes32 messageHash = ECDSA.toEthSignedMessageHash(offChainQuoteHash);
address recoveredSigner;
address prevSigner;
for (uint256 i; i < compactSigsLength; ) {
(bytes32 r, bytes32 vs) = Helpers.splitSignature(compactSigs[i]);
recoveredSigner = messageHash.recover(r, vs);
if (!ILenderVaultImpl(lenderVault).isSigner(recoveredSigner)) {
return false;
}
if (recoveredSigner <= prevSigner) {
return false;
}
prevSigner = recoveredSigner;
unchecked {
++i;
}
}
return true;
}
function _hashOffChainQuote(
DataTypesPeerToPeer.OffChainQuote memory offChainQuote,
address lenderVault
) internal view returns (bytes32 quoteHash) {
quoteHash = keccak256(
abi.encode(
offChainQuote.generalQuoteInfo,
offChainQuote.quoteTuplesRoot,
offChainQuote.salt,
offChainQuote.nonce,
lenderVault,
block.chainid
)
);
}
function _checkSenderAndPolicyAndQuoteInfo(
address borrower,
address lenderVault,
DataTypesPeerToPeer.GeneralQuoteInfo calldata generalQuoteInfo,
DataTypesPeerToPeer.QuoteTuple calldata quoteTuple
) internal view returns (uint256 minNumOfSignersOverwrite) {
if (msg.sender != IAddressRegistry(addressRegistry).borrowerGateway()) {
revert Errors.InvalidSender();
}
address quotePolicyManager = quotePolicyManagerForVault[lenderVault];
if (quotePolicyManager != address(0)) {
bool isAllowed;
(isAllowed, minNumOfSignersOverwrite) = IQuotePolicyManager(
quotePolicyManager
).isAllowed(borrower, lenderVault, generalQuoteInfo, quoteTuple);
if (!isAllowed) {
revert Errors.QuoteViolatesPolicy();
}
}
_checkWhitelist(
generalQuoteInfo.collToken,
generalQuoteInfo.loanToken,
generalQuoteInfo.borrowerCompartmentImplementation,
generalQuoteInfo.oracleAddr,
_isSwap(generalQuoteInfo, quoteTuple)
);
if (generalQuoteInfo.validUntil < block.timestamp) {
revert Errors.OutdatedQuote();
}
if (
generalQuoteInfo.collToken == generalQuoteInfo.loanToken ||
generalQuoteInfo.maxLoan == 0 ||
generalQuoteInfo.minLoan == 0 ||
generalQuoteInfo.minLoan > generalQuoteInfo.maxLoan
) {
revert Errors.InvalidQuote();
}
if (
generalQuoteInfo.whitelistAddr != address(0) &&
((generalQuoteInfo.isWhitelistAddrSingleBorrower &&
generalQuoteInfo.whitelistAddr != borrower) ||
(!generalQuoteInfo.isWhitelistAddrSingleBorrower &&
!IAddressRegistry(addressRegistry).isWhitelistedBorrower(
generalQuoteInfo.whitelistAddr,
borrower
)))
) {
revert Errors.InvalidBorrower();
}
}
function _isValidOnChainQuote(
DataTypesPeerToPeer.OnChainQuote calldata onChainQuote
) internal view returns (bool) {
if (
onChainQuote.generalQuoteInfo.collToken ==
onChainQuote.generalQuoteInfo.loanToken
) {
return false;
}
if (onChainQuote.generalQuoteInfo.validUntil < block.timestamp) {
return false;
}
if (
onChainQuote.generalQuoteInfo.maxLoan == 0 ||
onChainQuote.generalQuoteInfo.minLoan == 0 ||
onChainQuote.generalQuoteInfo.minLoan >
onChainQuote.generalQuoteInfo.maxLoan
) {
return false;
}
uint256 quoteTuplesLen = onChainQuote.quoteTuples.length;
if (quoteTuplesLen == 0) {
return false;
}
bool isSwap;
for (uint256 k; k < quoteTuplesLen; ) {
(bool isValid, bool isSwapCurr) = _isValidOnChainQuoteTuple(
onChainQuote.generalQuoteInfo,
onChainQuote.quoteTuples[k]
);
if (!isValid) {
return false;
}
if (isSwapCurr && quoteTuplesLen > 1) {
return false;
}
isSwap = isSwapCurr;
unchecked {
++k;
}
}
_checkWhitelist(
onChainQuote.generalQuoteInfo.collToken,
onChainQuote.generalQuoteInfo.loanToken,
onChainQuote.generalQuoteInfo.borrowerCompartmentImplementation,
onChainQuote.generalQuoteInfo.oracleAddr,
isSwap
);
return true;
}
function _checkWhitelist(
address collToken,
address loanToken,
address compartmentImpl,
address oracleAddr,
bool isSwap
) internal view {
if (
!IAddressRegistry(addressRegistry).isWhitelistedERC20(loanToken) ||
!IAddressRegistry(addressRegistry).isWhitelistedERC20(collToken)
) {
revert Errors.NonWhitelistedToken();
}
if (isSwap) {
if (compartmentImpl != address(0)) {
revert Errors.InvalidSwap();
}
return;
}
if (compartmentImpl == address(0)) {
DataTypesPeerToPeer.WhitelistState collTokenWhitelistState = IAddressRegistry(
addressRegistry
).whitelistState(collToken);
if (
collTokenWhitelistState ==
DataTypesPeerToPeer
.WhitelistState
.ERC20_TOKEN_REQUIRING_COMPARTMENT
) {
revert Errors.CollateralMustBeCompartmentalized();
}
} else {
if (
!IAddressRegistry(addressRegistry).isWhitelistedCompartment(
compartmentImpl,
collToken
)
) {
revert Errors.InvalidCompartmentForToken();
}
}
if (
oracleAddr != address(0) &&
IAddressRegistry(addressRegistry).whitelistState(oracleAddr) !=
DataTypesPeerToPeer.WhitelistState.ORACLE
) {
revert Errors.NonWhitelistedOracle();
}
}
function _checkIsVaultAndSenderIsApproved(
address lenderVault,
bool onlyOwner
) internal view {
if (!IAddressRegistry(addressRegistry).isRegisteredVault(lenderVault)) {
revert Errors.UnregisteredVault();
}
if (
ILenderVaultImpl(lenderVault).owner() != msg.sender &&
(onlyOwner ||
ILenderVaultImpl(lenderVault).onChainQuotingDelegate() !=
msg.sender)
) {
revert Errors.InvalidSender();
}
}
function _hashOnChainQuote(
DataTypesPeerToPeer.OnChainQuote memory onChainQuote
) internal pure returns (bytes32 quoteHash) {
quoteHash = keccak256(abi.encode(onChainQuote));
}
function _isValidOnChainQuoteTuple(
DataTypesPeerToPeer.GeneralQuoteInfo calldata generalQuoteInfo,
DataTypesPeerToPeer.QuoteTuple calldata quoteTuple
) internal pure returns (bool, bool) {
bool isSwap = _isSwap(generalQuoteInfo, quoteTuple);
if (quoteTuple.upfrontFeePctInBase < Constants.BASE) {
if (
quoteTuple.tenor <
generalQuoteInfo.earliestRepayTenor +
Constants.MIN_TIME_BETWEEN_EARLIEST_REPAY_AND_EXPIRY
) {
return (false, isSwap);
}
} else if (quoteTuple.upfrontFeePctInBase == Constants.BASE) {
if (!isSwap) {
return (false, isSwap);
}
} else {
return (false, isSwap);
}
if (quoteTuple.loanPerCollUnitOrLtv == 0) {
return (false, isSwap);
}
if (
generalQuoteInfo.oracleAddr != address(0) &&
quoteTuple.loanPerCollUnitOrLtv > Constants.BASE &&
generalQuoteInfo.whitelistAddr == address(0)
) {
return (false, isSwap);
}
if (quoteTuple.interestRatePctInBase + int(Constants.BASE) <= 0) {
return (false, isSwap);
}
return (true, isSwap);
}
function _isSwap(
DataTypesPeerToPeer.GeneralQuoteInfo calldata generalQuoteInfo,
DataTypesPeerToPeer.QuoteTuple calldata quoteTuple
) internal pure returns (bool) {
return
quoteTuple.upfrontFeePctInBase == Constants.BASE &&
quoteTuple.tenor + generalQuoteInfo.earliestRepayTenor == 0 &&
quoteTuple.interestRatePctInBase == 0 &&
generalQuoteInfo.borrowerCompartmentImplementation == address(0);
}
}
文件 13 的 14:SignedMath.sol
pragma solidity ^0.8.0;
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);
}
}
}
文件 14 的 14:Strings.sol
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
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), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(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) {
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] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
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 keccak256(bytes(a)) == keccak256(bytes(b));
}
}
{
"compilationTarget": {
"contracts/peer-to-peer/QuoteHandler.sol": "QuoteHandler"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_addressRegistry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyPublished","type":"error"},{"inputs":[],"name":"CollateralMustBeCompartmentalized","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidArrayIndex","type":"error"},{"inputs":[],"name":"InvalidBorrower","type":"error"},{"inputs":[],"name":"InvalidCompartmentForToken","type":"error"},{"inputs":[],"name":"InvalidOffChainMerkleProof","type":"error"},{"inputs":[],"name":"InvalidOffChainSignature","type":"error"},{"inputs":[],"name":"InvalidQuote","type":"error"},{"inputs":[],"name":"InvalidSender","type":"error"},{"inputs":[],"name":"InvalidSwap","type":"error"},{"inputs":[],"name":"NonWhitelistedOracle","type":"error"},{"inputs":[],"name":"NonWhitelistedToken","type":"error"},{"inputs":[],"name":"OffChainQuoteHasBeenInvalidated","type":"error"},{"inputs":[],"name":"OnChainQuoteAlreadyAdded","type":"error"},{"inputs":[],"name":"OutdatedQuote","type":"error"},{"inputs":[],"name":"QuoteViolatesPolicy","type":"error"},{"inputs":[],"name":"UnknownOnChainQuote","type":"error"},{"inputs":[],"name":"UnregisteredVault","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lenderVault","type":"address"},{"indexed":true,"internalType":"bytes32","name":"offChainQuoteHash","type":"bytes32"}],"name":"OffChainQuoteInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lenderVault","type":"address"},{"indexed":false,"internalType":"uint256","name":"newNonce","type":"uint256"}],"name":"OffChainQuoteNonceIncremented","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lenderVault","type":"address"},{"indexed":true,"internalType":"bytes32","name":"offChainQuoteHash","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"toBeRegisteredLoanId","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanPerCollUnitOrLtv","type":"uint256"},{"internalType":"int256","name":"interestRatePctInBase","type":"int256"},{"internalType":"uint256","name":"upfrontFeePctInBase","type":"uint256"},{"internalType":"uint256","name":"tenor","type":"uint256"}],"indexed":false,"internalType":"struct DataTypesPeerToPeer.QuoteTuple","name":"quoteTuple","type":"tuple"}],"name":"OffChainQuoteUsed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lenderVault","type":"address"},{"components":[{"components":[{"internalType":"address","name":"collToken","type":"address"},{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"oracleAddr","type":"address"},{"internalType":"uint256","name":"minLoan","type":"uint256"},{"internalType":"uint256","name":"maxLoan","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"uint256","name":"earliestRepayTenor","type":"uint256"},{"internalType":"address","name":"borrowerCompartmentImplementation","type":"address"},{"internalType":"bool","name":"isSingleUse","type":"bool"},{"internalType":"address","name":"whitelistAddr","type":"address"},{"internalType":"bool","name":"isWhitelistAddrSingleBorrower","type":"bool"}],"internalType":"struct DataTypesPeerToPeer.GeneralQuoteInfo","name":"generalQuoteInfo","type":"tuple"},{"components":[{"internalType":"uint256","name":"loanPerCollUnitOrLtv","type":"uint256"},{"internalType":"int256","name":"interestRatePctInBase","type":"int256"},{"internalType":"uint256","name":"upfrontFeePctInBase","type":"uint256"},{"internalType":"uint256","name":"tenor","type":"uint256"}],"internalType":"struct DataTypesPeerToPeer.QuoteTuple[]","name":"quoteTuples","type":"tuple[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"indexed":false,"internalType":"struct DataTypesPeerToPeer.OnChainQuote","name":"onChainQuote","type":"tuple"},{"indexed":true,"internalType":"bytes32","name":"onChainQuoteHash","type":"bytes32"}],"name":"OnChainQuoteAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lenderVault","type":"address"},{"indexed":true,"internalType":"bytes32","name":"onChainQuoteHash","type":"bytes32"}],"name":"OnChainQuoteCopied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lenderVault","type":"address"},{"indexed":true,"internalType":"bytes32","name":"onChainQuoteHash","type":"bytes32"}],"name":"OnChainQuoteDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lenderVault","type":"address"},{"indexed":true,"internalType":"bytes32","name":"onChainQuoteHash","type":"bytes32"}],"name":"OnChainQuoteInvalidated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"components":[{"internalType":"address","name":"collToken","type":"address"},{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"oracleAddr","type":"address"},{"internalType":"uint256","name":"minLoan","type":"uint256"},{"internalType":"uint256","name":"maxLoan","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"uint256","name":"earliestRepayTenor","type":"uint256"},{"internalType":"address","name":"borrowerCompartmentImplementation","type":"address"},{"internalType":"bool","name":"isSingleUse","type":"bool"},{"internalType":"address","name":"whitelistAddr","type":"address"},{"internalType":"bool","name":"isWhitelistAddrSingleBorrower","type":"bool"}],"internalType":"struct DataTypesPeerToPeer.GeneralQuoteInfo","name":"generalQuoteInfo","type":"tuple"},{"components":[{"internalType":"uint256","name":"loanPerCollUnitOrLtv","type":"uint256"},{"internalType":"int256","name":"interestRatePctInBase","type":"int256"},{"internalType":"uint256","name":"upfrontFeePctInBase","type":"uint256"},{"internalType":"uint256","name":"tenor","type":"uint256"}],"internalType":"struct DataTypesPeerToPeer.QuoteTuple[]","name":"quoteTuples","type":"tuple[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"indexed":false,"internalType":"struct DataTypesPeerToPeer.OnChainQuote","name":"onChainQuote","type":"tuple"},{"indexed":true,"internalType":"bytes32","name":"onChainQuoteHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"proposer","type":"address"}],"name":"OnChainQuotePublished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lenderVault","type":"address"},{"indexed":true,"internalType":"bytes32","name":"onChainQuoteHash","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"toBeRegisteredLoanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quoteTupleIdx","type":"uint256"}],"name":"OnChainQuoteUsed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lenderVault","type":"address"},{"indexed":true,"internalType":"address","name":"newPolicyManagerAddress","type":"address"}],"name":"QuotePolicyManagerUpdated","type":"event"},{"inputs":[{"internalType":"address","name":"lenderVault","type":"address"},{"components":[{"components":[{"internalType":"address","name":"collToken","type":"address"},{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"oracleAddr","type":"address"},{"internalType":"uint256","name":"minLoan","type":"uint256"},{"internalType":"uint256","name":"maxLoan","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"uint256","name":"earliestRepayTenor","type":"uint256"},{"internalType":"address","name":"borrowerCompartmentImplementation","type":"address"},{"internalType":"bool","name":"isSingleUse","type":"bool"},{"internalType":"address","name":"whitelistAddr","type":"address"},{"internalType":"bool","name":"isWhitelistAddrSingleBorrower","type":"bool"}],"internalType":"struct DataTypesPeerToPeer.GeneralQuoteInfo","name":"generalQuoteInfo","type":"tuple"},{"components":[{"internalType":"uint256","name":"loanPerCollUnitOrLtv","type":"uint256"},{"internalType":"int256","name":"interestRatePctInBase","type":"int256"},{"internalType":"uint256","name":"upfrontFeePctInBase","type":"uint256"},{"internalType":"uint256","name":"tenor","type":"uint256"}],"internalType":"struct DataTypesPeerToPeer.QuoteTuple[]","name":"quoteTuples","type":"tuple[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"internalType":"struct DataTypesPeerToPeer.OnChainQuote","name":"onChainQuote","type":"tuple"}],"name":"addOnChainQuote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addressRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"lenderVault","type":"address"},{"components":[{"components":[{"internalType":"address","name":"collToken","type":"address"},{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"oracleAddr","type":"address"},{"internalType":"uint256","name":"minLoan","type":"uint256"},{"internalType":"uint256","name":"maxLoan","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"uint256","name":"earliestRepayTenor","type":"uint256"},{"internalType":"address","name":"borrowerCompartmentImplementation","type":"address"},{"internalType":"bool","name":"isSingleUse","type":"bool"},{"internalType":"address","name":"whitelistAddr","type":"address"},{"internalType":"bool","name":"isWhitelistAddrSingleBorrower","type":"bool"}],"internalType":"struct DataTypesPeerToPeer.GeneralQuoteInfo","name":"generalQuoteInfo","type":"tuple"},{"internalType":"bytes32","name":"quoteTuplesRoot","type":"bytes32"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes[]","name":"compactSigs","type":"bytes[]"}],"internalType":"struct DataTypesPeerToPeer.OffChainQuote","name":"offChainQuote","type":"tuple"},{"components":[{"internalType":"uint256","name":"loanPerCollUnitOrLtv","type":"uint256"},{"internalType":"int256","name":"interestRatePctInBase","type":"int256"},{"internalType":"uint256","name":"upfrontFeePctInBase","type":"uint256"},{"internalType":"uint256","name":"tenor","type":"uint256"}],"internalType":"struct DataTypesPeerToPeer.QuoteTuple","name":"quoteTuple","type":"tuple"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"checkAndRegisterOffChainQuote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"lenderVault","type":"address"},{"internalType":"uint256","name":"quoteTupleIdx","type":"uint256"},{"components":[{"components":[{"internalType":"address","name":"collToken","type":"address"},{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"oracleAddr","type":"address"},{"internalType":"uint256","name":"minLoan","type":"uint256"},{"internalType":"uint256","name":"maxLoan","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"uint256","name":"earliestRepayTenor","type":"uint256"},{"internalType":"address","name":"borrowerCompartmentImplementation","type":"address"},{"internalType":"bool","name":"isSingleUse","type":"bool"},{"internalType":"address","name":"whitelistAddr","type":"address"},{"internalType":"bool","name":"isWhitelistAddrSingleBorrower","type":"bool"}],"internalType":"struct DataTypesPeerToPeer.GeneralQuoteInfo","name":"generalQuoteInfo","type":"tuple"},{"components":[{"internalType":"uint256","name":"loanPerCollUnitOrLtv","type":"uint256"},{"internalType":"int256","name":"interestRatePctInBase","type":"int256"},{"internalType":"uint256","name":"upfrontFeePctInBase","type":"uint256"},{"internalType":"uint256","name":"tenor","type":"uint256"}],"internalType":"struct DataTypesPeerToPeer.QuoteTuple[]","name":"quoteTuples","type":"tuple[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"internalType":"struct DataTypesPeerToPeer.OnChainQuote","name":"onChainQuote","type":"tuple"}],"name":"checkAndRegisterOnChainQuote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lenderVault","type":"address"},{"internalType":"bytes32","name":"onChainQuoteHash","type":"bytes32"}],"name":"copyPublishedOnChainQuote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lenderVault","type":"address"},{"internalType":"bytes32","name":"onChainQuoteHash","type":"bytes32"}],"name":"deleteOnChainQuote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lenderVault","type":"address"},{"internalType":"uint256","name":"idx","type":"uint256"}],"name":"getOnChainQuoteHistory","outputs":[{"components":[{"internalType":"bytes32","name":"quoteHash","type":"bytes32"},{"internalType":"uint256","name":"validUntil","type":"uint256"}],"internalType":"struct DataTypesPeerToPeer.OnChainQuoteInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lenderVault","type":"address"}],"name":"getOnChainQuoteHistoryLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lenderVault","type":"address"},{"internalType":"uint256","name":"startIdx","type":"uint256"},{"internalType":"uint256","name":"endIdx","type":"uint256"}],"name":"getOnChainQuoteHistorySlice","outputs":[{"components":[{"internalType":"bytes32","name":"quoteHash","type":"bytes32"},{"internalType":"uint256","name":"validUntil","type":"uint256"}],"internalType":"struct DataTypesPeerToPeer.OnChainQuoteInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lenderVault","type":"address"}],"name":"incrementOffChainQuoteNonce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lenderVault","type":"address"},{"internalType":"bytes32","name":"offChainQuoteHash","type":"bytes32"}],"name":"invalidateOffChainQuote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"isOnChainQuote","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"isPublishedOnChainQuote","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"offChainQuoteIsInvalidated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"offChainQuoteNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"collToken","type":"address"},{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"oracleAddr","type":"address"},{"internalType":"uint256","name":"minLoan","type":"uint256"},{"internalType":"uint256","name":"maxLoan","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"uint256","name":"earliestRepayTenor","type":"uint256"},{"internalType":"address","name":"borrowerCompartmentImplementation","type":"address"},{"internalType":"bool","name":"isSingleUse","type":"bool"},{"internalType":"address","name":"whitelistAddr","type":"address"},{"internalType":"bool","name":"isWhitelistAddrSingleBorrower","type":"bool"}],"internalType":"struct DataTypesPeerToPeer.GeneralQuoteInfo","name":"generalQuoteInfo","type":"tuple"},{"components":[{"internalType":"uint256","name":"loanPerCollUnitOrLtv","type":"uint256"},{"internalType":"int256","name":"interestRatePctInBase","type":"int256"},{"internalType":"uint256","name":"upfrontFeePctInBase","type":"uint256"},{"internalType":"uint256","name":"tenor","type":"uint256"}],"internalType":"struct DataTypesPeerToPeer.QuoteTuple[]","name":"quoteTuples","type":"tuple[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"internalType":"struct DataTypesPeerToPeer.OnChainQuote","name":"onChainQuote","type":"tuple"}],"name":"publishOnChainQuote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"publishedOnChainQuoteValidUntil","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"quotePolicyManagerForVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lenderVault","type":"address"},{"internalType":"bytes32","name":"oldOnChainQuoteHash","type":"bytes32"},{"components":[{"components":[{"internalType":"address","name":"collToken","type":"address"},{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"oracleAddr","type":"address"},{"internalType":"uint256","name":"minLoan","type":"uint256"},{"internalType":"uint256","name":"maxLoan","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"uint256","name":"earliestRepayTenor","type":"uint256"},{"internalType":"address","name":"borrowerCompartmentImplementation","type":"address"},{"internalType":"bool","name":"isSingleUse","type":"bool"},{"internalType":"address","name":"whitelistAddr","type":"address"},{"internalType":"bool","name":"isWhitelistAddrSingleBorrower","type":"bool"}],"internalType":"struct DataTypesPeerToPeer.GeneralQuoteInfo","name":"generalQuoteInfo","type":"tuple"},{"components":[{"internalType":"uint256","name":"loanPerCollUnitOrLtv","type":"uint256"},{"internalType":"int256","name":"interestRatePctInBase","type":"int256"},{"internalType":"uint256","name":"upfrontFeePctInBase","type":"uint256"},{"internalType":"uint256","name":"tenor","type":"uint256"}],"internalType":"struct DataTypesPeerToPeer.QuoteTuple[]","name":"quoteTuples","type":"tuple[]"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"internalType":"struct DataTypesPeerToPeer.OnChainQuote","name":"newOnChainQuote","type":"tuple"}],"name":"updateOnChainQuote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lenderVault","type":"address"},{"internalType":"address","name":"newPolicyManagerAddress","type":"address"}],"name":"updateQuotePolicyManagerForVault","outputs":[],"stateMutability":"nonpayable","type":"function"}]