Skip to content

Commit

Permalink
Add saviour health checks (#12)
Browse files Browse the repository at this point in the history
* added saviour changes and tests

* added saviour is ready tests

* fixed view functions in unit test:

* making requested changes to saviour

* finished making requested changes
  • Loading branch information
MrDeadCe11 authored May 30, 2024
1 parent 07e5917 commit 6165cb8
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 13 deletions.
26 changes: 25 additions & 1 deletion src/contracts/ODSaviour.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity 0.8.20;
import {IERC20} from '@openzeppelin/token/ERC20/ERC20.sol';
import {ISAFEEngine} from '@opendollar/contracts/SAFEEngine.sol';
import {IOracleRelayer} from '@opendollar/interfaces/IOracleRelayer.sol';
import {ILiquidationEngine} from '@opendollar/interfaces/ILiquidationEngine.sol';
import {IDelayedOracle} from '@opendollar/interfaces/oracles/IDelayedOracle.sol';

Check warning on line 8 in src/contracts/ODSaviour.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

imported name IDelayedOracle is not used
import {ICollateralJoinFactory} from '@opendollar/interfaces/factories/ICollateralJoinFactory.sol';
import {ICollateralJoin} from '@opendollar/interfaces/utils/ICollateralJoin.sol';
import {IVault721} from '@opendollar/interfaces/proxies/IVault721.sol';
Expand Down Expand Up @@ -54,14 +56,36 @@ contract ODSaviour is Authorizable, Modifiable, ModifiablePerCollateral, IODSavi
safeEngine = ISAFEEngine(address(safeManager.safeEngine()));
}

function isEnabled(uint256 _vaultId) external view returns (bool _enabled) {
function isVaultEnabled(uint256 _vaultId) public view returns (bool _enabled) {
_enabled = _enabledVaults[_vaultId];
}

function cType(bytes32 _cType) public view returns (address _tokenAddress) {
return address(_saviourTokenAddresses[_cType]);
}

function saviourIsReady(bytes32 _cType) public view returns (bool) {

Check warning on line 67 in src/contracts/ODSaviour.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

Return value 'bool' in function 'saviourIsReady' must be named
return (IERC20(_saviourTokenAddresses[_cType]).allowance(saviourTreasury, address(this)) != 0)
&& (ILiquidationEngine(liquidationEngine).safeSaviours(address(this)) != 0);
}

function vaultData(uint256 vaultId) public view returns (VaultData memory vData) {

Check warning on line 72 in src/contracts/ODSaviour.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

'vaultId' should start with _

Check warning on line 72 in src/contracts/ODSaviour.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

'vData' should start with _
vData.id = vaultId;
IODSafeManager.SAFEData memory safeData = safeManager.safeData(vaultId);

Check warning on line 74 in src/contracts/ODSaviour.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

'safeData' should start with _
vData.isAllowed = safeManager.safeCan(safeData.owner, vaultId, safeData.nonce, address(this));

vData.isChosenSaviour = ILiquidationEngine(liquidationEngine).chosenSAFESaviour(
safeData.collateralType, safeData.safeHandler
) == address(this);
vData.isEnabled = isVaultEnabled(vaultId);
vData.vaultCtypeTokenAddress = cType(safeData.collateralType);
if (vData.vaultCtypeTokenAddress == address(0)) revert UninitializedCollateral(safeData.collateralType);

vData.saviourAllowance = IERC20(vData.vaultCtypeTokenAddress).allowance(saviourTreasury, address(this));

vData.treasuryBalance = IERC20(vData.vaultCtypeTokenAddress).balanceOf(saviourTreasury);
}

function saveSAFE(
address _liquidator,

Check warning on line 90 in src/contracts/ODSaviour.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

Variable "_liquidator" is unused
bytes32 _cType,
Expand Down
20 changes: 19 additions & 1 deletion src/interfaces/IODSaviour.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,23 @@ interface IODSaviour is ISAFESaviour {
address collateralJoinFactory;
}

function isEnabled(uint256 _vaultId) external view returns (bool _enabled);
struct VaultData {
uint256 id;
bool isAllowed;
bool isChosenSaviour;
bool isEnabled;
address vaultCtypeTokenAddress;
uint256 saviourAllowance;
uint256 treasuryBalance;
}

function isVaultEnabled(uint256 _vaultId) external view returns (bool _enabled);
function vaultData(uint256 vaultId) external view returns (VaultData memory vData);
function saviourIsReady(bytes32 _cType) external view returns (bool);
function cType(bytes32 _cType) external view returns (address _tokenAddress);
function saveSAFE(
address _liquidator,
bytes32 _cType,
address _safe
) external returns (bool _ok, uint256 _collateralAdded, uint256 _liquidatorReward);
}
159 changes: 148 additions & 11 deletions test/unit/ODSaviour.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity 0.8.20;

import {IOracleRelayer} from '@opendollar/interfaces/IOracleRelayer.sol';
import {IDelayedOracle} from '@opendollar/interfaces/oracles/IDelayedOracle.sol';
import {ILiquidationEngine} from '@opendollar/interfaces/ILiquidationEngine.sol';
import {IBaseOracle} from '@opendollar/interfaces/oracles/IBaseOracle.sol';
import {Assertions} from '@opendollar/libraries/Assertions.sol';
import {ODSaviour} from '../../src/contracts/ODSaviour.sol';
Expand Down Expand Up @@ -58,17 +59,150 @@ contract ODSaviourSetUp is SetUp {
liquidationEngine.connectSAFESaviour(address(saviour));

vm.stopPrank();
}
}

contract UnitODSaviourVaultData is ODSaviourSetUp {
function setUp() public override {
super.setUp();

collateralToken.mint(saviourTreasury, 100 ether);
}

modifier happyPath() {
vm.prank(aliceProxy);
safeManager.protectSAFE(vaultId, address(saviour));
vm.prank(saviourTreasury);
saviour.modifyParameters('setVaultStatus', abi.encode(vaultId, true));
vm.prank(saviourTreasury);
collateralToken.approve(address(saviour), type(uint256).max);
vm.prank(aliceProxy);
safeManager.allowSAFE(vaultId, address(saviour), true);
_;
}

function test_VaultData_TreasuryBalance() public happyPath {
IODSaviour.VaultData memory vData = saviour.vaultData(vaultId);
assertEq(vData.treasuryBalance, 100 ether);
}
/**
* uint256 vaultId;
* bool allowed;
* bool enabled;
* address vaultCtypeTokenAddress;
* uint256 saviourAllowance;
* bool isChosenSaviour;
* bool treasuryBalance;
*/

function test_VaultData_False_NotAllowed() public {
vm.prank(aliceProxy);
safeManager.protectSAFE(vaultId, address(saviour));
vm.prank(saviourTreasury);
saviour.modifyParameters('setVaultStatus', abi.encode(vaultId, true));
vm.prank(saviourTreasury);
collateralToken.approve(address(saviour), type(uint256).max);

IODSaviour.VaultData memory saftey = saviour.vaultData(vaultId);

assertEq(saftey.treasuryBalance, 100 ether);
assertFalse(saftey.isAllowed);
assertTrue(saftey.isEnabled);
assertTrue(saftey.saviourAllowance != 0);
assertTrue(saftey.isChosenSaviour);
assertEq(saftey.vaultCtypeTokenAddress, address(collateralToken));
}

function test_VaultData_False_NoAllowance() public {
vm.prank(aliceProxy);
safeManager.protectSAFE(vaultId, address(saviour));
vm.prank(saviourTreasury);
saviour.modifyParameters('setVaultStatus', abi.encode(vaultId, true));
vm.prank(aliceProxy);
safeManager.allowSAFE(vaultId, address(saviour), true);
IODSaviour.VaultData memory saftey = saviour.vaultData(vaultId);
assertEq(saftey.treasuryBalance, 100 ether);
assertTrue(saftey.isAllowed);
assertTrue(saftey.isEnabled);
assertTrue(saftey.saviourAllowance == 0);
assertTrue(saftey.isChosenSaviour);
assertEq(saftey.vaultCtypeTokenAddress, address(collateralToken));
}

function test_VaultData_False_NotEnabled() public {
vm.prank(aliceProxy);
safeManager.protectSAFE(vaultId, address(saviour));

vm.prank(saviourTreasury);
collateralToken.approve(address(saviour), type(uint256).max);
vm.prank(aliceProxy);
safeManager.allowSAFE(vaultId, address(saviour), true);
IODSaviour.VaultData memory saftey = saviour.vaultData(vaultId);
assertEq(saftey.treasuryBalance, 100 ether);
assertTrue(saftey.isAllowed);
assertFalse(saftey.isEnabled);
assertTrue(saftey.saviourAllowance != 0);
assertTrue(saftey.isChosenSaviour);
assertEq(saftey.vaultCtypeTokenAddress, address(collateralToken));
}

function test_VaultData_False_NotProtected() public {
vm.prank(saviourTreasury);
saviour.modifyParameters('setVaultStatus', abi.encode(vaultId, true));
vm.prank(saviourTreasury);
collateralToken.approve(address(saviour), type(uint256).max);
vm.prank(aliceProxy);
safeManager.allowSAFE(vaultId, address(saviour), true);
IODSaviour.VaultData memory saftey = saviour.vaultData(vaultId);
assertEq(saftey.treasuryBalance, 100 ether);
assertTrue(saftey.isAllowed);
assertTrue(saftey.isEnabled);
assertTrue(saftey.saviourAllowance != 0);
assertFalse(saftey.isChosenSaviour);
assertEq(saftey.vaultCtypeTokenAddress, address(collateralToken));
}

function test_VaultData_False_WrongCType() public {
vm.startPrank(aliceProxy);
uint256 newVaultId = safeManager.openSAFE('TKN', aliceProxy);
safeManager.protectSAFE(newVaultId, address(saviour));
safeManager.allowSAFE(newVaultId, address(saviour), true);
vm.expectRevert(
abi.encodeWithSelector(IODSaviour.UninitializedCollateral.selector, bytes32(abi.encodePacked('TKN')))
);
IODSaviour.VaultData memory saftey = saviour.vaultData(newVaultId);
}
}

contract UnitOdSaviourSaviourIsReady is ODSaviourSetUp {
function test_SaviourIsReady_True() public {
vm.prank(saviourTreasury);
collateralToken.approve(address(saviour), type(uint256).max);
assertTrue(saviour.saviourIsReady(ARB));
}

function test_SaviourIsReady_False_NoAllowance() public {
assertFalse(saviour.saviourIsReady(ARB));
}

function test_SaviourIsReady_False_NotAChosenSafe() public {
vm.prank(saviourTreasury);
collateralToken.approve(address(saviour), type(uint256).max);
vm.mockCall(
address(liquidationEngine),
abi.encodeWithSelector(ILiquidationEngine.safeSaviours.selector, address(saviour)),
abi.encode(0)
);
assertFalse(saviour.saviourIsReady(ARB));
}
}

contract UnitODSaviourDeployment is ODSaviourSetUp {
function test_Set_LiquidationEngine() public view {
function test_Set_LiquidationEngine() public {
assertEq(address(saviour.liquidationEngine()), address(liquidationEngine));
}

function test_Set_Vault721() public view {
function test_Set_Vault721() public {
assertEq(address(saviour.vault721()), address(vault721));
}

Expand All @@ -78,7 +212,7 @@ contract UnitODSaviourDeployment is ODSaviourSetUp {
saviour = new ODSaviour(saviourInit);
}

function test_Set_OracleRelayer() public view {
function test_Set_OracleRelayer() public {
assertEq(address(saviour.oracleRelayer()), address(oracleRelayer));
}

Expand All @@ -88,15 +222,15 @@ contract UnitODSaviourDeployment is ODSaviourSetUp {
saviour = new ODSaviour(saviourInit);
}

function test_Set_SafeManager() public view {
function test_Set_SafeManager() public {
assertEq(address(saviour.safeManager()), address(safeManager));
}

function test_Set_SafeEngine() public view {
function test_Set_SafeEngine() public {
assertEq(address(saviour.safeEngine()), address(safeEngine));
}

function test_Set_CollateralJoinFactory() public view {
function test_Set_CollateralJoinFactory() public {
assertEq(address(saviour.collateralJoinFactory()), address(collateralJoinFactory));
}

Expand All @@ -106,11 +240,11 @@ contract UnitODSaviourDeployment is ODSaviourSetUp {
saviour = new ODSaviour(saviourInit);
}

function test_Set_LiquidatorReward() public view {
function test_Set_LiquidatorReward() public {
assertEq(saviour.liquidatorReward(), 0);
}

function test_Set_SaviourTokens() public view {
function test_Set_SaviourTokens() public {
assertEq(saviour.cType(ARB), address(collateralToken));
}
}
Expand All @@ -121,17 +255,17 @@ contract UnitODSaviourModifyParameters is ODSaviourSetUp {
uint256 safeId = safeManager.openSAFE(ARB, aliceProxy);
vm.startPrank(saviourTreasury);
saviour.modifyParameters('setVaultStatus', abi.encode(safeId, true));
assertTrue(saviour.isEnabled(safeId));
assertTrue(saviour.isVaultEnabled(safeId));
saviour.modifyParameters('setVaultStatus', abi.encode(safeId, false));
assertFalse(saviour.isEnabled(safeId));
assertFalse(saviour.isVaultEnabled(safeId));
}

function test_ModifyParameters_SetVaultStatus_Revert() public {
uint256 safeId = 3;
vm.prank(saviourTreasury);
vm.expectRevert(abi.encodeWithSelector(IODSaviour.UninitializedCollateral.selector, bytes32(0)));
saviour.modifyParameters('setVaultStatus', abi.encode(safeId, true));
assertFalse(saviour.isEnabled(safeId));
assertFalse(saviour.isVaultEnabled(safeId));
}

function test_ModifyParameters_liquidatorReward() public {
Expand Down Expand Up @@ -217,6 +351,9 @@ contract UnitODSaviourSaveSafe is ODSaviourSetUp {
liquidationPenalty: 20_000,
liquidationQuantity: 1 ether
});

vm.prank(aliceProxy);
safeManager.allowSAFE(vaultId, address(saviour), true);
}

event Liquidate(
Expand Down

0 comments on commit 6165cb8

Please sign in to comment.