Skip to content

Commit 7ae4337

Browse files
authored
Merge pull request #122 from ampleforth/dev
Merge dev into master
2 parents 67aa791 + b03dc04 commit 7ae4337

File tree

9 files changed

+287
-169
lines changed

9 files changed

+287
-169
lines changed

.eslintrc.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ module.exports = {
3939
"passcode", "geth", "rpc", "rpcmsg","stdev", "stochasm",
4040
"whitelist", "uint", "passcodes", "keystore", "hdwallet",
4141
"formatter", "zos", "stderr", "stdout", "upgradable",
42-
"mainnet", "testnets", "npx", "testrpc",
42+
"mainnet", "testnets", "npx", "testrpc", "solc",
4343

4444
// shorthand
4545
"eth", "args", "util", "utils", "msg", "prev", "bal",
@@ -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: 71 additions & 59 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,22 +30,32 @@ contract UFragmentsPolicy is Ownable {
3030
event LogRebase(
3131
uint256 indexed epoch,
3232
uint256 exchangeRate,
33-
uint256 volume24hrs,
34-
int256 requestedSupplyAdjustment
33+
uint256 cpi,
34+
int256 requestedSupplyAdjustment,
35+
uint256 timestampSec
3536
);
3637

3738
UFragments public uFrags;
38-
IMarketOracle public marketOracle;
3939

40-
// If the current exchange rate is within this absolute distance from the target, no supply
40+
// Provides the current CPI, as an 18 decimal fixed point number.
41+
IOracle public cpiOracle;
42+
43+
// Market oracle provides the token/USD exchange rate as an 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+
// CPI value at the time of launch, as an 18 decimal fixed point number.
48+
uint256 private baseCpi;
49+
50+
// If the current exchange rate is within this fractional distance from the target, no supply
4151
// update is performed. Fixed point number--same format as the rate.
52+
// (ie) abs(rate - targetRate) / targetRate < deviationThreshold, then no supply change.
53+
// DECIMALS Fixed point number.
4254
uint256 public deviationThreshold;
4355

44-
// The 24hr market volume must be at least this value before any supply adjustments occur.
45-
uint256 public minimumVolume;
46-
4756
// The rebase lag parameter, used to dampen the applied supply adjustment by 1 / rebaseLag
4857
// Check setRebaseLag comments for more details.
58+
// Natural number, no decimal places.
4959
uint256 public rebaseLag;
5060

5161
// More than this much time must pass between rebase operations.
@@ -57,38 +67,45 @@ contract UFragmentsPolicy is Ownable {
5767
// The number of rebase cycles since inception
5868
uint256 public epoch;
5969

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);
70+
uint256 private constant DECIMALS = 18;
6571

6672
// Due to the expression in computeSupplyDelta(), MAX_RATE * MAX_SUPPLY must fit into an int256.
6773
// Both are 18 decimals fixed point numbers.
68-
uint256 private constant MAX_RATE = 10**6 * 10**RATE_DECIMALS;
74+
uint256 private constant MAX_RATE = 10**6 * 10**DECIMALS;
6975
// MAX_SUPPLY = MAX_INT256 / MAX_RATE
7076
uint256 private constant MAX_SUPPLY = ~(uint256(1) << 255) / MAX_RATE;
7177

7278
/**
7379
* @notice Anyone can call this function to initiate a new rebase operation, provided more than
7480
* the minimum time period has elapsed.
7581
* @dev The supply adjustment equals (_totalSupply * DeviationFromTargetRate) / rebaseLag
76-
* Where DeviationFromTargetRate is (MarketOracleRate - TARGET_RATE) / TARGET_RATE
82+
* Where DeviationFromTargetRate is (MarketOracleRate - targetRate) / targetRate
83+
* and targetRate is CpiOracleRate / baseCpi
7784
*/
7885
function rebase() external {
7986
// This comparison also ensures there is no reentrancy.
8087
require(lastRebaseTimestampSec.add(minRebaseTimeIntervalSec) < now);
8188
lastRebaseTimestampSec = now;
8289
epoch = epoch.add(1);
8390

91+
uint256 cpi;
92+
bool cpiValid;
93+
(cpi, cpiValid) = cpiOracle.getData();
94+
require(cpiValid);
95+
96+
uint256 targetRate = cpi.mul(10 ** DECIMALS).div(baseCpi);
97+
8498
uint256 exchangeRate;
85-
uint256 volume;
86-
(exchangeRate, volume) = marketOracle.getPriceAnd24HourVolume();
99+
bool rateValid;
100+
(exchangeRate, rateValid) = marketOracle.getData();
101+
require(rateValid);
102+
87103
if (exchangeRate > MAX_RATE) {
88104
exchangeRate = MAX_RATE;
89105
}
90106

91-
int256 supplyDelta = computeSupplyDelta(exchangeRate, volume);
107+
int256 supplyDelta = computeSupplyDelta(exchangeRate, targetRate);
108+
92109
// Apply the Dampening factor.
93110
supplyDelta = supplyDelta.div(rebaseLag.toInt256Safe());
94111

@@ -98,43 +115,42 @@ contract UFragmentsPolicy is Ownable {
98115

99116
uint256 supplyAfterRebase = uFrags.rebase(epoch, supplyDelta);
100117
assert(supplyAfterRebase <= MAX_SUPPLY);
101-
emit LogRebase(epoch, exchangeRate, volume, supplyDelta);
118+
emit LogRebase(epoch, exchangeRate, cpi, supplyDelta, lastRebaseTimestampSec);
102119
}
103120

104121
/**
105-
* @notice Sets the reference to the market oracle.
106-
* @param marketOracle_ The address of the market oracle contract.
122+
* @notice Sets the reference to the CPI oracle.
123+
* @param cpiOracle_ The address of the cpi oracle contract.
107124
*/
108-
function setMarketOracle(IMarketOracle marketOracle_)
125+
function setCpiOracle(IOracle cpiOracle_)
109126
external
110127
onlyOwner
111128
{
112-
marketOracle = marketOracle_;
129+
cpiOracle = cpiOracle_;
113130
}
114131

115132
/**
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.
133+
* @notice Sets the reference to the market oracle.
134+
* @param marketOracle_ The address of the market oracle contract.
120135
*/
121-
function setDeviationThreshold(uint256 deviationThreshold_)
136+
function setMarketOracle(IOracle marketOracle_)
122137
external
123138
onlyOwner
124139
{
125-
deviationThreshold = deviationThreshold_;
140+
marketOracle = marketOracle_;
126141
}
127142

128143
/**
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.
144+
* @notice Sets the deviation threshold fraction. If the exchange rate given by the market
145+
* oracle is within this fractional distance from the targetRate, then no supply
146+
* modifications are made. DECIMALS fixed point number.
147+
* @param deviationThreshold_ The new exchange rate threshold fraction.
132148
*/
133-
function setMinimumVolume(uint256 minimumVolume_)
149+
function setDeviationThreshold(uint256 deviationThreshold_)
134150
external
135151
onlyOwner
136152
{
137-
minimumVolume = minimumVolume_;
153+
deviationThreshold = deviationThreshold_;
138154
}
139155

140156
/**
@@ -171,63 +187,59 @@ contract UFragmentsPolicy is Ownable {
171187
* It is called at the time of contract creation to invoke parent class initializers and
172188
* initialize the contract's state variables.
173189
*/
174-
function initialize(address owner, UFragments uFrags_)
190+
function initialize(address owner, UFragments uFrags_, uint256 baseCpi_)
175191
public
176192
initializer
177193
{
178194
Ownable.initialize(owner);
179195

180-
deviationThreshold = (5 * TARGET_RATE) / 100; // 5% of target
181-
minimumVolume = 1;
196+
// deviationThreshold = 0.05e18 = 5e16
197+
deviationThreshold = 5 * 10 ** (DECIMALS-2);
198+
182199
rebaseLag = 30;
183200
minRebaseTimeIntervalSec = 1 days;
184201
lastRebaseTimestampSec = 0;
185202
epoch = 0;
186203

187204
uFrags = uFrags_;
205+
baseCpi = baseCpi_;
188206
}
189207

190208
/**
191-
* @return Computes the total supply adjustment in response to the exchange rate.
209+
* @return Computes the total supply adjustment in response to the exchange rate
210+
* and the targetRate.
192211
*/
193-
function computeSupplyDelta(uint256 rate, uint256 volume)
212+
function computeSupplyDelta(uint256 rate, uint256 targetRate)
194213
private
195214
view
196215
returns (int256)
197216
{
198-
if (withinDeviationThreshold(rate) || !enoughVolume(volume)) {
217+
if (withinDeviationThreshold(rate, targetRate)) {
199218
return 0;
200219
}
201220

202-
// (totalSupply * (rate - target)) / target
203-
return uFrags.totalSupply().toInt256Safe().mul(
204-
rate.toInt256Safe().sub(TARGET_RATE_SIGNED)
205-
).div(TARGET_RATE_SIGNED);
221+
// supplyDelta = totalSupply * (rate - targetRate) / targetRate
222+
int256 targetRateSigned = targetRate.toInt256Safe();
223+
return uFrags.totalSupply().toInt256Safe()
224+
.mul(rate.toInt256Safe().sub(targetRateSigned))
225+
.div(targetRateSigned);
206226
}
207227

208228
/**
209229
* @param rate The current exchange rate, an 18 decimal fixed point number.
230+
* @param targetRate The target exchange rate, an 18 decimal fixed point number.
210231
* @return If the rate is within the deviation threshold from the target rate, returns true.
211232
* Otherwise, returns false.
212233
*/
213-
function withinDeviationThreshold(uint256 rate)
234+
function withinDeviationThreshold(uint256 rate, uint256 targetRate)
214235
private
215236
view
216237
returns (bool)
217238
{
218-
return (rate >= TARGET_RATE && rate.sub(TARGET_RATE) < deviationThreshold)
219-
|| (rate < TARGET_RATE && TARGET_RATE.sub(rate) < deviationThreshold);
220-
}
239+
uint256 absoluteDeviationThreshold = targetRate.mul(deviationThreshold)
240+
.div(10 ** DECIMALS);
221241

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;
242+
return (rate >= targetRate && rate.sub(targetRate) < absoluteDeviationThreshold)
243+
|| (rate < targetRate && targetRate.sub(rate) < absoluteDeviationThreshold);
232244
}
233245
}

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/MockMarketOracle.sol

Lines changed: 0 additions & 34 deletions
This file was deleted.

contracts/mocks/MockOracle.sol

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
pragma solidity 0.4.24;
2+
3+
import "./Mock.sol";
4+
5+
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+
}
14+
15+
// Mock methods
16+
function getData()
17+
external
18+
returns (uint256, bool)
19+
{
20+
emit FunctionCalled(name, "getData", msg.sender);
21+
uint256[] memory uintVals = new uint256[](0);
22+
int256[] memory intVals = new int256[](0);
23+
emit FunctionArguments(uintVals, intVals);
24+
return (_data, _validity);
25+
}
26+
27+
// Methods to mock data on the chain
28+
function storeData(uint256 data)
29+
public
30+
{
31+
_data = data;
32+
}
33+
34+
function storeValidity(bool validity)
35+
public
36+
{
37+
_validity = validity;
38+
}
39+
}

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)