diff --git a/contracts/interfaces/IKeydonixOracleEth.sol b/contracts/interfaces/IKeydonixOracleEth.sol new file mode 100644 index 0000000..790dbfb --- /dev/null +++ b/contracts/interfaces/IKeydonixOracleEth.sol @@ -0,0 +1,8 @@ +pragma abicoder v2; +import "../interfaces/IKeydonixOracleUsd.sol"; + +interface IKeydonixOracleEth { + + // returns Q112-encoded value + function assetToEth(address asset, uint amount, IKeydonixOracleUsd.ProofDataStruct calldata proofData) external view returns (uint); +} \ No newline at end of file diff --git a/contracts/test-helpers/KeydonixOracleMainAsset_Mock.sol b/contracts/test-helpers/KeydonixOracleMainAsset_Mock.sol index 3d4a342..e05940a 100644 --- a/contracts/test-helpers/KeydonixOracleMainAsset_Mock.sol +++ b/contracts/test-helpers/KeydonixOracleMainAsset_Mock.sol @@ -11,59 +11,60 @@ import "../helpers/SafeMath.sol"; import "../interfaces/IAggregator.sol"; import "../helpers/IUniswapV2Factory.sol"; import "../interfaces/IKeydonixOracleUsd.sol"; +import "../interfaces/IOracleRegistry.sol"; +import "../interfaces/IOracleEth.sol"; +import "../interfaces/IKeydonixOracleEth.sol"; /** * @title KeydonixOracleMainAsset_Mock * @dev Calculates the USD price of desired tokens **/ -contract KeydonixOracleMainAsset_Mock is IKeydonixOracleUsd { +contract KeydonixOracleMainAsset_Mock is IKeydonixOracleEth, IKeydonixOracleUsd { using SafeMath for uint; - uint public constant ETH_USD_DENOMINATOR = 100000000; + uint public constant ETH_USD_DENOMINATOR = 1e8; - IAggregator public immutable ethUsdChainlinkAggregator; + uint public constant Q112 = 2 ** 112; IUniswapV2Factory public immutable uniswapFactory; + IOracleRegistry public oracleRegistry; + address public immutable WETH; constructor( IUniswapV2Factory uniFactory, - address weth, - IAggregator chainlinkAggregator + IOracleRegistry _oracleRegistry ) public { require(address(uniFactory) != address(0), "Unit Protocol: ZERO_ADDRESS"); - require(weth != address(0), "Unit Protocol: ZERO_ADDRESS"); - require(address(chainlinkAggregator) != address(0), "Unit Protocol: ZERO_ADDRESS"); + require(address(_oracleRegistry) != address(0), "Unit Protocol: ZERO_ADDRESS"); uniswapFactory = uniFactory; - WETH = weth; - ethUsdChainlinkAggregator = chainlinkAggregator; + WETH = _oracleRegistry.WETH(); + oracleRegistry = _oracleRegistry; } - // override with mock; only for tests - function assetToUsd(address asset, uint amount, ProofDataStruct memory proofData) public override view returns (uint) { - - if (asset == WETH) { - return ethToUsd(amount); - } - - address uniswapPair = uniswapFactory.getPair(asset, WETH); - require(uniswapPair != address(0), "Unit Protocol: UNISWAP_PAIR_DOES_NOT_EXIST"); - - // token reserve of {Token}/WETH pool - uint tokenReserve = ERC20Like(asset).balanceOf(uniswapPair); - - // revert if there is no liquidity - require(tokenReserve != 0, "Unit Protocol: UNISWAP_EMPTY_POOL"); - - return ethToUsd(assetToEth(asset, amount, proofData)).div(tokenReserve); + /** + * @notice USD token's rate is UniswapV2 Token/WETH pool's average time weighted price between proofs' blockNumber and current block number + * @notice Merkle proof must be in range [MIN_BLOCKS_BACK ... MAX_BLOCKS_BACK] blocks ago + * @notice {Token}/WETH pair must exists on Uniswap + * @param asset The token address + * @param amount Amount of tokens + * @param proofData Merkle proof data + * @return Q112-encoded price of tokens in USD + **/ + function assetToUsd(address asset, uint amount, IKeydonixOracleUsd.ProofDataStruct memory proofData) public override view returns (uint) { + uint priceInEth = assetToEth(asset, amount, proofData); + return IOracleEth(oracleRegistry.oracleByAsset(WETH)).ethToUsd(priceInEth); } // override with mock; only for tests - function assetToEth(address asset, uint amount, ProofDataStruct memory proofData) public override view returns (uint) { + function assetToEth(address asset, uint amount, IKeydonixOracleUsd.ProofDataStruct memory proofData) public override view returns (uint) { + if (amount == 0) { return 0; } + + if (asset == WETH) { return amount.mul(Q112); } address uniswapPair = uniswapFactory.getPair(asset, WETH); require(uniswapPair != address(0), "Unit Protocol: UNISWAP_PAIR_DOES_NOT_EXIST"); @@ -73,16 +74,9 @@ contract KeydonixOracleMainAsset_Mock is IKeydonixOracleUsd { // WETH reserve of {Token}/WETH pool uint wethReserve = ERC20Like(WETH).balanceOf(uniswapPair); - return amount.mul(wethReserve).mul(Q112); - } + // Asset reserve of {Token}/WETH pool + uint assetReserve = ERC20Like(asset).balanceOf(uniswapPair); - /** - * @notice ETH/USD price feed from Chainlink, see for more info: https://feeds.chain.link/eth-usd - * returns Price of given amount of Ether in USD (0 decimals) - **/ - function ethToUsd(uint ethAmount) public override view returns (uint) { - require(ethUsdChainlinkAggregator.latestTimestamp() > block.timestamp - 6 hours, "Unit Protocol: OUTDATED_CHAINLINK_PRICE"); - uint ethUsdPrice = uint(ethUsdChainlinkAggregator.latestAnswer()); - return ethAmount.mul(ethUsdPrice).div(ETH_USD_DENOMINATOR); + return amount.mul(wethReserve).mul(Q112).div(assetReserve); } } diff --git a/contracts/test-helpers/KeydonixOraclePoolToken_Mock.sol b/contracts/test-helpers/KeydonixOraclePoolToken_Mock.sol index 556bf55..4a8438d 100644 --- a/contracts/test-helpers/KeydonixOraclePoolToken_Mock.sol +++ b/contracts/test-helpers/KeydonixOraclePoolToken_Mock.sol @@ -9,6 +9,9 @@ pragma experimental ABIEncoderV2; import "../helpers/IUniswapV2PairFull.sol"; import "../helpers/SafeMath.sol"; import "../interfaces/IKeydonixOracleUsd.sol"; +import "../interfaces/IVaultParameters.sol"; +import "../interfaces/IOracleRegistry.sol"; +import "../interfaces/IOracleEth.sol"; /** * @title KeydonixOraclePoolToken_Mock @@ -17,22 +20,33 @@ import "../interfaces/IKeydonixOracleUsd.sol"; contract KeydonixOraclePoolToken_Mock is IKeydonixOracleUsd { using SafeMath for uint; - constructor(address _keydonixOracleMainAsset_Mock) public { - uniswapOracleMainAsset = IKeydonixOracleUsd(_keydonixOracleMainAsset_Mock); + uint public constant Q112 = 2 ** 112; + + IOracleRegistry public immutable oracleRegistry; + + IVaultParameters public immutable vaultParameters; + + constructor(address _oracleRegistry, address _vaultParameters) public { + require(_oracleRegistry != address(0), "Unit Protocol: ZERO_ADDRESS"); + require(_vaultParameters != address(0), "Unit Protocol: ZERO_ADDRESS"); + oracleRegistry = IOracleRegistry(_oracleRegistry); + vaultParameters = IVaultParameters(_vaultParameters); } // override with mock; only for tests - function assetToUsd(address asset, uint amount, ProofDataStruct memory proofData) public override view returns (uint) { + function assetToUsd(address asset, uint amount, ProofDataStruct calldata proofData) public override view returns (uint) { IUniswapV2PairFull pair = IUniswapV2PairFull(asset); + proofData; + uint ePool; // current WETH pool (uint112 _reserve0, uint112 _reserve1,) = pair.getReserves(); - if (pair.token0() == uniswapOracleMainAsset.WETH()) { + if (pair.token0() == oracleRegistry.WETH()) { ePool = _reserve0; - } else if (pair.token1() == uniswapOracleMainAsset.WETH()) { + } else if (pair.token1() == oracleRegistry.WETH()) { ePool = _reserve1; } else { revert("Unit Protocol: NOT_REGISTERED_PAIR"); @@ -41,6 +55,23 @@ contract KeydonixOraclePoolToken_Mock is IKeydonixOracleUsd { uint lpSupply = pair.totalSupply(); uint totalValueInEth_q112 = amount.mul(ePool).mul(2).mul(Q112); - return uniswapOracleMainAsset.ethToUsd(totalValueInEth_q112).div(lpSupply); + return IOracleEth(oracleRegistry.oracleByAsset(oracleRegistry.WETH())).ethToUsd(totalValueInEth_q112).div(lpSupply); + } + + function _selectOracle(address asset) internal view returns (address oracle) { + uint oracleType = _getOracleType(asset); + require(oracleType != 0, "Unit Protocol: INVALID_ORACLE_TYPE"); + oracle = oracleRegistry.oracleByType(oracleType); + require(oracle != address(0), "Unit Protocol: DISABLED_ORACLE"); + } + + function _getOracleType(address asset) internal view returns (uint) { + uint[] memory keydonixOracleTypes = oracleRegistry.getKeydonixOracleTypes(); + for (uint i = 0; i < keydonixOracleTypes.length; i++) { + if (vaultParameters.isOracleTypeEnabled(keydonixOracleTypes[i], asset)) { + return keydonixOracleTypes[i]; + } + } + revert("Unit Protocol: NO_ORACLE_FOUND"); } } diff --git a/contracts/vault-managers/CDPManager01_Fallback.sol b/contracts/vault-managers/CDPManager01_Fallback.sol index 4ca9f94..051dbf9 100644 --- a/contracts/vault-managers/CDPManager01_Fallback.sol +++ b/contracts/vault-managers/CDPManager01_Fallback.sol @@ -89,7 +89,6 @@ contract CDPManager01_Fallback is ReentrancyGuard { } else { - // check oracle uint oracleType = _selectOracleType(asset); bool spawned = vault.debts(asset, msg.sender) != 0; @@ -187,7 +186,7 @@ contract CDPManager01_Fallback is ReentrancyGuard { } } - function _ensurePositionCollateralization(address asset, address owner, IKeydonixOracleUsd.ProofDataStruct memory proofData) internal view { + function _ensurePositionCollateralization(address asset, address owner, IKeydonixOracleUsd.ProofDataStruct calldata proofData) internal view { // collateral value of the position in USD uint usdValue_q112 = getCollateralUsdValue_q112(asset, owner, proofData); @@ -207,8 +206,6 @@ contract CDPManager01_Fallback is ReentrancyGuard { **/ function triggerLiquidation(address asset, address owner, IKeydonixOracleUsd.ProofDataStruct calldata proofData) external nonReentrant { - uint oracleType = _selectOracleType(asset); - // USD value of the collateral uint usdValue_q112 = getCollateralUsdValue_q112(asset, owner, proofData); @@ -289,9 +286,7 @@ contract CDPManager01_Fallback is ReentrancyGuard { IKeydonixOracleUsd.ProofDataStruct calldata proofData ) public view returns (uint) { uint debt = vault.getTotalDebt(asset, owner); - if (debt == 0) return uint(-1); - - uint oracleType = _getOracleType(asset); + if (debt == 0) return uint(0); uint usdValue_q112 = getCollateralUsdValue_q112(asset, owner, proofData); diff --git a/package.json b/package.json index c355999..4a35f87 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "test": "truffle test", "test:curve": "truffle test test/CDPManager_WrappedAssets.test.js test/LiquidationTrigger_WrappedAssets.test.js test/LiquidationAuction.test.js", "test:single-point": "truffle test test/*Keep3r*.test.js test/*Wrapped*.test.js test/Li*Bearing*.test.js test/CDP*Bearing*.test.js test/LiquidationAuction.test.js test/LiquidationTrigger_Chainlink.test.js test/LiquidationTrigger_PoolToken_Chainlink.test.js", + "test:keydonix": "truffle test test/*Keydonix*.test.js", "build": "rm -rf build && truffle compile", "coverage": "truffle run coverage" }, diff --git a/test/CDPManager_Keydonix.test.js b/test/CDPManager_Keydonix.test.js index 199867b..5b7e2da 100755 --- a/test/CDPManager_Keydonix.test.js +++ b/test/CDPManager_Keydonix.test.js @@ -2,7 +2,6 @@ const { expectEvent, ether, } = require('openzeppelin-test-helpers'); -const balance = require('./helpers/balances'); const BN = web3.utils.BN; const { expect } = require('chai'); const utils = require('./helpers/utils'); @@ -45,31 +44,6 @@ const time = require('./helpers/time'); expect(mainAmountInPosition).to.be.bignumber.equal(mainAmount); expect(usdpBalance).to.be.bignumber.equal(usdpAmount); }) - - it('Should spawn position using ETH', async function() { - const mainAmount = ether('2'); - const usdpAmount = ether('1'); - - const wethInVaultBefore = await this.weth.balanceOf(this.vault.address); - - const { logs } = await this.utils.spawnEth(mainAmount, usdpAmount); - - expectEvent.inLogs(logs, 'Join', { - asset: this.weth.address, - owner: deployer, - main: mainAmount, - usdp: usdpAmount, - }); - - const wethInVaultAfter = await this.weth.balanceOf(this.vault.address); - expect(wethInVaultAfter.sub(wethInVaultBefore)).to.be.bignumber.equal(mainAmount); - - const mainAmountInPosition = await this.vault.collaterals(this.weth.address, deployer); - const usdpBalance = await this.usdp.balanceOf(deployer); - - expect(mainAmountInPosition).to.be.bignumber.equal(mainAmount); - expect(usdpBalance).to.be.bignumber.equal(usdpAmount); - }) }) describe('Repay & withdraw', function() { @@ -114,7 +88,6 @@ const time = require('./helpers/time'); // get some usdp to cover fee await this.utils.updatePrice(); - await this.utils.spawnEth(ether('2'), ether('1'), ether('2')); // repay debt partially await this.utils.repay(this.mainCollateral, deployer, usdpAmount.div(new BN(2))); @@ -124,7 +97,6 @@ const time = require('./helpers/time'); expectedDebt.div(new BN(2)).div(new BN(10 ** 12)) ); - await this.utils.repayAllAndWithdraw(this.mainCollateral, deployer); }) it('Should partially repay the debt of a position and withdraw collaterals partially', async function() { @@ -151,60 +123,6 @@ const time = require('./helpers/time'); expect(mainAmountInPosition).to.be.bignumber.equal(mainAmount.sub(mainToWithdraw)); expect(usdpInPosition).to.be.bignumber.equal(usdpAmount.sub(usdpToWithdraw)); }) - - it('Should partially repay the debt of a position and withdraw collaterals partially using ETH', async function() { - const mainAmount = ether('2'); - const usdpAmount = ether('1'); - - await this.utils.spawnEth(mainAmount, usdpAmount); - - const mainToWithdraw = ether('1'); - const usdpToWithdraw = ether('0.5'); - - const wethBalanceBefore = await balance.current(this.weth.address); - - const { logs } = await this.utils.withdrawAndRepayEth(mainToWithdraw, usdpToWithdraw); - - expectEvent.inLogs(logs, 'Exit', { - asset: this.weth.address, - owner: deployer, - main: mainToWithdraw, - usdp: usdpToWithdraw, - }); - - const mainAmountInPosition = await this.vault.collaterals(this.weth.address, deployer); - const usdpInPosition = await this.vault.debts(this.weth.address, deployer); - const wethBalanceAfter = await balance.current(this.weth.address); - - expect(mainAmountInPosition).to.be.bignumber.equal(mainAmount.sub(mainToWithdraw)); - expect(usdpInPosition).to.be.bignumber.equal(usdpAmount.sub(usdpToWithdraw)); - expect(wethBalanceBefore.sub(wethBalanceAfter)).to.be.bignumber.equal(mainToWithdraw); - }) - - it('Should repay the debt of a position and withdraw collaterals using ETH', async function() { - const mainAmount = ether('2'); - const usdpAmount = ether('1'); - - await this.utils.spawnEth(mainAmount, usdpAmount); - - const wethInVaultBefore = await this.weth.balanceOf(this.vault.address); - - const { logs } = await this.utils.repayAllAndWithdrawEth(deployer); - - expectEvent.inLogs(logs, 'Exit', { - asset: this.weth.address, - owner: deployer, - main: mainAmount, - usdp: usdpAmount, - }); - - const wethInVaultAfter = await this.weth.balanceOf(this.vault.address); - - const mainAmountInPosition = await this.vault.collaterals(this.weth.address, deployer); - - expect(mainAmountInPosition).to.be.bignumber.equal(new BN(0)); - expect(wethInVaultBefore.sub(wethInVaultAfter)).to.be.bignumber.equal(mainAmount); - }) }) it('Should deposit collaterals to position and mint USDP', async function () { @@ -296,20 +214,6 @@ const time = require('./helpers/time'); }) }) - describe('Join', function () { - it('Reverts non-spawned position', async function() { - const mainAmount = ether('100'); - const usdpAmount = ether('20'); - - const tx = this.utils.join( - this.mainCollateral, - mainAmount, - usdpAmount, - ); - await this.utils.expectRevert(tx, "Unit Protocol: NOT_SPAWNED_POSITION"); - }) - }) - describe('Exit', function () { it('Reverts non valuable tx', async function() { const mainAmount = ether('100'); diff --git a/test/CDPManager_PoolToken_Keydonix.test.js b/test/CDPManager_PoolToken_Keydonix.test.js index 34c8b0a..26e58c7 100755 --- a/test/CDPManager_PoolToken_Keydonix.test.js +++ b/test/CDPManager_PoolToken_Keydonix.test.js @@ -1,6 +1,5 @@ const { expectEvent, - expectRevert, } = require('openzeppelin-test-helpers'); const BN = web3.utils.BN; const { expect } = require('chai'); @@ -90,7 +89,6 @@ const time = require('./helpers/time'); // get some usdp to cover fee await this.utils.updatePrice(); - await this.utils.spawnEth(new BN('2'), new BN('1'), new BN('2')); // repay debt partially await this.utils.repay(this.poolToken, deployer, usdpAmount.div(new BN(2))); @@ -99,8 +97,6 @@ const time = require('./helpers/time'); expect(accumulatedDebtAfterRepayment.div(new BN(10 ** 12))).to.be.bignumber.equal( expectedDebt.div(new BN(2)).div(new BN(10 ** 12)) ); - - await this.utils.repayAllAndWithdraw(this.poolToken, deployer); }) it('Should partially repay the debt of a position and withdraw collaterals partially', async function() { @@ -214,20 +210,6 @@ const time = require('./helpers/time'); }) }) - describe('Join', function () { - it('Reverts non-spawned position', async function() { - const mainAmount = new BN('100'); - const usdpAmount = new BN('20'); - - const tx = this.utils.join( - this.poolToken, - mainAmount, - usdpAmount - ); - await this.utils.expectRevert(tx, "Unit Protocol: NOT_SPAWNED_POSITION"); - }) - }) - describe('Exit', function () { it('Reverts non valuable tx', async function() { const mainAmount = new BN('100'); @@ -239,16 +221,6 @@ const time = require('./helpers/time'); await this.utils.expectRevert(tx, "Unit Protocol: USELESS_TX"); }) - it('Reverts when specified repayment amount is greater than the accumulated debt', async function() { - const mainAmount = new BN('100'); - const usdpAmount = new BN('20'); - - await this.utils.spawn(this.poolToken, mainAmount, usdpAmount); - - const tx = this.utils.exit(this.poolToken, mainAmount, usdpAmount.add(new BN(1))); - await expectRevert.unspecified(tx); - }) - it('Reverts when position state after exit becomes undercollateralized', async function() { const mainAmount = new BN('100'); const usdpAmount = new BN('20'); diff --git a/test/LiquidationTrigger_Keydonix.test.js b/test/LiquidationTrigger_Keydonix.test.js index e4f18c7..3d15459 100755 --- a/test/LiquidationTrigger_Keydonix.test.js +++ b/test/LiquidationTrigger_Keydonix.test.js @@ -83,4 +83,14 @@ contract('LiquidationTriggerKeydonixMainAsset', function([ expect(liquidationBlock).to.be.bignumber.equal(expectedLiquidationBlock); expect(liquidationPrice).to.be.bignumber.equal(expectedLiquidationPrice); }) + + it('Should fail to trigger liquidation of collateralized position', async function () { + const mainAmount = ether('60'); + const usdpAmount = ether('70'); + + await this.utils.spawn(this.mainCollateral, mainAmount, usdpAmount); + + const tx = this.utils.triggerLiquidation(this.mainCollateral, positionOwner, liquidator); + await this.utils.expectRevert(tx, "Unit Protocol: SAFE_POSITION"); + }) }); diff --git a/test/LiquidationTrigger_PoolToken_Keydonix.test.js b/test/LiquidationTrigger_PoolToken_Keydonix.test.js index 70ad747..288e60a 100755 --- a/test/LiquidationTrigger_PoolToken_Keydonix.test.js +++ b/test/LiquidationTrigger_PoolToken_Keydonix.test.js @@ -79,5 +79,15 @@ contract('LiquidationTriggerKeydonixPoolToken', function([ expect(liquidationBlock).to.be.bignumber.equal(expectedLiquidationBlock); expect(liquidationPrice).to.be.bignumber.equal(expectedLiquidationPrice); }) + + it('Should fail to trigger liquidation of collateralized position', async function () { + const mainAmount = new BN('3'); + const usdpAmount = new BN('78'); + + await this.utils.spawn(this.poolToken, mainAmount, usdpAmount); + + const tx = this.utils.triggerLiquidation(this.poolToken, positionOwner, liquidator); + await this.utils.expectRevert(tx, "Unit Protocol: SAFE_POSITION"); + }) }); }); diff --git a/test/helpers/utils.js b/test/helpers/utils.js index 4903789..f604a07 100644 --- a/test/helpers/utils.js +++ b/test/helpers/utils.js @@ -22,6 +22,7 @@ const IUniswapV2Factory = artifacts.require('IUniswapV2Factory'); const IUniswapV2Pair = artifacts.require('IUniswapV2PairFull'); const UniswapV2Router02 = artifacts.require('UniswapV2Router02'); const CDPManager = artifacts.require('CDPManager01'); +const CDPManagerFallback = artifacts.require('CDPManager01_Fallback'); const LiquidationAuction = artifacts.require('LiquidationAuction02'); const CDPRegistry = artifacts.require('CDPRegistry'); const CollateralRegistry = artifacts.require('CollateralRegistry'); @@ -89,13 +90,6 @@ module.exports = (context, mode) => { return IUniswapV2Pair.at(poolAddress); }; - const repayAllAndWithdraw = async (main, user) => { - const totalDebt = await context.vault.getTotalDebt(main.address, user); - await context.usdp.approve(context.vault.address, totalDebt); - const mainAmount = await context.vault.collaterals(main.address, user); - return context.vaultManager.exit(main.address, mainAmount, MAX_UINT); - }; - const repayAllAndWithdrawEth = async (user) => { const totalDebt = await context.vault.getTotalDebt(context.weth.address, user); await context.usdp.approve(context.vault.address, totalDebt); @@ -104,15 +98,6 @@ module.exports = (context, mode) => { return context.vaultManager.exit_Eth(mainAmount, MAX_UINT); }; - const repay = async (main, user, usdpAmount) => { - const totalDebt = await context.vault.getTotalDebt(main.address, user); - await context.usdp.approve(context.vault.address, totalDebt); - return context.vaultManagerStandard.repay( - main.address, - usdpAmount, - ); - } - const updatePrice = async () => { await context.ethUsd.setPrice(await context.ethUsd.latestAnswer()); if (chainlink) { @@ -204,12 +189,19 @@ module.exports = (context, mode) => { poolTokenOracleType = 2 context.keydonixOracleMainAssetMock = await KeydonixOracleMainAssetMock.new( context.uniswapFactory.address, - context.weth.address, - context.ethUsd.address, + context.oracleRegistry.address, ) + + await context.oracleRegistry.setOracle(mainAssetOracleType, context.keydonixOracleMainAssetMock.address); + context.keydonixOraclePoolTokenMock = await KeydonixOraclePoolTokenMock.new( - context.keydonixOracleMainAssetMock.address + context.oracleRegistry.address, + context.vaultParameters.address, ) + + await context.oracleRegistry.setOracle(poolTokenOracleType, context.keydonixOraclePoolTokenMock.address); + + await context.oracleRegistry.setKeydonixOracleTypes([mainAssetOracleType, poolTokenOracleType]); } else if (uniswapKeep3r || sushiswapKeep3r) { context.keep3rOracleMainAssetMock = await Keep3rOracleMainAssetMock.new( context.uniswapFactory.address, @@ -309,8 +301,7 @@ module.exports = (context, mode) => { await context.vaultParameters.setManager(context.vaultManagerParameters.address, true); if (keydonix) { - context.liquidatorKeydonixMainAsset = await LiquidatorKeydonixMainAsset.new(context.vaultManagerParameters.address, context.keydonixOracleMainAssetMock.address); - context.liquidatorKeydonixPoolToken = await LiquidatorKeydonixPoolToken.new(context.vaultManagerParameters.address, context.keydonixOraclePoolTokenMock.address); + context.vaultManager = await CDPManagerFallback.new(context.vaultManagerParameters.address, context.oracleRegistry.address, context.cdpRegistry.address); } else { context.vaultManager = await CDPManager.new(context.vaultManagerParameters.address, context.oracleRegistry.address, context.cdpRegistry.address); } @@ -323,31 +314,8 @@ module.exports = (context, mode) => { context.cdpRegistry.address ); - if (keydonix) { - context.vaultManagerKeydonixMainAsset = await VaultManagerKeydonixMainAsset.new( - context.vaultManagerParameters.address, - context.keydonixOracleMainAssetMock.address, - ); - context.vaultManagerKeydonixPoolToken = await VaultManagerKeydonixPoolToken.new( - context.vaultManagerParameters.address, - context.keydonixOraclePoolTokenMock.address, - ); - context.vaultManagerStandard = await VaultManagerStandard.new( - context.vault.address, - ); - } - - // set access of position manipulation contracts to the Vault - if (keydonix) { - await context.vaultParameters.setVaultAccess(context.vaultManagerKeydonixMainAsset.address, true); - await context.vaultParameters.setVaultAccess(context.vaultManagerKeydonixPoolToken.address, true); - await context.vaultParameters.setVaultAccess(context.liquidatorKeydonixMainAsset.address, true); - await context.vaultParameters.setVaultAccess(context.liquidatorKeydonixPoolToken.address, true); - await context.vaultParameters.setVaultAccess(context.vaultManagerStandard.address, true); - } else { - await context.vaultParameters.setVaultAccess(context.vaultManager.address, true); - } + await context.vaultParameters.setVaultAccess(context.vaultManager.address, true); await context.vaultParameters.setVaultAccess(context.liquidationAuction.address, true); @@ -412,13 +380,13 @@ module.exports = (context, mode) => { exit: w.exit, exitTarget: w.exitTarget, exitEth: w.exitEth, - repayAllAndWithdraw, + repayAllAndWithdraw: w.repayAllAndWithdraw, repayAllAndWithdrawEth, withdrawAndRepay: w.exit, withdrawAndRepayEth: w.exitEth, deploy, updatePrice, - repay, + repay: w.repay, expectRevert, } } diff --git a/test/helpers/wrappers.js b/test/helpers/wrappers.js index 1365984..6f0381b 100644 --- a/test/helpers/wrappers.js +++ b/test/helpers/wrappers.js @@ -1,3 +1,5 @@ +const MAX_UINT = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + module.exports = function(context, mode) { const simpleWrapper = { @@ -56,119 +58,87 @@ module.exports = function(context, mode) { user, { from } ); + }, + repayAllAndWithdraw: async (main, user) => { + const totalDebt = await context.vault.getTotalDebt(main.address, user); + await context.usdp.approve(context.vault.address, totalDebt); + const mainAmount = await context.vault.collaterals(main.address, user); + return context.vaultManager.exit(main.address, mainAmount, MAX_UINT); + }, + repay: async (main, user, usdpAmount) => { + const totalDebt = await context.vault.getTotalDebt(main.address, user); + await context.usdp.approve(context.vault.address, totalDebt); + return context.vaultManager.exit( + main.address, + 0, + usdpAmount, + ); } } - const wrappers = { - keydonixMainAsset: { - spawn: async (main, mainAmount, usdpAmount, { from = context.deployer, noApprove } = {}) => { - if (!noApprove) - await context.approveCollaterals(main, mainAmount, from); - return context.vaultManagerKeydonixMainAsset.spawn( - main.address, - mainAmount, // main - usdpAmount, // USDP - ['0x', '0x', '0x', '0x'], // main price proof - { from }, - ); - }, - spawnEth: async (mainAmount, usdpAmount) => { - return context.vaultManagerKeydonixMainAsset.spawn_Eth( - usdpAmount, // USDP - { value: mainAmount } - ); - }, - join: async (main, mainAmount, usdpAmount) => { - await main.approve(context.vault.address, mainAmount); - return context.vaultManagerKeydonixMainAsset.depositAndBorrow( - main.address, - mainAmount, // main - usdpAmount, // USDP - ['0x', '0x', '0x', '0x'], // main price proof - ) - }, - exit: async (main, mainAmount, usdpAmount) => { - return context.vaultManagerKeydonixMainAsset.withdrawAndRepay( - main.address, - mainAmount, // main - usdpAmount, // USDP - ['0x', '0x', '0x', '0x'], // main price proof - ); - }, - triggerLiquidation: (main, user, from = context.deployer) => { - return context.liquidatorKeydonixMainAsset.triggerLiquidation( - main.address, - user, - ['0x', '0x', '0x', '0x'], // main price proof - { from } - ); - }, - withdrawAndRepay: async (main, mainAmount, usdpAmount) => { - return context.vaultManagerKeydonixMainAsset.withdrawAndRepay( - main.address, - mainAmount, - usdpAmount, - ['0x', '0x', '0x', '0x'], // main price proof - ); - }, - withdrawAndRepayEth: async (mainAmount, usdpAmount) => { - return context.vaultManagerKeydonixMainAsset.withdrawAndRepay_Eth( - mainAmount, - usdpAmount, - ); - }, + const keydonixWrapper = { + join: async (asset, mainAmount, usdpAmount, { noApprove, from = context.deployer } = {}) => { + if (!noApprove) + await asset.approve(context.vault.address, mainAmount, { from }); + return context.vaultManager.join( + asset.address, + mainAmount, // main + usdpAmount, // USDP + ['0x', '0x', '0x', '0x'], // merkle proof mock + { from } + ); + }, + exit: async (asset, mainAmount, usdpAmount) => { + if (+usdpAmount > 0) { + await context.usdp.approve(context.vault.address, usdpAmount) + } + return context.vaultManager.exit( + asset.address, + mainAmount, // main + usdpAmount, // USDP + ['0x', '0x', '0x', '0x'], // merkle proof mock + ); + }, + exitTarget: async (asset, mainAmount, repayment) => { + if (+repayment > 0) { + await context.usdp.approve(context.vault.address, repayment) + } + return context.vaultManager.exit_targetRepayment( + asset.address, + mainAmount, // main + repayment, // USDP + ['0x', '0x', '0x', '0x'], // merkle proof mock + ); + }, + triggerLiquidation: (asset, user, from = context.deployer) => { + return context.vaultManager.triggerLiquidation( + asset.address, + user, + ['0x', '0x', '0x', '0x'], // merkle proof mock + { from } + ); }, - keydonixPoolToken: { - spawn: async (main, mainAmount, usdpAmount, { from = context.deployer, noApprove } = {}) => { - if (!noApprove) - await context.approveCollaterals(main, mainAmount, from); - return context.vaultManagerKeydonixPoolToken.spawn( - main.address, - mainAmount, // main - usdpAmount, // USDP - ['0x', '0x', '0x', '0x'], // underlying token price proof - ); - }, - join: async (main, mainAmount, usdpAmount) => { - await main.approve(context.vault.address, mainAmount); - return context.vaultManagerKeydonixPoolToken.depositAndBorrow( - main.address, - mainAmount, // main - usdpAmount, // USDP - ['0x', '0x', '0x', '0x'], // underlying token price proof - ); - }, - exit: async (main, mainAmount, usdpAmount) => { - return context.vaultManagerKeydonixPoolToken.withdrawAndRepay( - main.address, - mainAmount, // main - usdpAmount, // USDP - ['0x', '0x', '0x', '0x'], // main price proof - ); - }, - triggerLiquidation: (main, user, from = context.deployer) => { - return context.liquidatorKeydonixPoolToken.triggerLiquidation( - main.address, - user, - ['0x', '0x', '0x', '0x'], // main price proof - { from } - ); - }, - withdrawAndRepay: async (main, mainAmount, usdpAmount) => { - return context.vaultManagerKeydonixPoolToken.withdrawAndRepay( - main.address, - mainAmount, - usdpAmount, - ['0x', '0x', '0x', '0x'], // main price proof - ); - }, - spawnEth: async (mainAmount, usdpAmount) => { - return context.vaultManagerKeydonixMainAsset.spawn_Eth( - usdpAmount, // USDP - { value: mainAmount } - ); - }, + repayAllAndWithdraw: async (main, user) => { + const totalDebt = await context.vault.getTotalDebt(main.address, user); + await context.usdp.approve(context.vault.address, totalDebt); + const mainAmount = await context.vault.collaterals(main.address, user); + return context.vaultManager.exit(main.address, mainAmount, MAX_UINT, ['0x', '0x', '0x', '0x']); }, + repay: async (main, user, usdpAmount) => { + const totalDebt = await context.vault.getTotalDebt(main.address, user); + await context.usdp.approve(context.vault.address, totalDebt); + return context.vaultManager.exit( + main.address, + 0, + usdpAmount, + ['0x', '0x', '0x', '0x'], // merkle proof mock + ); + } + } + + const wrappers = { + keydonixMainAsset: keydonixWrapper, + keydonixPoolToken: keydonixWrapper, uniswapKeep3rMainAsset: simpleWrapper, uniswapKeep3rPoolToken: simpleWrapper, chainlinkMainAsset: simpleWrapper,