Skip to content

Commit

Permalink
Merge pull request unitprotocol#29 from unitprotocol/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
C11online authored May 18, 2021
2 parents 17e9926 + 2be0e86 commit 9d95384
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 48 deletions.
44 changes: 44 additions & 0 deletions contracts/auction/ForceTransferAssetStore.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: bsl-1.1

/*
Copyright 2020 Unit Protocol: Artem Zakharov ([email protected]).
*/
pragma solidity 0.7.6;

import "../VaultParameters.sol";
import "../interfaces/IForceTransferAssetStore.sol";


/**
* @title ForceTransferAssetStore
**/
contract ForceTransferAssetStore is Auth, IForceTransferAssetStore {

/*
Mapping of assets that require a transfer of at least 1 unit of token
to update internal logic related to staking rewards in case of full liquidation
*/
mapping(address => bool) public override shouldForceTransfer;

event ForceTransferAssetAdded(address indexed asset);

constructor(address _vaultParameters, address[] memory initialAssets) Auth(_vaultParameters) {
for (uint i = 0; i < initialAssets.length; i++) {
require(!shouldForceTransfer[initialAssets[i]], "Unit Protocol: Already exists");
shouldForceTransfer[initialAssets[i]] = true;
emit ForceTransferAssetAdded(initialAssets[i]);
}
}

/**
* @notice Only manager is able to call this function
* @dev Mark asset as `shouldForceTransfer`
* @param asset The address of the asset
**/
function add(address asset) external override onlyManager {
require(!shouldForceTransfer[asset], "Unit Protocol: Already exists");
require(asset != address(0), "Unit Protocol: ZERO_ADDRESS");
shouldForceTransfer[asset] = true;
emit ForceTransferAssetAdded(asset);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@
*/
pragma solidity 0.7.6;

import './interfaces/IOracleRegistry.sol';
import './interfaces/IVault.sol';
import './interfaces/ICDPRegistry.sol';
import './interfaces/IVaultManagerParameters.sol';
import './interfaces/IVaultParameters.sol';
import './interfaces/ICurveProvider.sol';
import './interfaces/ICurveRegistry.sol';
import './interfaces/IWrappedToUnderlyingOracle.sol';

import './helpers/ReentrancyGuard.sol';
import './helpers/SafeMath.sol';
import '../interfaces/IOracleRegistry.sol';
import '../interfaces/IVault.sol';
import '../interfaces/ICDPRegistry.sol';
import '../interfaces/IVaultManagerParameters.sol';
import '../interfaces/IVaultParameters.sol';
import '../interfaces/IWrappedToUnderlyingOracle.sol';
import '../interfaces/IForceTransferAssetStore.sol';

import '../helpers/ReentrancyGuard.sol';
import '../helpers/SafeMath.sol';

/**
* @title LiquidationAuction02
Expand All @@ -25,11 +24,8 @@ contract LiquidationAuction02 is ReentrancyGuard {

IVault public immutable vault;
IVaultManagerParameters public immutable vaultManagerParameters;
IOracleRegistry public immutable oracleRegistry;
ICDPRegistry public immutable cdpRegistry;

// CurveProvider contract
ICurveProvider public immutable curveProvider;
IForceTransferAssetStore public immutable forceTransferAssetStore;

uint public constant DENOMINATOR_1E2 = 1e2;
uint public constant WRAPPED_TO_UNDERLYING_ORACLE_TYPE = 11;
Expand All @@ -46,23 +42,19 @@ contract LiquidationAuction02 is ReentrancyGuard {

/**
* @param _vaultManagerParameters The address of the contract with Vault manager parameters
* @param _oracleRegistry The address of the oracle registry
* @param _curveProvider The address of the Curve Provider. Mainnet: 0x0000000022D53366457F9d5E68Ec105046FC4383
* @param _cdpRegistry The address of the CDP registry
* @param _forceTransferAssetStore The address of the ForceTransferAssetStore
**/
constructor(address _vaultManagerParameters, address _oracleRegistry, address _curveProvider, address _cdpRegistry) {
constructor(address _vaultManagerParameters, address _cdpRegistry, address _forceTransferAssetStore) {
require(
_vaultManagerParameters != address(0) &&
_oracleRegistry != address(0) &&
_cdpRegistry != address(0) &&
_curveProvider != address(0),
_vaultManagerParameters != address(0) &&
_forceTransferAssetStore != (address(0)),
"Unit Protocol: INVALID_ARGS"
);
vaultManagerParameters = IVaultManagerParameters(_vaultManagerParameters);
vault = IVault(IVaultParameters(IVaultManagerParameters(_vaultManagerParameters).vaultParameters()).vault());
oracleRegistry = IOracleRegistry(_oracleRegistry);
curveProvider = ICurveProvider(_curveProvider);
cdpRegistry = ICDPRegistry(_cdpRegistry);
forceTransferAssetStore = IForceTransferAssetStore(_forceTransferAssetStore);
}

/**
Expand Down Expand Up @@ -91,8 +83,8 @@ contract LiquidationAuction02 is ReentrancyGuard {
collateralInPosition
);

// ensure that at least 1 wei of Curve LP is transferred to cdp owner
if (collateralToOwner == 0 && isCurveLP(asset)) {
// ensure that at least 1 unit of token is transferred to cdp owner
if (collateralToOwner == 0 && forceTransferAssetStore.shouldForceTransfer(asset)) {
collateralToOwner = 1;
collateralToLiquidator = collateralToLiquidator.sub(1);
}
Expand Down Expand Up @@ -160,12 +152,4 @@ contract LiquidationAuction02 is ReentrancyGuard {
collateralToBuyer = collateralInPosition;
}
}

function isCurveLP(address asset) public view returns (bool) {
address underlying = IWrappedToUnderlyingOracle(oracleRegistry.oracleByType(WRAPPED_TO_UNDERLYING_ORACLE_TYPE)).assetToUnderlying(asset);

if (underlying == address(0)) { return false; }

return ICurveRegistry(curveProvider.get_registry()).get_pool_from_lp_token(underlying) != address(0);
}
}
4 changes: 4 additions & 0 deletions contracts/interfaces/IForceTransferAssetStore.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
interface IForceTransferAssetStore {
function shouldForceTransfer ( address ) external view returns ( bool );
function add ( address asset ) external;
}
6 changes: 6 additions & 0 deletions contracts/interfaces/IcyToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
interface IcyToken {
function underlying() external view returns (address);
function implementation() external view returns (address);
function decimals() external view returns (uint8);
function exchangeRateStored() external view returns (uint);
}
61 changes: 61 additions & 0 deletions contracts/oracles/CyTokenOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: bsl-1.1

pragma solidity 0.7.6;

import "../helpers/SafeMath.sol";
import "../helpers/ERC20Like.sol";
import "../interfaces/IcyToken.sol";
import "../interfaces/IOracleUsd.sol";
import "../interfaces/IOracleRegistry.sol";
import "../interfaces/IOracleEth.sol";
import "../VaultParameters.sol";

/**
* @title CyTokensOracle
* @dev Wrapper to quote cyToken assets like cyWETH, cyDAI, cyUSDT, cyUSDC
* @dev cyToken list: https://docs.cream.finance/iron-bank/iron-bank#yearn-token-cytoken
**/

contract CyTokenOracle is IOracleUsd, Auth {
using SafeMath for uint;

uint constant expScale = 1e18;

address public cytokenImplementation;

IOracleRegistry public immutable oracleRegistry;

event NewImplementation(address indexed implementation);

constructor(address _vaultParameters, address _oracleRegistry, address _cytokenImplementation) Auth(_vaultParameters) {
require(_vaultParameters != address(0) && _oracleRegistry != address(0), "Unit Protocol: ZERO_ADDRESS");
oracleRegistry = IOracleRegistry(_oracleRegistry);
cytokenImplementation = _cytokenImplementation;
}

function setNewImplementation(address newImplementation) external onlyManager {
cytokenImplementation = newImplementation;
emit NewImplementation(newImplementation);
}

// returns Q112-encoded value
function assetToUsd(address bearing, uint amount) public override view returns (uint) {
if (amount == 0) return 0;
(address underlying, uint underlyingAmount) = bearingToUnderlying(bearing, amount);
IOracleUsd _oracleForUnderlying = IOracleUsd(oracleRegistry.oracleByAsset(underlying));
require(address(_oracleForUnderlying) != address(0), "Unit Protocol: ORACLE_NOT_FOUND");
return _oracleForUnderlying.assetToUsd(underlying, underlyingAmount);
}

function bearingToUnderlying(address bearing, uint amount) public view returns (address, uint) {
address _underlying = IcyToken(bearing).underlying();
require(_underlying != address(0), "Unit Protocol: UNDEFINED_UNDERLYING");
address _implementation = IcyToken(bearing).implementation();
require(_implementation == cytokenImplementation, "Unit Protocol: CYTOKEN_IMPLEMENTATION_CHANGED");
uint _exchangeRateStored = IcyToken(bearing).exchangeRateStored();
uint _totalSupply = ERC20Like(bearing).totalSupply();
require(amount <= _totalSupply, "Unit Protocol: AMOUNT_EXCEEDS_SUPPLY");
return (_underlying, amount.mul(_exchangeRateStored).div(expScale));
}

}
41 changes: 41 additions & 0 deletions contracts/test-helpers/CyWETH.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: bsl-1.1

/*
Copyright 2020 Unit Protocol: Artem Zakharov ([email protected]).
*/
pragma solidity 0.7.6;

import "./EmptyToken.sol";


contract CyWETH is EmptyToken {

address public underlying;

address public implementation;

uint public exchangeRateStoredInternal;

constructor(
uint _totalSupply,
address _underlying,
address _implementation,
uint _exchangeRateStoredInternal
) EmptyToken(
"Yearn Wrapped Ether",
"cyWETH",
8,
_totalSupply,
msg.sender
)
public {
underlying = _underlying;
implementation = _implementation;
exchangeRateStoredInternal = _exchangeRateStoredInternal;
}

function exchangeRateStored() public view returns (uint) {
return exchangeRateStoredInternal;
}

}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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:cytoken": "truffle test test/CyTokenOracle.test.js",
"build": "rm -rf build && truffle compile",
"coverage": "truffle run coverage"
},
Expand Down
45 changes: 45 additions & 0 deletions test/CyTokenOracle.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const BN = web3.utils.BN
const { expect } = require('chai')
const utils = require('./helpers/utils')

const Q112 = new BN('2').pow(new BN('112'))

contract('CyTokenOracle', function([
account1,
foundation,
]) {
// deploy & initial settings
beforeEach(async function() {
this.utils = utils(this, 'cyWETHsample')
this.deployer = account1
this.foundation = foundation;
await this.utils.deploy()
});

it('Should check implementation of cyWETH with data from CyTokenOracle', async function () {
const oracleImplementation = await this.CyTokenOracle.cytokenImplementation();
const tokenImplementation = await this.cyWETH.implementation();
expect(tokenImplementation).to.equal(oracleImplementation);
});

it('Should check that underlying of cyWETH equal to WETH address', async function () {
const underlying = await this.cyWETH.underlying();
expect(underlying).to.equal(this.weth.address);
});

let cyWETHamount = 20000000000;

it('Should check that cyWETH totalSupply not less then cyWETH amount', async function () {
const supply = await this.cyWETH.totalSupply();
expect(!(supply < cyWETHamount)).to.be.true;
});

it('Should quote cyWETH', async function () {
const storedRate = await this.cyWETH.exchangeRateStored();
const rate = await this.CyTokenOracle.bearingToUnderlying(this.cyWETH.address, cyWETHamount);
// since 1 WETH token costs 250$
const expectedUsdValue_q112 = rate[1].mul(Q112).mul(new BN('250'));
const usd_q112 = await this.CyTokenOracle.assetToUsd(this.cyWETH.address, cyWETHamount);
expect(usd_q112).to.be.bignumber.equal(expectedUsdValue_q112);
});
});
Loading

0 comments on commit 9d95384

Please sign in to comment.