Skip to content

Commit

Permalink
Merge pull request #594 from mysofinance/statemind-172-174
Browse files Browse the repository at this point in the history
fixed tests, edited edge case for ltv on global policy and changes fo…
  • Loading branch information
jpick713 authored Aug 11, 2023
2 parents 1c20582 + a63ce43 commit f3637bb
Show file tree
Hide file tree
Showing 9 changed files with 358 additions and 113 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,13 @@ File | % Stmts | % Branch |
Helpers.sol | 100 | 50 | 100 | 100 | |
contracts\interfaces\ | 100 | 100 | 100 | 100 | |
IMysoTokenManager.sol | 100 | 100 | 100 | 100 | |
contracts\peer-to-peer\ | 99.74 | 94.61 | 98.84 | 98.66 | |
AddressRegistry.sol | 100 | 96.74 | 100 | 99.17 | 117 |
contracts\peer-to-peer\ | 99.75 | 94.5 | 98.82 | 98.2 | |
AddressRegistry.sol | 100 | 96.74 | 100 | 99.17 | 116 |
BorrowerGateway.sol | 98.57 | 90.91 | 90.91 | 96.97 | 241,317,358 |
DataTypesPeerToPeer.sol | 100 | 100 | 100 | 100 | |
LenderVaultFactory.sol | 100 | 87.5 | 100 | 100 | |
LenderVaultImpl.sol | 100 | 93.1 | 100 | 98.92 | 64,207 |
QuoteHandler.sol | 100 | 96.83 | 100 | 98.91 | 266,478 |
LenderVaultImpl.sol | 100 | 93.1 | 100 | 98.91 | 64,207 |
QuoteHandler.sol | 100 | 96.32 | 100 | 97.46 |... 332,333,516 |
contracts\peer-to-peer\callbacks\ | 100 | 75 | 88.89 | 96.88 | |
BalancerV2Looping.sol | 100 | 100 | 100 | 100 | |
UniV3Looping.sol | 100 | 100 | 100 | 100 | |
Expand Down Expand Up @@ -257,6 +257,6 @@ File | % Stmts | % Branch |
IFundingPoolImpl.sol | 100 | 100 | 100 | 100 | |
ILoanProposalImpl.sol | 100 | 100 | 100 | 100 | |
---------------------------------------------------------|----------|----------|----------|----------|----------------|
All files | 99.07 | 89.56 | 98.74 | 96.97 | |
All files | 99.08 | 89.7 | 98.74 | 96.84 | |
---------------------------------------------------------|----------|----------|----------|----------|----------------|
```
6 changes: 4 additions & 2 deletions contracts/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,10 @@ library Errors {
error AlreadyPublished();
error PolicyAlreadySet();
error NoPolicyToDelete();
error InvalidTenors();
error InvalidLoanPerCollOrLtv();
error InvalidTenorBounds();
error InvalidLtvBounds();
error InvalidLoanPerCollBounds();
error InvalidMinApr();
error NoPolicy();
error InvalidMinFee();
}
58 changes: 48 additions & 10 deletions contracts/peer-to-peer/QuoteHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ contract QuoteHandler is IQuoteHandler {
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;
internal _onChainQuoteHistory;

constructor(address _addressRegistry) {
if (_addressRegistry == address(0)) {
Expand All @@ -47,7 +48,7 @@ contract QuoteHandler is IQuoteHandler {
revert Errors.OnChainQuoteAlreadyAdded();
}
// @dev: on-chain quote history is append only
onChainQuoteHistory[lenderVault].push(
_onChainQuoteHistory[lenderVault].push(
DataTypesPeerToPeer.OnChainQuoteInfo({
quoteHash: onChainQuoteHash,
validUntil: onChainQuote.generalQuoteInfo.validUntil
Expand Down Expand Up @@ -77,7 +78,7 @@ contract QuoteHandler is IQuoteHandler {
revert Errors.UnknownOnChainQuote();
}
// @dev: on-chain quote history is append only
onChainQuoteHistory[lenderVault].push(
_onChainQuoteHistory[lenderVault].push(
DataTypesPeerToPeer.OnChainQuoteInfo({
quoteHash: newOnChainQuoteHash,
validUntil: newOnChainQuote.generalQuoteInfo.validUntil
Expand Down Expand Up @@ -115,12 +116,21 @@ contract QuoteHandler is IQuoteHandler {
_checkIsVaultAndSenderIsApproved(lenderVault, false);
mapping(bytes32 => bool)
storage isOnChainQuoteFromVault = isOnChainQuote[lenderVault];
uint256 validUntil = publishedOnChainQuoteValidUntil[onChainQuoteHash];
if (
!isPublishedOnChainQuote[onChainQuoteHash] ||
isOnChainQuoteFromVault[onChainQuoteHash]
isOnChainQuoteFromVault[onChainQuoteHash] ||
validUntil < block.timestamp
) {
revert Errors.InvalidQuote();
}
// @dev: on-chain quote history is append only
_onChainQuoteHistory[lenderVault].push(
DataTypesPeerToPeer.OnChainQuoteInfo({
quoteHash: onChainQuoteHash,
validUntil: validUntil
})
);
isOnChainQuoteFromVault[onChainQuoteHash] = true;
emit OnChainQuoteCopied(lenderVault, onChainQuoteHash);
}
Expand All @@ -136,6 +146,9 @@ contract QuoteHandler is IQuoteHandler {
revert Errors.AlreadyPublished();
}
isPublishedOnChainQuote[onChainQuoteHash] = true;
publishedOnChainQuoteValidUntil[onChainQuoteHash] = onChainQuote
.generalQuoteInfo
.validUntil;
emit OnChainQuotePublished(onChainQuote, onChainQuoteHash, msg.sender);
}

Expand Down Expand Up @@ -285,23 +298,48 @@ contract QuoteHandler is IQuoteHandler {
address lenderVault,
uint256 idx
) external view returns (DataTypesPeerToPeer.OnChainQuoteInfo memory) {
if (idx < onChainQuoteHistory[lenderVault].length) {
return onChainQuoteHistory[lenderVault][idx];
if (idx < _onChainQuoteHistory[lenderVault].length) {
return _onChainQuoteHistory[lenderVault][idx];
} else {
revert Errors.InvalidArrayIndex();
}
}

function getFullOnChainQuoteHistory(
address lenderVault
function getOnChainQuoteHistorySlice(
address lenderVault,
uint256 startIdx,
uint256 endIdx
) external view returns (DataTypesPeerToPeer.OnChainQuoteInfo[] memory) {
return onChainQuoteHistory[lenderVault];
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;
return _onChainQuoteHistory[lenderVault].length;
}

/**
Expand Down
17 changes: 15 additions & 2 deletions contracts/peer-to-peer/interfaces/IQuoteHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,15 @@ interface IQuoteHandler {
bytes32 hashToCheck
) external view returns (bool);

/**
* @notice function returns valid until timestamp of the published on-chain quote
* @param hashToCheck hash of the on chain quote
* @return valid until timestamp of the published on-chain quote
*/
function publishedOnChainQuoteValidUntil(
bytes32 hashToCheck
) external view returns (uint256);

/**
* @notice function returns the address of the policy manager for a vault
* @param lenderVault address of vault
Expand All @@ -241,10 +250,14 @@ interface IQuoteHandler {
/**
* @notice function returns array of structs containing the on-chain quote hash and validUntil timestamp
* @param lenderVault address of vault
* @param startIdx starting index from on chain quote history array
* @param endIdx ending index of on chain quote history array (non-inclusive)
* @return array of quote hash and validUntil data for on-chain quote history of a vault
*/
function getFullOnChainQuoteHistory(
address lenderVault
function getOnChainQuoteHistorySlice(
address lenderVault,
uint256 startIdx,
uint256 endIdx
) external view returns (DataTypesPeerToPeer.OnChainQuoteInfo[] memory);

/**
Expand Down
64 changes: 45 additions & 19 deletions contracts/peer-to-peer/policyManagers/BasicQuotePolicyManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ contract BasicQuotePolicyManager is IQuotePolicyManager {
currSinglePolicy.requiresOracle &&
singlePolicy.minNumOfSignersOverwrite ==
currSinglePolicy.minNumOfSignersOverwrite &&
singlePolicy.minLoanPerCollUnit ==
currSinglePolicy.minLoanPerCollUnit &&
singlePolicy.maxLoanPerCollUnit ==
currSinglePolicy.maxLoanPerCollUnit &&
_equalQuoteBounds(
singlePolicy.quoteBounds,
currSinglePolicy.quoteBounds
Expand All @@ -109,6 +113,13 @@ contract BasicQuotePolicyManager is IQuotePolicyManager {
revert Errors.PolicyAlreadySet();
}
_checkNewQuoteBounds(singlePolicy.quoteBounds);
if (
singlePolicy.minLoanPerCollUnit == 0 ||
singlePolicy.minLoanPerCollUnit >
singlePolicy.maxLoanPerCollUnit
) {
revert Errors.InvalidLoanPerCollBounds();
}
if (!_hasSingleQuotingPolicy[loanToken]) {
_hasSingleQuotingPolicy[loanToken] = true;
}
Expand Down Expand Up @@ -146,19 +157,24 @@ contract BasicQuotePolicyManager is IQuotePolicyManager {

// @dev: pair policy (if defined) takes precedence over global policy
bool hasOracle = generalQuoteInfo.oracleAddr != address(0);
bool checkLoanPerColl;
bool requiresOracle;
uint256[2] memory minMaxLoanPerCollUnit;
DataTypesBasicPolicies.QuoteBounds memory quoteBounds;
if (hasPairPolicy) {
DataTypesBasicPolicies.PairPolicy
memory singlePolicy = _pairQuotingPolicies[lenderVault][
generalQuoteInfo.collToken
][generalQuoteInfo.loanToken];
requiresOracle = singlePolicy.requiresOracle;
quoteBounds = singlePolicy.quoteBounds;
minMaxLoanPerCollUnit[0] = singlePolicy.minLoanPerCollUnit;
minMaxLoanPerCollUnit[1] = singlePolicy.maxLoanPerCollUnit;
requiresOracle = singlePolicy.requiresOracle;
minNumOfSignersOverwrite = singlePolicy.minNumOfSignersOverwrite;
checkLoanPerColl = !hasOracle;
} else {
requiresOracle = globalPolicy.requiresOracle;
quoteBounds = globalPolicy.quoteBounds;
requiresOracle = globalPolicy.requiresOracle;
}

if (requiresOracle && !hasOracle) {
Expand All @@ -168,9 +184,11 @@ contract BasicQuotePolicyManager is IQuotePolicyManager {
return (
_isAllowedWithBounds(
quoteBounds,
minMaxLoanPerCollUnit,
quoteTuple,
generalQuoteInfo.earliestRepayTenor,
requiresOracle
hasOracle,
checkLoanPerColl
),
minNumOfSignersOverwrite
);
Expand Down Expand Up @@ -229,10 +247,7 @@ contract BasicQuotePolicyManager is IQuotePolicyManager {
quoteBounds1.minFee == quoteBounds2.minFee &&
quoteBounds1.minApr == quoteBounds2.minApr &&
quoteBounds1.minLtv == quoteBounds2.minLtv &&
quoteBounds1.maxLtv == quoteBounds2.maxLtv &&
quoteBounds1.minLoanPerCollUnit ==
quoteBounds2.minLoanPerCollUnit &&
quoteBounds1.maxLoanPerCollUnit == quoteBounds2.maxLoanPerCollUnit
quoteBounds1.maxLtv == quoteBounds2.maxLtv
) {
isEqual = true;
}
Expand All @@ -243,24 +258,29 @@ contract BasicQuotePolicyManager is IQuotePolicyManager {
) internal pure {
// @dev: allow minTenor == 0 to enable swaps
if (quoteBounds.minTenor > quoteBounds.maxTenor) {
revert Errors.InvalidTenors();
revert Errors.InvalidTenorBounds();
}
if (
quoteBounds.minLtv > quoteBounds.maxLtv ||
quoteBounds.minLoanPerCollUnit > quoteBounds.maxLoanPerCollUnit
quoteBounds.minLtv == 0 || quoteBounds.minLtv > quoteBounds.maxLtv
) {
revert Errors.InvalidLoanPerCollOrLtv();
revert Errors.InvalidLtvBounds();
}
if (quoteBounds.minApr + int(Constants.BASE) <= 0) {
revert Errors.InvalidMinApr();
}
// @dev: if minFee = BASE, then only swaps will be allowed
if (quoteBounds.minFee > Constants.BASE) {
revert Errors.InvalidMinFee();
}
}

function _isAllowedWithBounds(
DataTypesBasicPolicies.QuoteBounds memory quoteBounds,
uint256[2] memory minMaxLoanPerCollUnit,
DataTypesPeerToPeer.QuoteTuple calldata quoteTuple,
uint256 earliestRepayTenor,
bool checkLtv
bool checkLtv,
bool checkLoanPerColl
) internal pure returns (bool) {
if (
quoteTuple.tenor < quoteBounds.minTenor ||
Expand All @@ -269,13 +289,19 @@ contract BasicQuotePolicyManager is IQuotePolicyManager {
return false;
}

// @dev: if requires oracle check against LTV bounds, else against loan-per-coll bounds
(uint256 lowerBnd, uint256 upperBnd) = checkLtv
? (quoteBounds.minLtv, quoteBounds.maxLtv)
: (quoteBounds.minLoanPerCollUnit, quoteBounds.maxLoanPerCollUnit);
if (
quoteTuple.loanPerCollUnitOrLtv < lowerBnd ||
quoteTuple.loanPerCollUnitOrLtv > upperBnd
if (checkLtv) {
// @dev: check either against LTV bounds
if (
quoteTuple.loanPerCollUnitOrLtv < quoteBounds.minLtv ||
quoteTuple.loanPerCollUnitOrLtv > quoteBounds.maxLtv
) {
return false;
}
} else if (
// @dev: only check against absolute loan-per-coll bounds on pair policy and if no oracle
checkLoanPerColl &&
(quoteTuple.loanPerCollUnitOrLtv < minMaxLoanPerCollUnit[0] ||
quoteTuple.loanPerCollUnitOrLtv > minMaxLoanPerCollUnit[1])
) {
return false;
}
Expand Down
16 changes: 8 additions & 8 deletions contracts/peer-to-peer/policyManagers/DataTypesBasicPolicies.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,26 @@ library DataTypesBasicPolicies {
uint128 minLtv;
// Allowed maximum LTV for the quote
uint128 maxLtv;
// Allowed minimum loan per collateral unit or LTV for the quote
uint128 minLoanPerCollUnit;
// Allowed maximum loan per collateral unit or LTV for the quote
uint128 maxLoanPerCollUnit;
}

struct GlobalPolicy {
// Applicable general bounds
QuoteBounds quoteBounds;
// Flag indicating if an oracle is required for the pair
bool requiresOracle;
// Applicable global bounds
QuoteBounds quoteBounds;
}

struct PairPolicy {
// Applicable general bounds
QuoteBounds quoteBounds;
// Allowed minimum loan per collateral unit or LTV for the quote
uint128 minLoanPerCollUnit;
// Allowed maximum loan per collateral unit or LTV for the quote
uint128 maxLoanPerCollUnit;
// Flag indicating if an oracle is required for the pair
bool requiresOracle;
// Minimum number of signers required for the pair (if zero ignored, otherwise overwrites vault min signers)
// @dev: can overwrite signer threshold to be lower or higher than vault min signers
uint8 minNumOfSignersOverwrite;
// Applicable global bounds
QuoteBounds quoteBounds;
}
}
Loading

0 comments on commit f3637bb

Please sign in to comment.