Skip to content

Commit

Permalink
test bid/ask priceing
Browse files Browse the repository at this point in the history
  • Loading branch information
dglowinski committed Apr 2, 2024
1 parent c0c0bb7 commit aec989b
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 24 deletions.
46 changes: 38 additions & 8 deletions test/mocks/MockPriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,64 @@ contract MockPriceOracle {
error PO_Overflow();
error PO_NoPath();

mapping(address base => mapping(address quote => uint256)) prices;
mapping(address base => mapping(address quote => uint256)) price;
mapping(address base => mapping(address quote => Prices)) prices;

struct Prices {
bool set;
uint256 bid;
uint256 ask;
}

function name() external pure returns (string memory) {
return "MockPriceOracle";
}

function getQuote(uint256 amount, address base, address quote) public view returns (uint256 out) {
uint256 price = prices[base][quote];
(bool success,) = base.staticcall(abi.encodeCall(IERC4626.asset, ()));
if (base.code.length > 0 && success) amount = IEVault(base).convertToAssets(amount);

return amount * price / 1e18;
return calculateQuote(base, amount, price[resolveUnderlying(base)][quote]);
}

function getQuotes(uint256 amount, address base, address quote)
external
view
returns (uint256 bidOut, uint256 askOut)
{
if (prices[resolveUnderlying(base)][quote].set) {
return (
calculateQuote(base, amount, prices[resolveUnderlying(base)][quote].bid),
calculateQuote(base, amount, prices[resolveUnderlying(base)][quote].ask)
);
}

bidOut = askOut = getQuote(amount, base, quote);
}

///// Mock functions

function setPrice(address base, address quote, uint256 price) external {
prices[base][quote] = price;
function setPrice(address base, address quote, uint256 newPrice) external {
price[resolveUnderlying(base)][quote] = newPrice;
}

function setPrices(address base, address quote, uint256 newBid, uint256 newAsk) external {
prices[resolveUnderlying(base)][quote] = Prices({set: true, bid: newBid, ask: newAsk});
}

function calculateQuote(address base, uint256 amount, uint256 p) internal view returns (uint256) {
(bool success,) = base.staticcall(abi.encodeCall(IERC4626.asset, ()));
if (base.code.length > 0 && success) amount = IEVault(base).convertToAssets(amount);

return amount * p / 1e18;
}

function resolveUnderlying(address asset) internal view returns (address) {
if (asset.code.length > 0) {
(bool success, bytes memory data) = asset.staticcall(abi.encodeCall(IERC4626.asset, ()));
if (success) return abi.decode(data, (address));
}

return asset;
}

function testExcludeFromCoverage() public pure {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion test/unit/evault/DToken.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,6 @@ contract DTokenTest is EVaultTestBase {
vm.stopPrank();

oracle.setPrice(address(assetTST), unitOfAccount, 1 ether);
oracle.setPrice(address(eTST2), unitOfAccount, 1000 ether);
oracle.setPrice(address(assetTST2), unitOfAccount, 1000 ether);
}
}
2 changes: 1 addition & 1 deletion test/unit/evault/modules/BalanceForwarder/hooks.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ contract BalanceForwarderTest_Hooks is EVaultTestBase {
evc.enableCollateral(user, address(eTST2));

oracle.setPrice(address(assetTST), unitOfAccount, 1 ether);
oracle.setPrice(address(eTST2), unitOfAccount, 1 ether);
oracle.setPrice(address(assetTST2), unitOfAccount, 1 ether);
vm.stopPrank();
}
}
2 changes: 1 addition & 1 deletion test/unit/evault/modules/Governance/convertFees.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract Governance_ConvertFees is EVaultTestBase {
// Setup

oracle.setPrice(address(assetTST), unitOfAccount, 1e18);
oracle.setPrice(address(eTST2), unitOfAccount, 1e18);
oracle.setPrice(address(assetTST2), unitOfAccount, 1e18);

eTST.setLTV(address(eTST2), 0.9e4, 0);

Expand Down
2 changes: 1 addition & 1 deletion test/unit/evault/modules/Governance/interestRates.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract Governance_InterestRates is EVaultTestBase {
// Setup

oracle.setPrice(address(assetTST), unitOfAccount, 1e18);
oracle.setPrice(address(eTST2), unitOfAccount, 1e18);
oracle.setPrice(address(assetTST2), unitOfAccount, 1e18);

eTST.setLTV(address(eTST2), 0.9e4, 0);

Expand Down
4 changes: 2 additions & 2 deletions test/unit/evault/modules/Governance/pause.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ contract Governance_PauseOps is EVaultTestBase {
// ----------------- Setup vaults --------------------
eTST.setLTV(address(eTST2), 0.9e4, 0);
oracle.setPrice(address(assetTST), unitOfAccount, 1e18);
oracle.setPrice(address(eTST2), unitOfAccount, 1e18);
oracle.setPrice(address(assetTST2), unitOfAccount, 1e18);
// ----------------- Setup depositor -----------------
vm.startPrank(depositor);
assetTST.mint(depositor, type(uint256).max);
Expand Down Expand Up @@ -345,7 +345,7 @@ contract Governance_PauseOps is EVaultTestBase {
eTST.borrow(8 * MINT_AMOUNT / 10, borrower);
vm.stopPrank();

oracle.setPrice(address(eTST2), unitOfAccount, 0.5e17);
oracle.setPrice(address(assetTST2), unitOfAccount, 0.5e17);

vm.startPrank(liquidator);
evc.enableCollateral(liquidator, address(eTST2));
Expand Down
2 changes: 1 addition & 1 deletion test/unit/evault/modules/Vault/borrow.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ contract VaultTest_Borrow is EVaultTestBase {
// Setup

oracle.setPrice(address(assetTST), unitOfAccount, 1e18);
oracle.setPrice(address(eTST2), unitOfAccount, 1e18);
oracle.setPrice(address(assetTST2), unitOfAccount, 1e18);

eTST.setLTV(address(eTST2), 0.9e4, 0);

Expand Down
2 changes: 1 addition & 1 deletion test/unit/evault/modules/Vault/caps.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ contract VaultTest_Caps is EVaultTestBase {
evc.enableCollateral(user, address(eTST2));

oracle.setPrice(address(assetTST), unitOfAccount, 1 ether);
oracle.setPrice(address(eTST2), unitOfAccount, 1000 ether);
oracle.setPrice(address(assetTST2), unitOfAccount, 1000 ether);
vm.stopPrank();
}
}
101 changes: 95 additions & 6 deletions test/unit/evault/modules/Vault/liquidation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@ contract VaultLiquidation_Test is EVaultTestBase {

oracle.setPrice(address(assetTST), unitOfAccount, 2.2e18);
oracle.setPrice(address(assetTST2), unitOfAccount, 0.4e18);
oracle.setPrice(address(eTST), unitOfAccount, 0.4e18);
oracle.setPrice(address(eTST2), unitOfAccount, 0.4e18);
oracle.setPrice(address(eTST3), unitOfAccount, 2.2e18);
oracle.setPrice(address(eWETH), unitOfAccount, 1e18);
oracle.setPrice(address(assetTST3), unitOfAccount, 2.2e18);
oracle.setPrice(address(assetWETH), unitOfAccount, 1e18);

startHoax(lender);

Expand Down Expand Up @@ -677,7 +675,7 @@ contract VaultLiquidation_Test is EVaultTestBase {
(uint256 collateralValue, uint256 liabilityValue) = eTST.accountLiquidity(borrower, false);
assertApproxEqAbs(collateralValue * 1e18 / liabilityValue, 1.09e18, 0.01e18);

oracle.setPrice(address(eTST2), unitOfAccount, 0);
oracle.setPrice(address(assetTST2), unitOfAccount, 0);

(collateralValue, liabilityValue) = eTST.accountLiquidity(borrower, false);
assertEq(collateralValue, 0);
Expand Down Expand Up @@ -904,7 +902,7 @@ contract VaultLiquidation_Test is EVaultTestBase {
eTST2.deposit(1, borrower);

oracle.setPrice(address(assetTST), unitOfAccount, 0.001e18);
oracle.setPrice(address(eTST2), unitOfAccount, 15e18);
oracle.setPrice(address(assetTST2), unitOfAccount, 15e18);

eTST.borrow(3000, borrower);
(uint256 collateralValue, uint256 liabilityValue) = eTST.accountLiquidity(borrower, false);
Expand Down Expand Up @@ -1181,6 +1179,97 @@ contract VaultLiquidation_Test is EVaultTestBase {
vm.revertTo(snapshot);
}

function test_liquidationWithBidAskPricing() public {
// set up liquidator to support the debt
startHoax(lender);
evc.enableController(lender, address(eTST));
evc.enableCollateral(lender, address(eTST3));
evc.enableCollateral(lender, address(eTST2));

startHoax(borrower);
evc.enableController(borrower, address(eTST));
eTST.borrow(5e18, borrower);

startHoax(address(this));
eTST.setLTV(address(eTST3), 0.95e4, 0);

(uint256 collateralValue, uint256 liabilityValue) = eTST.accountLiquidity(borrower, false);
assertApproxEqAbs(collateralValue * 1e18 / liabilityValue, 1.09e18, 0.01e18);

// regular liquidation, bid = ask = mid price

oracle.setPrice(address(assetTST), unitOfAccount, 2.5e18);

(collateralValue, liabilityValue) = eTST.accountLiquidity(borrower, false);
uint256 healthScore = collateralValue * 1e18 / liabilityValue;
assertApproxEqAbs(healthScore, 0.96e18, 0.001e18);
// liability price bid price changes

oracle.setPrices(address(assetTST), unitOfAccount, 2.4e18, 2.5e18);

// no influence
(uint256 newCollateralValue, uint256 newLiabilityValue) = eTST.accountLiquidity(borrower, false);
assertEq(collateralValue, newCollateralValue);
assertEq(liabilityValue, newLiabilityValue);

// liability ask price changes

oracle.setPrices(address(assetTST), unitOfAccount, 2.5e18, 2.6e18);

// liability valued at ask
(newCollateralValue, newLiabilityValue) = eTST.accountLiquidity(borrower, false);
assertEq(collateralValue, newCollateralValue);
assertGt(newLiabilityValue, liabilityValue);

liabilityValue = newLiabilityValue;

// collateral ask price changes

oracle.setPrices(address(assetTST2), unitOfAccount, 0.4e18, 0.5e18);

(newCollateralValue, newLiabilityValue) = eTST.accountLiquidity(borrower, false);
assertEq(collateralValue, newCollateralValue);
assertEq(liabilityValue, newLiabilityValue);

// collateral bid price changes

oracle.setPrices(address(assetTST2), unitOfAccount, 0.3e18, 0.4e18);

(newCollateralValue, newLiabilityValue) = eTST.accountLiquidity(borrower, false);
assertGt(collateralValue, newCollateralValue);
assertEq(liabilityValue, newLiabilityValue);

collateralValue = newCollateralValue;

healthScore = collateralValue * 1e18 / liabilityValue;
assertApproxEqAbs(healthScore, 0.692e18, 0.001e18);

// The discount is max - 20% on mid point prices

(uint256 maxRepay, uint256 maxYield) = eTST.checkLiquidation(lender, borrower, address(eTST2));

uint256 repayValue = oracle.getQuote(maxRepay, address(eTST), unitOfAccount);
uint256 yieldValue = oracle.getQuote(maxYield, address(eTST2), unitOfAccount);

assertEq(yieldValue, repayValue * 1e18 / 0.8e18);

uint256 violatorsOriginalCollateral = eTST2.balanceOf(borrower);

startHoax(lender);

// max uint is equivalent to maxRepay
eTST.liquidate(borrower, address(eTST2), type(uint256).max, 0);

// liquidator:
assertEq(eTST.debtOf(lender), maxRepay);
assertEq(eTST2.balanceOf(lender), maxYield);

// violator:
startHoax(borrower);
assertEq(eTST.debtOf(borrower), 0);
assertEq(eTST2.balanceOf(borrower), violatorsOriginalCollateral - maxYield);
}

function getRiskAdjustedValue(uint256 amount, uint256 price, uint256 factor) public pure returns (uint256) {
return amount * price / 1e18 * factor / 1e18;
}
Expand Down
2 changes: 1 addition & 1 deletion test/unit/evault/modules/Vault/ltv.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract VaultTest_LTV is EVaultTestBase {
// Setup

oracle.setPrice(address(assetTST), unitOfAccount, 1e18);
oracle.setPrice(address(eTST2), unitOfAccount, 1e18);
oracle.setPrice(address(assetTST2), unitOfAccount, 1e18);

depositor = makeAddr("depositor");
borrower = makeAddr("borrower");
Expand Down
2 changes: 1 addition & 1 deletion test/unit/evault/modules/Vault/withdraw.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ contract VaultTest_withdraw is EVaultTestBase {
// Setup

oracle.setPrice(address(assetTST), unitOfAccount, 1e18);
oracle.setPrice(address(eTST2), unitOfAccount, 1e18);
oracle.setPrice(address(assetTST2), unitOfAccount, 1e18);

eTST.setLTV(address(eTST2), 0.9e4, 0);

Expand Down

0 comments on commit aec989b

Please sign in to comment.