Skip to content

Commit 7921e87

Browse files
Nithin Krishnaaalavandhan
authored andcommitted
Calculating a new target rate with every rebase using data from CPI Oracle (#109)
- The policy now has no notion of volume. - Using a cpi Oracle to compute the new `targetRate` on every rebase.
1 parent 1173bf4 commit 7921e87

File tree

6 files changed

+252
-138
lines changed

6 files changed

+252
-138
lines changed

.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ module.exports = {
5050
// project-specific
5151
"rebase", "gons", "frg", "rng", "blockchain", "minlot",
5252
"redemptions", "rebased", "ganache", "ethclient",
53-
"bytecode", "Binance", "ethereum", "opcode",
53+
"bytecode", "Binance", "ethereum", "opcode", "cpi",
5454

5555
// names
5656
"nithin",

contracts/UFragmentsPolicy.sol

Lines changed: 66 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import "./lib/UInt256Lib.sol";
88
import "./UFragments.sol";
99

1010

11-
interface IMarketOracle {
12-
function getPriceAnd24HourVolume() external returns (uint256, uint256);
11+
interface IOracle {
12+
function getData() external returns (uint256, bool);
1313
}
1414

1515

@@ -30,20 +30,25 @@ contract UFragmentsPolicy is Ownable {
3030
event LogRebase(
3131
uint256 indexed epoch,
3232
uint256 exchangeRate,
33-
uint256 volume24hrs,
33+
uint256 cpi,
3434
int256 requestedSupplyAdjustment
3535
);
3636

3737
UFragments public uFrags;
38-
IMarketOracle public marketOracle;
3938

40-
// If the current exchange rate is within this absolute distance from the target, no supply
39+
// Cpi oracle provides the current CPI-U, seasonally adjusted data,
40+
// as a 18 decimal fixed point number
41+
IOracle public cpiOracle;
42+
43+
// Market oracle provides the token/USD exchange rate as a 18 decimal fixed point number
44+
// (eg) An oracle value of 1.5e18 it would mean 1 Ample is trading for $1.50.
45+
IOracle public marketOracle;
46+
47+
// If the current exchange rate is within this fractional distance from the target, no supply
4148
// update is performed. Fixed point number--same format as the rate.
49+
// (ie) abs(rate - targetRate) / targetRate < deviationThreshold, then no supply change.
4250
uint256 public deviationThreshold;
4351

44-
// The 24hr market volume must be at least this value before any supply adjustments occur.
45-
uint256 public minimumVolume;
46-
4752
// The rebase lag parameter, used to dampen the applied supply adjustment by 1 / rebaseLag
4853
// Check setRebaseLag comments for more details.
4954
uint256 public rebaseLag;
@@ -57,38 +62,48 @@ contract UFragmentsPolicy is Ownable {
5762
// The number of rebase cycles since inception
5863
uint256 public epoch;
5964

60-
uint256 private constant RATE_DECIMALS = 18;
61-
62-
uint256 private constant TARGET_RATE = 1 * 10**RATE_DECIMALS;
63-
64-
int256 private constant TARGET_RATE_SIGNED = int256(TARGET_RATE);
65+
uint256 private constant DECIMALS = 18;
6566

6667
// Due to the expression in computeSupplyDelta(), MAX_RATE * MAX_SUPPLY must fit into an int256.
6768
// Both are 18 decimals fixed point numbers.
68-
uint256 private constant MAX_RATE = 10**6 * 10**RATE_DECIMALS;
69+
uint256 private constant MAX_RATE = 10**6 * 10**DECIMALS;
6970
// MAX_SUPPLY = MAX_INT256 / MAX_RATE
7071
uint256 private constant MAX_SUPPLY = ~(uint256(1) << 255) / MAX_RATE;
7172

73+
// CPI-U value July 10th 1983 100, DECIMALS Fixed point number
74+
uint256 private constant BASE_CPI = 100 * (10**DECIMALS);
75+
7276
/**
7377
* @notice Anyone can call this function to initiate a new rebase operation, provided more than
7478
* the minimum time period has elapsed.
7579
* @dev The supply adjustment equals (_totalSupply * DeviationFromTargetRate) / rebaseLag
76-
* Where DeviationFromTargetRate is (MarketOracleRate - TARGET_RATE) / TARGET_RATE
80+
* Where DeviationFromTargetRate is (MarketOracleRate - targetRate) / targetRate
81+
* and targetRate is CpiOracleRate / BASE_CPI
7782
*/
7883
function rebase() external {
7984
// This comparison also ensures there is no reentrancy.
8085
require(lastRebaseTimestampSec.add(minRebaseTimeIntervalSec) < now);
8186
lastRebaseTimestampSec = now;
8287
epoch = epoch.add(1);
8388

89+
uint256 cpi;
90+
bool cpiValid;
91+
(cpi, cpiValid) = cpiOracle.getData();
92+
require(cpiValid);
93+
94+
uint256 targetRate = cpi.mul(10 ** DECIMALS).div(BASE_CPI);
95+
8496
uint256 exchangeRate;
85-
uint256 volume;
86-
(exchangeRate, volume) = marketOracle.getPriceAnd24HourVolume();
97+
bool rateValid;
98+
(exchangeRate, rateValid) = marketOracle.getData();
99+
require(rateValid);
100+
87101
if (exchangeRate > MAX_RATE) {
88102
exchangeRate = MAX_RATE;
89103
}
90104

91-
int256 supplyDelta = computeSupplyDelta(exchangeRate, volume);
105+
int256 supplyDelta = computeSupplyDelta(exchangeRate, targetRate);
106+
92107
// Apply the Dampening factor.
93108
supplyDelta = supplyDelta.div(rebaseLag.toInt256Safe());
94109

@@ -98,43 +113,42 @@ contract UFragmentsPolicy is Ownable {
98113

99114
uint256 supplyAfterRebase = uFrags.rebase(epoch, supplyDelta);
100115
assert(supplyAfterRebase <= MAX_SUPPLY);
101-
emit LogRebase(epoch, exchangeRate, volume, supplyDelta);
116+
emit LogRebase(epoch, exchangeRate, cpi, supplyDelta);
102117
}
103118

104119
/**
105-
* @notice Sets the reference to the market oracle.
106-
* @param marketOracle_ The address of the market oracle contract.
120+
* @notice Sets the reference to the CPI oracle.
121+
* @param cpiOracle_ The address of the cpi oracle contract.
107122
*/
108-
function setMarketOracle(IMarketOracle marketOracle_)
123+
function setCpiOracle(IOracle cpiOracle_)
109124
external
110125
onlyOwner
111126
{
112-
marketOracle = marketOracle_;
127+
cpiOracle = cpiOracle_;
113128
}
114129

115130
/**
116-
* @notice Sets the deviation threshold. If the exchange rate given by the market
117-
* oracle is within this absolute distance from the target, then no supply
118-
* modifications are made. RATE_DECIMALS fixed point number.
119-
* @param deviationThreshold_ The new exchange rate threshold.
131+
* @notice Sets the reference to the market oracle.
132+
* @param marketOracle_ The address of the market oracle contract.
120133
*/
121-
function setDeviationThreshold(uint256 deviationThreshold_)
134+
function setMarketOracle(IOracle marketOracle_)
122135
external
123136
onlyOwner
124137
{
125-
deviationThreshold = deviationThreshold_;
138+
marketOracle = marketOracle_;
126139
}
127140

128141
/**
129-
* @notice Sets the minimum volume. During rebase, the volume must be at least this value before
130-
* any supply adjustment is made.
131-
* @param minimumVolume_ The new minimum volume, measured in 24hr market Token Volume.
142+
* @notice Sets the deviation threshold fraction. If the exchange rate given by the market
143+
* oracle is within this fractional distance from the targetRate, then no supply
144+
* modifications are made. DECIMALS fixed point number.
145+
* @param deviationThreshold_ The new exchange rate threshold fraction.
132146
*/
133-
function setMinimumVolume(uint256 minimumVolume_)
147+
function setDeviationThreshold(uint256 deviationThreshold_)
134148
external
135149
onlyOwner
136150
{
137-
minimumVolume = minimumVolume_;
151+
deviationThreshold = deviationThreshold_;
138152
}
139153

140154
/**
@@ -177,8 +191,9 @@ contract UFragmentsPolicy is Ownable {
177191
{
178192
Ownable.initialize(owner);
179193

180-
deviationThreshold = (5 * TARGET_RATE) / 100; // 5% of target
181-
minimumVolume = 1;
194+
// deviationThreshold = 0.05e18 = 5e16
195+
deviationThreshold = 5 * 10 ** (DECIMALS-2);
196+
182197
rebaseLag = 30;
183198
minRebaseTimeIntervalSec = 1 days;
184199
lastRebaseTimestampSec = 0;
@@ -188,46 +203,40 @@ contract UFragmentsPolicy is Ownable {
188203
}
189204

190205
/**
191-
* @return Computes the total supply adjustment in response to the exchange rate.
206+
* @return Computes the total supply adjustment in response to the exchange rate
207+
* and the targetRate.
192208
*/
193-
function computeSupplyDelta(uint256 rate, uint256 volume)
209+
function computeSupplyDelta(uint256 rate, uint256 targetRate)
194210
private
195211
view
196212
returns (int256)
197213
{
198-
if (withinDeviationThreshold(rate) || !enoughVolume(volume)) {
214+
if (withinDeviationThreshold(rate, targetRate)) {
199215
return 0;
200216
}
201217

202-
// (totalSupply * (rate - target)) / target
203-
return uFrags.totalSupply().toInt256Safe().mul(
204-
rate.toInt256Safe().sub(TARGET_RATE_SIGNED)
205-
).div(TARGET_RATE_SIGNED);
218+
// supplyDelta = totalSupply * (rate - targetRate) / targetRate
219+
int256 targetRateSigned = targetRate.toInt256Safe();
220+
return uFrags.totalSupply().toInt256Safe()
221+
.mul(rate.toInt256Safe().sub(targetRateSigned))
222+
.div(targetRateSigned);
206223
}
207224

208225
/**
209226
* @param rate The current exchange rate, an 18 decimal fixed point number.
227+
* @param targetRate The target exchange rate, an 18 decimal fixed point number.
210228
* @return If the rate is within the deviation threshold from the target rate, returns true.
211229
* Otherwise, returns false.
212230
*/
213-
function withinDeviationThreshold(uint256 rate)
231+
function withinDeviationThreshold(uint256 rate, uint256 targetRate)
214232
private
215233
view
216234
returns (bool)
217235
{
218-
return (rate >= TARGET_RATE && rate.sub(TARGET_RATE) < deviationThreshold)
219-
|| (rate < TARGET_RATE && TARGET_RATE.sub(rate) < deviationThreshold);
220-
}
236+
uint256 absoluteDeviationThreshold = targetRate.mul(deviationThreshold)
237+
.div(10 ** DECIMALS);
221238

222-
/**
223-
* @param volume Total trade volume of the last reported 24 hours in Token volume.
224-
* return True, if the volume meets requirements for a supply adjustment. False otherwise.
225-
*/
226-
function enoughVolume(uint256 volume)
227-
private
228-
view
229-
returns (bool)
230-
{
231-
return volume >= minimumVolume;
239+
return (rate >= targetRate && rate.sub(targetRate) < absoluteDeviationThreshold)
240+
|| (rate < targetRate && targetRate.sub(rate) < absoluteDeviationThreshold);
232241
}
233242
}

contracts/mocks/Mock.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ pragma solidity 0.4.24;
22

33

44
contract Mock {
5-
event FunctionCalled(string functionName, address caller);
5+
event FunctionCalled(string instanceName, string functionName, address caller);
66
event FunctionArguments(uint256[] uintVals, int256[] intVals);
77
event ReturnValueInt256(int256 val);
88
event ReturnValueUInt256(uint256 val);

contracts/mocks/MockOracle.sol

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,37 @@ pragma solidity 0.4.24;
33
import "./Mock.sol";
44

55

6-
contract MockMarketOracle is Mock {
7-
uint128 private _exchangeRate;
8-
uint128 private _volume;
6+
contract MockOracle is Mock {
7+
bool private _validity = true;
8+
uint256 private _data;
9+
string public name;
10+
11+
constructor(string name_) public {
12+
name = name_;
13+
}
914

1015
// Mock methods
11-
function getPriceAnd24HourVolume()
16+
function getData()
1217
external
13-
returns (uint128, uint128)
18+
returns (uint256, bool)
1419
{
15-
emit FunctionCalled("MarketOracle:getPriceAnd24HourVolume", msg.sender);
20+
emit FunctionCalled(name, "getData", msg.sender);
1621
uint256[] memory uintVals = new uint256[](0);
1722
int256[] memory intVals = new int256[](0);
1823
emit FunctionArguments(uintVals, intVals);
19-
return (_exchangeRate, _volume);
24+
return (_data, _validity);
2025
}
2126

2227
// Methods to mock data on the chain
23-
function storeRate(uint128 exchangeRate)
28+
function storeData(uint256 data)
2429
public
2530
{
26-
_exchangeRate = exchangeRate;
31+
_data = data;
2732
}
2833

29-
function storeVolume(uint128 volume)
34+
function storeValidity(bool validity)
3035
public
3136
{
32-
_volume = volume;
37+
_validity = validity;
3338
}
3439
}

contracts/mocks/MockUFragments.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ contract MockUFragments is Mock {
1818
public
1919
returns (uint256)
2020
{
21-
emit FunctionCalled("UFragments:rebase", msg.sender);
21+
emit FunctionCalled("UFragments", "rebase", msg.sender);
2222
uint256[] memory uintVals = new uint256[](1);
2323
uintVals[0] = epoch;
2424
int256[] memory intVals = new int256[](1);

0 commit comments

Comments
 (0)