Skip to content

Commit bfc2914

Browse files
authored
Merge pull request #13 from compound-finance/compound/2.31-rc1a
Add cDAIDelegate and DSR interest rate model
2 parents 0564802 + 340e479 commit bfc2914

33 files changed

+11815
-5435
lines changed

contracts/CDaiDelegate.sol

+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
pragma solidity ^0.5.12;
2+
3+
import "./CErc20Delegate.sol";
4+
5+
/**
6+
* @title Compound's CDai Contract
7+
* @notice CToken which wraps Multi-Collateral DAI
8+
* @author Compound
9+
*/
10+
contract CDaiDelegate is CErc20Delegate {
11+
/**
12+
* @notice DAI adapter address
13+
*/
14+
address public daiJoinAddress;
15+
16+
/**
17+
* @notice DAI Savings Rate (DSR) pot address
18+
*/
19+
address public potAddress;
20+
21+
/**
22+
* @notice DAI vat address
23+
*/
24+
address public vatAddress;
25+
26+
/**
27+
* @notice Delegate interface to become the implementation
28+
* @param data The encoded arguments for becoming
29+
*/
30+
function _becomeImplementation(bytes memory data) public {
31+
require(msg.sender == admin, "only the admin may initialize the implementation");
32+
33+
(address daiJoinAddress_, address potAddress_) = abi.decode(data, (address, address));
34+
return _becomeImplementation(daiJoinAddress_, potAddress_);
35+
}
36+
37+
/**
38+
* @notice Explicit interface to become the implementation
39+
* @param daiJoinAddress_ DAI adapter address
40+
* @param potAddress_ DAI Savings Rate (DSR) pot address
41+
*/
42+
function _becomeImplementation(address daiJoinAddress_, address potAddress_) internal {
43+
// Get dai and vat and sanity check the underlying
44+
DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress_);
45+
PotLike pot = PotLike(potAddress_);
46+
GemLike dai = daiJoin.dai();
47+
VatLike vat = daiJoin.vat();
48+
require(address(dai) == underlying, "DAI must be the same as underlying");
49+
50+
// Remember the relevant addresses
51+
daiJoinAddress = daiJoinAddress_;
52+
potAddress = potAddress_;
53+
vatAddress = address(vat);
54+
55+
// Approve moving our DAI into the vat through daiJoin
56+
dai.approve(daiJoinAddress, uint(-1));
57+
58+
// Approve the pot to transfer our funds within the vat
59+
vat.hope(potAddress);
60+
vat.hope(daiJoinAddress);
61+
62+
// Accumulate DSR interest -- must do this in order to doTransferIn
63+
pot.drip();
64+
65+
// Transfer all cash in (doTransferIn does this regardless of amount)
66+
doTransferIn(address(this), 0);
67+
}
68+
69+
/**
70+
* @notice Delegate interface to resign the implementation
71+
*/
72+
function _resignImplementation() public {
73+
require(msg.sender == admin, "only the admin may abandon the implementation");
74+
75+
// Transfer all cash out of the DSR - note that this relies on self-transfer
76+
DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress);
77+
PotLike pot = PotLike(potAddress);
78+
VatLike vat = VatLike(vatAddress);
79+
80+
// Accumulate interest
81+
pot.drip();
82+
83+
// Calculate the total amount in the pot, and move it out
84+
uint pie = pot.pie(address(this));
85+
pot.exit(pie);
86+
87+
// Checks the actual balance of DAI in the vat after the pot exit
88+
uint bal = vat.dai(address(this));
89+
90+
// Remove our whole balance
91+
daiJoin.exit(address(this), bal / RAY);
92+
}
93+
94+
/*** CToken Overrides ***/
95+
96+
/**
97+
* @notice Accrues DSR then applies accrued interest to total borrows and reserves
98+
* @dev This calculates interest accrued from the last checkpointed block
99+
* up to the current block and writes new checkpoint to storage.
100+
*/
101+
function accrueInterest() public returns (uint) {
102+
// Accumulate DSR interest
103+
PotLike(potAddress).drip();
104+
105+
// Accumulate CToken interest
106+
return super.accrueInterest();
107+
}
108+
109+
/*** Safe Token ***/
110+
111+
/**
112+
* @notice Gets balance of this contract in terms of the underlying
113+
* @dev This excludes the value of the current message, if any
114+
* @return The quantity of underlying tokens owned by this contract
115+
*/
116+
function getCashPrior() internal view returns (uint) {
117+
PotLike pot = PotLike(potAddress);
118+
uint pie = pot.pie(address(this));
119+
return mul(pot.chi(), pie) / RAY;
120+
}
121+
122+
/**
123+
* @notice Transfer the underlying to this contract and sweep into DSR pot
124+
* @param from Address to transfer funds from
125+
* @param amount Amount of underlying to transfer
126+
* @return The actual amount that is transferred
127+
*/
128+
function doTransferIn(address from, uint amount) internal returns (uint) {
129+
// Perform the EIP-20 transfer in
130+
EIP20Interface token = EIP20Interface(underlying);
131+
require(token.transferFrom(from, address(this), amount), "unexpected EIP-20 transfer in return");
132+
133+
DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress);
134+
GemLike dai = GemLike(underlying);
135+
PotLike pot = PotLike(potAddress);
136+
VatLike vat = VatLike(vatAddress);
137+
138+
// Convert all our DAI to internal DAI in the vat
139+
daiJoin.join(address(this), dai.balanceOf(address(this)));
140+
141+
// Checks the actual balance of DAI in the vat after the join
142+
uint bal = vat.dai(address(this));
143+
144+
// Calculate the percentage increase to th pot for the entire vat, and move it in
145+
// Note: We may leave a tiny bit of DAI in the vat...but we do the whole thing every time
146+
uint pie = bal / pot.chi();
147+
pot.join(pie);
148+
149+
return amount;
150+
}
151+
152+
/**
153+
* @notice Transfer the underlying from this contract, after sweeping out of DSR pot
154+
* @param to Address to transfer funds to
155+
* @param amount Amount of underlying to transfer
156+
*/
157+
function doTransferOut(address payable to, uint amount) internal {
158+
DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress);
159+
PotLike pot = PotLike(potAddress);
160+
161+
// Calculate the percentage decrease from the pot, and move that much out
162+
// Note: Use a slightly larger pie size to ensure that we get at least amount in the vat
163+
uint pie = add(mul(amount, RAY) / pot.chi(), 1);
164+
pot.exit(pie);
165+
166+
daiJoin.exit(to, amount);
167+
}
168+
169+
/*** Maker Internals ***/
170+
171+
uint256 constant RAY = 10 ** 27;
172+
173+
function add(uint x, uint y) internal pure returns (uint z) {
174+
require((z = x + y) >= x, "add-overflow");
175+
}
176+
177+
function mul(uint x, uint y) internal pure returns (uint z) {
178+
require(y == 0 || (z = x * y) / y == x, "mul-overflow");
179+
}
180+
}
181+
182+
/*** Maker Interfaces ***/
183+
184+
contract PotLike {
185+
function chi() public view returns (uint);
186+
function dsr() public view returns (uint);
187+
function rho() public view returns (uint);
188+
function pie(address) public view returns (uint);
189+
function drip() public returns (uint);
190+
function join(uint) public;
191+
function exit(uint) public;
192+
}
193+
194+
contract GemLike {
195+
function approve(address, uint) public;
196+
function balanceOf(address) public view returns (uint);
197+
function transfer(address, uint) public;
198+
function transferFrom(address, address, uint) public;
199+
function deposit() public payable;
200+
function withdraw(uint) public;
201+
}
202+
203+
contract VatLike {
204+
function can(address, address) public view returns (uint);
205+
function ilks(bytes32) public view returns (uint, uint, uint, uint, uint);
206+
function dai(address) public view returns (uint);
207+
function urns(bytes32, address) public view returns (uint, uint);
208+
function frob(bytes32, address, address, address, int, int) public;
209+
function hope(address) public;
210+
function move(address, address, uint) public;
211+
}
212+
213+
contract DaiJoinLike {
214+
function vat() public returns (VatLike);
215+
function dai() public returns (GemLike);
216+
function join(address, uint) public payable;
217+
function exit(address, uint) public;
218+
}

contracts/DAIInterestRateModel.sol

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
pragma solidity ^0.5.12;
2+
3+
import "./JumpRateModel.sol";
4+
import "./SafeMath.sol";
5+
6+
/**
7+
* @title Compound's DAIInterestRateModel Contract
8+
* @author Compound
9+
* @notice The parameterized model described in section 2.4 of the original Compound Protocol whitepaper
10+
*/
11+
contract DAIInterestRateModel is JumpRateModel {
12+
using SafeMath for uint;
13+
14+
/**
15+
* @notice The additional margin per block separating the base borrow rate from the roof (0.05% / block)
16+
*/
17+
uint public constant gapPerBlock = 0.05e16 / blocksPerYear;
18+
19+
/**
20+
* @notice The assumed (1 - reserve factor) used to calculate the minimum borrow rate (reserve factor = 0.05)
21+
*/
22+
uint public constant assumedOneMinusReserveFactorMantissa = 0.95e18;
23+
24+
PotLike pot;
25+
JugLike jug;
26+
27+
/**
28+
* @notice Construct an interest rate model
29+
* @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point
30+
* @param kink_ The utilization point at which the jump multiplier is applied
31+
* @param pot_ The address of the Dai pot (where DSR is earned)
32+
* @param jug_ The address of the Dai jug (where SF is kept)
33+
*/
34+
constructor(uint jumpMultiplierPerYear, uint kink_, address pot_, address jug_) JumpRateModel(0, 0, jumpMultiplierPerYear, kink_) public {
35+
pot = PotLike(pot_);
36+
jug = JugLike(jug_);
37+
poke();
38+
}
39+
40+
/**
41+
* @notice Calculates the current supply interest rate per block including the Dai savings rate
42+
* @param cash The total amount of cash the market has
43+
* @param borrows The total amount of borrows the market has outstanding
44+
* @param reserves The total amnount of reserves the market has
45+
* @param reserveFactorMantissa The current reserve factor the market has
46+
* @return The supply rate per block (as a percentage, and scaled by 1e18)
47+
*/
48+
function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) public view returns (uint) {
49+
uint protocolRate = super.getSupplyRate(cash, borrows, reserves, reserveFactorMantissa);
50+
51+
uint underlying = cash.add(borrows).sub(reserves);
52+
if (underlying == 0) {
53+
return protocolRate;
54+
} else {
55+
uint cashRate = cash.mul(dsrPerBlock()).div(underlying);
56+
return cashRate.add(protocolRate);
57+
}
58+
}
59+
60+
/**
61+
* @notice Calculates the Dai savings rate per block
62+
* @return The Dai savings rate per block (as a percentage, and scaled by 1e18)
63+
*/
64+
function dsrPerBlock() public view returns (uint) {
65+
return pot
66+
.dsr().sub(1e27) // scaled 1e27 aka RAY, and includes an extra "ONE" before subraction
67+
.div(1e9) // descale to 1e18
68+
.mul(15); // 15 seconds per block
69+
}
70+
71+
/**
72+
* @notice Resets the baseRate and multiplier per block based on the stability fee and Dai savings rate
73+
*/
74+
function poke() public {
75+
(uint duty, ) = jug.ilks("ETH-A");
76+
uint stabilityFeePerBlock = duty.add(jug.base()).sub(1e27).mul(1e18).div(1e27).mul(15);
77+
78+
// We ensure the minimum borrow rate >= DSR / (1 - reserve factor)
79+
baseRatePerBlock = dsrPerBlock().mul(1e18).div(assumedOneMinusReserveFactorMantissa);
80+
81+
// The roof borrow rate is max(base rate, stability fee) + gap, from which we derive the slope
82+
if (baseRatePerBlock < stabilityFeePerBlock) {
83+
multiplierPerBlock = stabilityFeePerBlock.sub(baseRatePerBlock).add(gapPerBlock).mul(1e18).div(kink);
84+
} else {
85+
multiplierPerBlock = gapPerBlock.mul(1e18).div(kink);
86+
}
87+
88+
emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink);
89+
}
90+
}
91+
92+
93+
/*** Maker Interfaces ***/
94+
95+
contract PotLike {
96+
function chi() public view returns (uint);
97+
function dsr() public view returns (uint);
98+
function rho() public view returns (uint);
99+
function pie(address) public view returns (uint);
100+
function drip() public returns (uint);
101+
function join(uint) public;
102+
function exit(uint) public;
103+
}
104+
105+
contract JugLike {
106+
// --- Data ---
107+
struct Ilk {
108+
uint256 duty;
109+
uint256 rho;
110+
}
111+
112+
mapping (bytes32 => Ilk) public ilks;
113+
uint256 public base;
114+
}
115+

contracts/JumpRateModel.sol

+13-14
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import "./SafeMath.sol";
1010
contract JumpRateModel is InterestRateModel {
1111
using SafeMath for uint;
1212

13-
event NewInterestParams(uint baseRatePerBlock, uint multiplierPerBlock, uint kink, uint jump);
13+
event NewInterestParams(uint baseRatePerBlock, uint multiplierPerBlock, uint jumpMultiplierPerBlock, uint kink);
1414

1515
/**
1616
* @notice Indicator that this is an InterestRateModel contract (for inspection)
@@ -33,29 +33,29 @@ contract JumpRateModel is InterestRateModel {
3333
uint public baseRatePerBlock;
3434

3535
/**
36-
* @notice the utilization point at which an additional multiplier is applied
37-
*/
38-
uint public kink;
36+
* @notice The multiplierPerBlock after hitting a specified utilization point
37+
*/
38+
uint public jumpMultiplierPerBlock;
3939

4040
/**
41-
* @notice the additional multiplier to be applied to multiplierPerBlock after hitting a specified utilization point
42-
*/
43-
uint public jump;
41+
* @notice The utilization point at which the jump multiplier is applied
42+
*/
43+
uint public kink;
4444

4545
/**
4646
* @notice Construct an interest rate model
4747
* @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18)
4848
* @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18)
49-
* @param kink_ The utilization point at which an additional multiplier is applied
50-
* @param jump_ The additional multiplier to be applied to multiplierPerBlock after hitting a specified utilization point
49+
* @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point
50+
* @param kink_ The utilization point at which the jump multiplier is applied
5151
*/
52-
constructor(uint baseRatePerYear, uint multiplierPerYear, uint kink_, uint jump_) public {
52+
constructor(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_) public {
5353
baseRatePerBlock = baseRatePerYear.div(blocksPerYear);
5454
multiplierPerBlock = multiplierPerYear.div(blocksPerYear);
55+
jumpMultiplierPerBlock = jumpMultiplierPerYear.div(blocksPerYear);
5556
kink = kink_;
56-
jump = jump_;
5757

58-
emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, kink, jump);
58+
emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink);
5959
}
6060

6161
/**
@@ -89,8 +89,7 @@ contract JumpRateModel is InterestRateModel {
8989
} else {
9090
uint normalRate = kink.mul(multiplierPerBlock).div(1e18).add(baseRatePerBlock);
9191
uint excessUtil = util.sub(kink);
92-
uint jumpMultiplier = multiplierPerBlock.mul(jump);
93-
return excessUtil.mul(jumpMultiplier).div(1e18).add(normalRate);
92+
return excessUtil.mul(jumpMultiplierPerBlock).div(1e18).add(normalRate);
9493
}
9594
}
9695

0 commit comments

Comments
 (0)