Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[submodule "configurable-rights-pool"]
path = configurable-rights-pool
url = git@github.com:defidollar/configurable-rights-pool.git
branch = develop
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
# DefiDollar
DefiDollar (DUSD) is a stablecoin backed by [Curve Finance](https://www.curve.fi/) LP tokens.

See [blog post](https://medium.com/@atvanguard/a-curvy-defidollar-c249438c154a).
DefiDollar ($DUSD) DefiDollar $DUSD is a stablecoin that uses DeFi primitives to stay near the $1 mark.

---
### Development

1. Run Ganache

```
npm run compile
npm run ganache
```

2. Setup Balancer Deps
```
npm run setup:balancer
```

3. Compile and Migrate
```
npm run compile
npm run migrate
```

4. Run Tests
```
npm t
```
1 change: 1 addition & 0 deletions configurable-rights-pool
13 changes: 13 additions & 0 deletions contracts/common/mocks/MockAToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pragma solidity 0.5.17;

import "./Reserve.sol";

contract MockAToken is Reserve {
constructor (string memory _name, string memory _symbol)
public
Reserve(_name, _symbol, 18)
{
}

function redirectInterestStream(address) external {}
}
26 changes: 26 additions & 0 deletions contracts/interfaces/IAave.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
pragma solidity 0.5.17;

interface aToken {
function redeem(uint256 _amount) external;
function transfer(address recipient, uint256 amount) external;
function transferFrom(address from, address to, uint256 amount) external;
function redirectInterestStream(address _to) external;
function redirectInterestStreamOf(address _from, address _to) external;
function allowInterestRedirectionTo(address _to) external;
}

interface LendingPool {
function deposit(address _reserve, uint256 _amount, uint16 _referralCode) external;
function getReserveData(address _reserve) external;
}

interface LendingPoolAddressesProvider {
function getLendingPool() external view returns (address);
function getLendingPoolCore() external view returns (address payable);
}

interface PriceOracleGetter {
function getAssetPrice(address _asset) external view returns (uint256);
function getAssetPrices(address[] calldata _assets) external view returns (uint256[] memory);
function getFallbackOracle() external view returns (address);
}
84 changes: 84 additions & 0 deletions contracts/interfaces/IConfigurableRightsPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
pragma solidity 0.5.17;

interface IConfigurableRightsPool {
function joinPool(uint poolAmountout, uint[] calldata maxAmountsIn) external;
function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut) external;
function getDenormalizedWeight(address token) external view returns (uint);
function getNormalizedWeight(address token) external view returns (uint);
}

interface IBPool {
function isBound(address token) external view returns(bool);
function getBalance(address token) external view returns (uint);
function rebind(address token, uint balance, uint denorm) external;
function setSwapFee(uint swapFee) external;
function bind(address token, uint balance, uint denorm) external;
function getDenormalizedWeight(address token) external view returns (uint);
function getNormalizedWeight(address token) external view returns (uint);
function getTotalDenormalizedWeight() external view returns (uint);
function setPublicSwap(bool public_) external;
function getCurrentTokens() external view returns (address[] memory tokens);
function calcPoolOutGivenSingleIn(
uint tokenBalanceIn,
uint tokenWeightIn,
uint poolSupply,
uint totalWeight,
uint tokenAmountIn,
uint swapFee
)
external pure
returns (uint poolAmountOut);

function calcSingleInGivenPoolOut(
uint tokenBalanceIn,
uint tokenWeightIn,
uint poolSupply,
uint totalWeight,
uint poolAmountOut,
uint swapFee
)
external pure
returns (uint tokenAmountIn);

function calcSingleOutGivenPoolIn(
uint tokenBalanceOut,
uint tokenWeightOut,
uint poolSupply,
uint totalWeight,
uint poolAmountIn,
uint swapFee
)
external pure
returns (uint tokenAmountOut);

function calcPoolInGivenSingleOut(
uint tokenBalanceOut,
uint tokenWeightOut,
uint poolSupply,
uint totalWeight,
uint tokenAmountOut,
uint swapFee
)
external pure
returns (uint poolAmountIn);

function swapExactAmountOut(
address tokenIn,
uint maxAmountIn,
address tokenOut,
uint tokenAmountOut,
uint maxPrice
)
external
returns (uint tokenAmountIn, uint spotPriceAfter);

function swapExactAmountIn(
address tokenIn,
uint tokenAmountIn,
address tokenOut,
uint minAmountOut,
uint maxPrice
)
external
returns (uint tokenAmountOut, uint spotPriceAfter);
}
1 change: 1 addition & 0 deletions contracts/interfaces/ICurve.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface ICurve {
function balances(int128 i) external view returns(uint);
function get_virtual_price() external view returns(uint);
function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external;
function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) external;
// for tests
function mock_add_to_balance(uint[4] calldata amounts) external;
}
Expand Down
186 changes: 186 additions & 0 deletions contracts/peaks/dai-susd/StableIndexPeak.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
pragma solidity 0.5.17;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20, SafeMath} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/math/Math.sol";

import {IPeak} from "../../interfaces/IPeak.sol";
import {ICore} from "../../interfaces/ICore.sol";
import {IOracle} from "../../interfaces/IOracle.sol";
import {ICurve} from "../../interfaces/ICurve.sol";
import {aToken, PriceOracleGetter} from "../../interfaces/IAave.sol";
import {IConfigurableRightsPool, IBPool} from "../../interfaces/IConfigurableRightsPool.sol";

import {Initializable} from "../../common/Initializable.sol";
import {OwnableProxy} from "../../common/OwnableProxy.sol";

contract StableIndexPeak is OwnableProxy, Initializable, IPeak {
using SafeERC20 for IERC20;
using SafeMath for uint;
using Math for uint;

// stablecoins and aTokens
uint constant public index = 2; // No. of stablecoins in peak

address[index] public reserveTokens = [
0x6B175474E89094C44Da98b954EedeAC495271d0F, // DAI
0x57Ab1ec28D129707052df4dF418D58a2D46d5f51 // sUSD
];

address[index] public interestTokens = [
0xfC1E690f61EFd961294b3e1Ce3313fBD8aa4f85d, // aDAI
0x625aE63000f46200499120B906716420bd059240 // aSUSD
];

// Configurable Rights Pool
IConfigurableRightsPool crp;
IBPool bPool;

// Aave Oracle
PriceOracleGetter priceOracle;
IOracle public oracle;

// Core contract
ICore core;
IERC20 dusd;

function initialize(
IConfigurableRightsPool _crp,
IBPool _bPool,
PriceOracleGetter _priceOracle,
IOracle _oracle
) public notInitialized {
// CRP & BPool
crp = _crp;
bPool = _bPool;
// Aave Price Oracle
priceOracle = _priceOracle;
oracle = _oracle;
// Curve swap
// Tokens
core = ICore(0xE449Ca7d10b041255E7e989D158Bee355d8f88d3);
dusd = IERC20(0x5BC25f649fc4e26069dDF4cF4010F9f706c23831);
}

// Returns average ETHUSD value from chainlink feeds
function ethusd() public view returns (uint value) {
uint[] memory feed = oracle.getPriceFeed();
for (uint i = 0; i < feed.length; i++) {
value.add(feed[i]);
}
return value.div(feed.length);
}

// Convert aToken wei value to usd
function weiToUSD(uint price) public view returns (uint) {
return price.mul(ethusd());
}

// Return prices of reserve tokens (wei)
function getPrices() public view returns (uint256[] memory prices) {
address[index] memory _reserveTokens = reserveTokens;
for (uint i = 0; i < index; i++) {
prices[i] = priceOracle.getAssetPrice(_reserveTokens[i]);
}
return prices;
}

// Return price of a reserve asset (wei)
function getPrice(address token) public view returns (uint price) {
return priceOracle.getAssetPrice(token);
}

function mint(uint[] calldata inAmounts) external {
// aTokens (zap -> peak)
address[index] memory _interestTokens = interestTokens;
for(uint i = 0; i < index; i++) {
aToken(_interestTokens[i]).transferFrom(msg.sender, address(this), inAmounts[i]);
IERC20(_interestTokens[i]).safeApprove(address(crp), inAmounts[i]);
}
// Migrate liquidity to BPool via CRP
crp.joinPool(0, inAmounts); // Check BPT
}

function redeem(uint dusdAmount) external {
address[index] memory _interestTokens = interestTokens;
uint[] memory balances;
// DUSD value (How many BPT tokens to redeem)
uint usd = core.dusdToUsd(dusdAmount, true);
uint bpt = bptValue(dusdAmount).div(usd);
// aToken balances before
for (uint i = 0; i < index; i++) {
balances[i] = IERC20(_interestTokens[i]).balanceOf(address(this));
}
// Remove liquidity
uint[] memory minAmountsOut;
for (uint i = 0; i < index; i++) {
minAmountsOut[i] = 0;
}
require(minAmountsOut.length <= 8, "Error: Balancer pool 8 tokens maximum.");
crp.exitPool(bpt, minAmountsOut);
// aTokens balances after (peak -> zap)
for (uint i = 0; i < index; i++) {
balances[i] = IERC20(_interestTokens[i]).balanceOf(address(this)).sub(balances[i]);
aToken(_interestTokens[i]).transfer(msg.sender, balances[i]);
}
}

function bptValue(uint /*dusdAmount*/) internal view returns (uint bpt) {
uint bptTotal = IERC20(address(bPool)).balanceOf(address(this));
uint poolValue = bPoolValue();
return bptTotal.div(poolValue);
}

// Assuming crp have provided peak with allowance (NOT NEEDED)
function redirectInterest(address _from, address _to) public onlyOwner {
address[index] memory _interestTokens = interestTokens;
for (uint i = 0; i < index; i++) {
aToken(_interestTokens[i]).redirectInterestStreamOf(_from, _to);
}
}

// USD valuation of stable index peak (aTokens interest + bPool deposits)
function portfolioValue() external view returns (uint) {
return peakValue().add(bPoolValue());
}

// Internal Functions
function peakValue() public view returns (uint interest) {
address[index] memory _interestTokens = interestTokens;
for (uint i = 0; i < index; i++) {
interest.add(weiToUSD(IERC20(_interestTokens[i]).balanceOf(address(this)).div(1e18)));
}
return interest;
}

function bPoolValue() public view returns (uint value) {
address[index] memory _interestTokens;
for (uint i = 0; i < index; i++) {
value.add(weiToUSD(IERC20(_interestTokens[i]).balanceOf(address(bPool)).div(1e18)));
}
return value;
}

function vars() public view returns (
address _crp,
address _bPool,
address _priceOracle,
address _oracle,
address _core,
address _dusd,
address[index] memory _reserveTokens,
address[index] memory _interestTokens
) {
return(
address(crp),
address(bPool),
address(priceOracle),
address(oracle),
address(core),
address(dusd),
reserveTokens,
interestTokens
);
}

}
7 changes: 7 additions & 0 deletions contracts/peaks/dai-susd/StableIndexPeakProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pragma solidity 0.5.17;

import {UpgradableProxy} from "../../common/proxy/UpgradableProxy.sol";

contract StableIndexPeakProxy is UpgradableProxy {
constructor() public UpgradableProxy() {}
}
Loading