-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Release Candidate] Connect cDAI with the Dai Savings Rate
This patch connects cDAI to the Dai Saving Rate, giving users the cominbation of DSR interest and borrowing interest. Any unborrowed assets "cash" are swept into the DSR and that interest is given to suppliers. That cash can still be borrowed and any interest from borrowers will still go back to suppliers. In this way, Compound suppliers will see a supply rate that is always higher than the Dai Savings Rate.
- Loading branch information
Showing
18 changed files
with
2,549 additions
and
95 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
pragma solidity ^0.5.12; | ||
|
||
import "./CErc20Delegate.sol"; | ||
|
||
/** | ||
* @title Compound's CDai Contract | ||
* @notice CToken which wraps Multi-Collateral DAI | ||
* @author Compound | ||
*/ | ||
contract CDaiDelegate is CErc20Delegate { | ||
/** | ||
* @notice DAI adapter address | ||
*/ | ||
address public daiJoinAddress; | ||
|
||
/** | ||
* @notice DAI Savings Rate (DSR) pot address | ||
*/ | ||
address public potAddress; | ||
|
||
/** | ||
* @notice DAI vat address | ||
*/ | ||
address public vatAddress; | ||
|
||
/** | ||
* @notice Delegate interface to become the implementation | ||
* @param data The encoded arguments for becoming | ||
*/ | ||
function _becomeImplementation(bytes memory data) public { | ||
require(msg.sender == admin, "only the admin may initialize the implementation"); | ||
|
||
(address daiJoinAddress_, address potAddress_) = abi.decode(data, (address, address)); | ||
return _becomeImplementation(daiJoinAddress_, potAddress_); | ||
} | ||
|
||
/** | ||
* @notice Explicit interface to become the implementation | ||
* @param daiJoinAddress_ DAI adapter address | ||
* @param potAddress_ DAI Savings Rate (DSR) pot address | ||
*/ | ||
function _becomeImplementation(address daiJoinAddress_, address potAddress_) internal { | ||
// Get dai and vat and sanity check the underlying | ||
DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress_); | ||
PotLike pot = PotLike(potAddress_); | ||
GemLike dai = daiJoin.dai(); | ||
VatLike vat = daiJoin.vat(); | ||
require(address(dai) == underlying, "DAI must be the same as underlying"); | ||
|
||
// Remember the relevant addresses | ||
daiJoinAddress = daiJoinAddress_; | ||
potAddress = potAddress_; | ||
vatAddress = address(vat); | ||
|
||
// Approve moving our DAI into the vat through daiJoin | ||
dai.approve(daiJoinAddress, uint(-1)); | ||
|
||
// Approve the pot to transfer our funds within the vat | ||
vat.hope(potAddress); | ||
vat.hope(daiJoinAddress); | ||
|
||
// Accumulate DSR interest -- must do this in order to doTransferIn | ||
pot.drip(); | ||
|
||
// Transfer all cash in (doTransferIn does this regardless of amount) | ||
doTransferIn(address(this), 0); | ||
} | ||
|
||
/** | ||
* @notice Delegate interface to resign the implementation | ||
*/ | ||
function _resignImplementation() public { | ||
require(msg.sender == admin, "only the admin may abandon the implementation"); | ||
|
||
// Transfer all cash out of the DSR - note that this relies on self-transfer | ||
DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress); | ||
PotLike pot = PotLike(potAddress); | ||
VatLike vat = VatLike(vatAddress); | ||
|
||
// Accumulate interest | ||
pot.drip(); | ||
|
||
// Calculate the total amount in the pot, and move it out | ||
uint pie = pot.pie(address(this)); | ||
pot.exit(pie); | ||
|
||
// Checks the actual balance of DAI in the vat after the pot exit | ||
uint bal = vat.dai(address(this)); | ||
|
||
// Remove our whole balance | ||
daiJoin.exit(address(this), bal / RAY); | ||
} | ||
|
||
/*** CToken Overrides ***/ | ||
|
||
/** | ||
* @notice Accrues DSR then applies accrued interest to total borrows and reserves | ||
* @dev This calculates interest accrued from the last checkpointed block | ||
* up to the current block and writes new checkpoint to storage. | ||
*/ | ||
function accrueInterest() public returns (uint) { | ||
// Accumulate DSR interest | ||
PotLike(potAddress).drip(); | ||
|
||
// Accumulate CToken interest | ||
return super.accrueInterest(); | ||
} | ||
|
||
/*** Safe Token ***/ | ||
|
||
/** | ||
* @notice Gets balance of this contract in terms of the underlying | ||
* @dev This excludes the value of the current message, if any | ||
* @return The quantity of underlying tokens owned by this contract | ||
*/ | ||
function getCashPrior() internal view returns (uint) { | ||
PotLike pot = PotLike(potAddress); | ||
uint pie = pot.pie(address(this)); | ||
return mul(pot.chi(), pie) / RAY; | ||
} | ||
|
||
/** | ||
* @notice Transfer the underlying to this contract and sweep into DSR pot | ||
* @param from Address to transfer funds from | ||
* @param amount Amount of underlying to transfer | ||
* @return The actual amount that is transferred | ||
*/ | ||
function doTransferIn(address from, uint amount) internal returns (uint) { | ||
// Perform the EIP-20 transfer in | ||
EIP20Interface token = EIP20Interface(underlying); | ||
require(token.transferFrom(from, address(this), amount), "unexpected EIP-20 transfer in return"); | ||
|
||
DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress); | ||
GemLike dai = GemLike(underlying); | ||
PotLike pot = PotLike(potAddress); | ||
VatLike vat = VatLike(vatAddress); | ||
|
||
// Convert all our DAI to internal DAI in the vat | ||
daiJoin.join(address(this), dai.balanceOf(address(this))); | ||
|
||
// Checks the actual balance of DAI in the vat after the join | ||
uint bal = vat.dai(address(this)); | ||
|
||
// Calculate the percentage increase to th pot for the entire vat, and move it in | ||
// Note: We may leave a tiny bit of DAI in the vat...but we do the whole thing every time | ||
uint pie = bal / pot.chi(); | ||
pot.join(pie); | ||
|
||
return amount; | ||
} | ||
|
||
/** | ||
* @notice Transfer the underlying from this contract, after sweeping out of DSR pot | ||
* @param to Address to transfer funds to | ||
* @param amount Amount of underlying to transfer | ||
*/ | ||
function doTransferOut(address payable to, uint amount) internal { | ||
DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress); | ||
PotLike pot = PotLike(potAddress); | ||
VatLike vat = VatLike(vatAddress); | ||
|
||
// Calculate the percentage decrease from the pot, and move that much out | ||
// Note: Use a slightly larger pie size to ensure that we get at least amount in the vat | ||
uint pie = mul(add(amount, 1), RAY) / pot.chi(); | ||
pot.exit(pie); | ||
|
||
// Checks the actual balance of DAI in the vat after the pot exit | ||
uint bal = vat.dai(address(this)); | ||
|
||
// Remove our whole balance if rounding would lead us to remove more than we have | ||
daiJoin.exit(to, bal >= mul(amount, RAY) ? amount : bal / RAY); | ||
} | ||
|
||
/*** Maker Internals ***/ | ||
|
||
uint256 constant RAY = 10 ** 27; | ||
|
||
function add(uint x, uint y) internal pure returns (uint z) { | ||
require((z = x + y) >= x, "add-overflow"); | ||
} | ||
|
||
function mul(uint x, uint y) internal pure returns (uint z) { | ||
require(y == 0 || (z = x * y) / y == x, "mul-overflow"); | ||
} | ||
} | ||
|
||
/*** Maker Interfaces ***/ | ||
|
||
contract PotLike { | ||
function chi() public view returns (uint); | ||
function dsr() public view returns (uint); | ||
function rho() public view returns (uint); | ||
function pie(address) public view returns (uint); | ||
function drip() public returns (uint); | ||
function join(uint) public; | ||
function exit(uint) public; | ||
} | ||
|
||
contract GemLike { | ||
function approve(address, uint) public; | ||
function balanceOf(address) public view returns (uint); | ||
function transfer(address, uint) public; | ||
function transferFrom(address, address, uint) public; | ||
function deposit() public payable; | ||
function withdraw(uint) public; | ||
} | ||
|
||
contract VatLike { | ||
function can(address, address) public view returns (uint); | ||
function ilks(bytes32) public view returns (uint, uint, uint, uint, uint); | ||
function dai(address) public view returns (uint); | ||
function urns(bytes32, address) public view returns (uint, uint); | ||
function frob(bytes32, address, address, address, int, int) public; | ||
function hope(address) public; | ||
function move(address, address, uint) public; | ||
} | ||
|
||
contract DaiJoinLike { | ||
function vat() public returns (VatLike); | ||
function dai() public returns (GemLike); | ||
function join(address, uint) public payable; | ||
function exit(address, uint) public; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
pragma solidity ^0.5.12; | ||
|
||
import "./JumpRateModel.sol"; | ||
import "./SafeMath.sol"; | ||
|
||
/** | ||
* @title Compound's DAIInterestRateModel Contract | ||
* @author Compound | ||
* @notice The parameterized model described in section 2.4 of the original Compound Protocol whitepaper | ||
*/ | ||
contract DAIInterestRateModel is JumpRateModel { | ||
using SafeMath for uint; | ||
|
||
PotLike pot; | ||
JugLike jug; | ||
|
||
/** | ||
* @notice Construct an interest rate model | ||
* @param _pot The approximate target base APR, as a mantissa (scaled by 1e18) | ||
* @param _jug The rate of increase in interest rate wrt utilization (scaled by 1e18) | ||
* @param _kink The utilization point at which an additional multiplier is applied | ||
* @param _jump The additional multiplier to be applied to multiplierPerBlock after hitting a specified utilization point | ||
*/ | ||
constructor(address _pot, address _jug, uint _kink, uint _jump) JumpRateModel(0, 0, _kink, _jump) public { | ||
pot = PotLike(_pot); | ||
jug = JugLike(_jug); | ||
poke(); | ||
} | ||
|
||
/** | ||
* @notice Calculates the current supply interest rate per block including the Dai savings rate | ||
* @param cash The total amount of cash the market has | ||
* @param borrows The total amount of borrows the market has outstanding | ||
* @param reserves The total amnount of reserves the market has | ||
* @param reserveFactorMantissa The current reserve factor the market has | ||
* @return The supply rate per block (as a percentage, and scaled by 1e18) | ||
*/ | ||
function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) public view returns (uint) { | ||
uint protocolRate = super.getSupplyRate(cash, borrows, reserves, reserveFactorMantissa); | ||
|
||
uint underlying = cash.add(borrows).sub(reserves); | ||
if (underlying == 0) { | ||
return protocolRate; | ||
} else { | ||
uint cashRate = cash.mul(dsrPerBlock()).div(underlying); | ||
return cashRate.add(protocolRate); | ||
} | ||
} | ||
|
||
/** | ||
* @notice Calculates the Dai savings rate per block | ||
* @return The Dai savings rate per block (as a percentage, and scaled by 1e18) | ||
*/ | ||
function dsrPerBlock() public view returns (uint) { | ||
return pot | ||
.dsr().sub(1e27) // scaled 1e27 aka RAY, and includes an extra "ONE" before subraction | ||
.div(1e9) // descale to 1e18 | ||
.mul(15); // 15 seconds per block | ||
} | ||
|
||
|
||
/** | ||
* @notice Resets the baseRate and multiplier per block based on the stability fee and Dai savings rate | ||
*/ | ||
function poke() public { | ||
(uint duty, ) = jug.ilks("ETH-A"); | ||
uint stabilityFee = duty.add(jug.base()).sub(1e27).mul(1e18).div(1e27).mul(15); | ||
|
||
baseRatePerBlock = dsrPerBlock().mul(1e18).div(0.9e18); // ensure borrow rate is higher than savings rate | ||
multiplierPerBlock = stabilityFee.sub(baseRatePerBlock).mul(1e18).div(kink); | ||
|
||
emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, kink, jump); | ||
} | ||
} | ||
|
||
|
||
/*** Maker Interfaces ***/ | ||
|
||
contract PotLike { | ||
function chi() public view returns (uint); | ||
function dsr() public view returns (uint); | ||
function rho() public view returns (uint); | ||
function pie(address) public view returns (uint); | ||
function drip() public returns (uint); | ||
function join(uint) public; | ||
function exit(uint) public; | ||
} | ||
|
||
contract JugLike { | ||
// --- Data --- | ||
struct Ilk { | ||
uint256 duty; | ||
uint256 rho; | ||
} | ||
|
||
mapping (bytes32 => Ilk) public ilks; | ||
uint256 public base; | ||
} | ||
|
Oops, something went wrong.