Skip to content

Commit ca56426

Browse files
committed
target rate update
1 parent 113254b commit ca56426

File tree

5 files changed

+239
-115
lines changed

5 files changed

+239
-115
lines changed

contracts/MedianOracle.sol

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
pragma solidity 0.8.4;
33

44
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
5+
import {MathUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
56
import "./lib/Select.sol";
67

78
interface IOracle {
@@ -15,6 +16,8 @@ interface IOracle {
1516
* providers.
1617
*/
1718
contract MedianOracle is OwnableUpgradeable, IOracle {
19+
using MathUpgradeable for uint256;
20+
1821
struct Report {
1922
uint256 timestamp;
2023
uint256 payload;
@@ -47,6 +50,9 @@ contract MedianOracle is OwnableUpgradeable, IOracle {
4750
// This is needed so that timestamp of 1 is always considered expired.
4851
uint256 private constant MAX_REPORT_EXPIRATION_TIME = 520 weeks;
4952

53+
// The final mediam value is scaled by dividing by this factor.
54+
uint256 public scalar;
55+
5056
/**
5157
* @notice Contract state initialization.
5258
*
@@ -67,6 +73,7 @@ contract MedianOracle is OwnableUpgradeable, IOracle {
6773
reportExpirationTimeSec = reportExpirationTimeSec_;
6874
reportDelaySec = reportDelaySec_;
6975
minimumProviders = minimumProviders_;
76+
scalar = 10**18;
7077
__Ownable_init();
7178
}
7279

@@ -99,6 +106,14 @@ contract MedianOracle is OwnableUpgradeable, IOracle {
99106
minimumProviders = minimumProviders_;
100107
}
101108

109+
/**
110+
* @notice Sets the scaling factor.
111+
* @param scalar_ The scaling factor.
112+
*/
113+
function setScalar(uint256 scalar_) external onlyOwner {
114+
scalar = scalar_;
115+
}
116+
102117
/**
103118
* @notice Pushes a report for the calling provider.
104119
* @param payload is expected to be 18 decimal fixed point number.
@@ -182,7 +197,7 @@ contract MedianOracle is OwnableUpgradeable, IOracle {
182197
return (0, false);
183198
}
184199

185-
return (Select.computeMedian(validReports, size), true);
200+
return (Select.computeMedian(validReports, size).mulDiv(10**18, scalar), true);
186201
}
187202

188203
/**

contracts/UFragmentsPolicy.sol

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ contract UFragmentsPolicy is Ownable {
2929
using SafeMathInt for int256;
3030
using UInt256Lib for uint256;
3131

32+
/// @notice DEPRECATED.
3233
event LogRebase(
3334
uint256 indexed epoch,
3435
uint256 exchangeRate,
@@ -37,16 +38,27 @@ contract UFragmentsPolicy is Ownable {
3738
uint256 timestampSec
3839
);
3940

41+
event Rebase(
42+
uint256 indexed epoch,
43+
uint256 currentRate,
44+
uint256 targetRate,
45+
int256 requestedSupplyAdjustment,
46+
uint256 timestampSec
47+
);
48+
4049
IUFragments public uFrags;
4150

42-
// Provides the current CPI, as an 18 decimal fixed point number.
51+
// Provides the cpi adjusted price target, as an 18 decimal fixed point number.
4352
IOracle public cpiOracle;
4453

4554
// Market oracle provides the token/USD exchange rate as an 18 decimal fixed point number.
4655
// (eg) An oracle value of 1.5e18 it would mean 1 Ample is trading for $1.50.
4756
IOracle public marketOracle;
4857

49-
// CPI value at the time of launch, as an 18 decimal fixed point number.
58+
// @notice DEPRECATED.
59+
// @dev This variable is NOT being used anymore.
60+
// This used to store the CPI value at the time of launch to scale the incoming target
61+
// and infer the price target. However, now the update CPI oracle returns the price target.
5062
uint256 private baseCpi;
5163

5264
// If the current exchange rate is within this fractional distance from the target, no supply
@@ -116,31 +128,29 @@ contract UFragmentsPolicy is Ownable {
116128

117129
epoch = epoch.add(1);
118130

119-
uint256 cpi;
120-
bool cpiValid;
121-
(cpi, cpiValid) = cpiOracle.getData();
122-
require(cpiValid);
123-
124-
uint256 targetRate = cpi.mul(10**DECIMALS).div(baseCpi);
131+
uint256 targetRate;
132+
bool targetRateValid;
133+
(targetRate, targetRateValid) = cpiOracle.getData();
134+
require(targetRateValid);
125135

126-
uint256 exchangeRate;
127-
bool rateValid;
128-
(exchangeRate, rateValid) = marketOracle.getData();
129-
require(rateValid);
136+
uint256 currentRate;
137+
bool currentRateValid;
138+
(currentRate, currentRateValid) = marketOracle.getData();
139+
require(currentRateValid);
130140

131-
if (exchangeRate > MAX_RATE) {
132-
exchangeRate = MAX_RATE;
141+
if (currentRate > MAX_RATE) {
142+
currentRate = MAX_RATE;
133143
}
134144

135-
int256 supplyDelta = computeSupplyDelta(exchangeRate, targetRate);
145+
int256 supplyDelta = computeSupplyDelta(currentRate, targetRate);
136146

137147
if (supplyDelta > 0 && uFrags.totalSupply().add(uint256(supplyDelta)) > MAX_SUPPLY) {
138148
supplyDelta = (MAX_SUPPLY.sub(uFrags.totalSupply())).toInt256Safe();
139149
}
140150

141151
uint256 supplyAfterRebase = uFrags.rebase(epoch, supplyDelta);
142152
assert(supplyAfterRebase <= MAX_SUPPLY);
143-
emit LogRebase(epoch, exchangeRate, cpi, supplyDelta, block.timestamp);
153+
emit Rebase(epoch, currentRate, targetRate, supplyDelta, block.timestamp);
144154
}
145155

146156
/**
@@ -241,11 +251,7 @@ contract UFragmentsPolicy is Ownable {
241251
* It is called at the time of contract creation to invoke parent class initializers and
242252
* initialize the contract's state variables.
243253
*/
244-
function initialize(
245-
address owner_,
246-
IUFragments uFrags_,
247-
uint256 baseCpi_
248-
) public initializer {
254+
function initialize(address owner_, IUFragments uFrags_) public initializer {
249255
Ownable.initialize(owner_);
250256

251257
// deviationThreshold = 0.05e18 = 5e16
@@ -263,7 +269,6 @@ contract UFragmentsPolicy is Ownable {
263269
epoch = 0;
264270

265271
uFrags = uFrags_;
266-
baseCpi = baseCpi_;
267272
}
268273

269274
/**

scripts/deploy.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,7 @@ task('deploy:amplforce:testnet', 'Deploy ampleforth contract suite for testnet')
6464
console.log('Implementation:', amplImpl)
6565

6666
// deploy market oracle
67-
const marketOracle = await deployContract(
68-
hre,
69-
'MedianOracle',
70-
deployer,
71-
)
67+
const marketOracle = await deployContract(hre, 'MedianOracle', deployer)
7268
await marketOracle.init(
7369
RATE_REPORT_EXPIRATION_SEC,
7470
RATE_REPORT_DELAY_SEC,
@@ -77,11 +73,7 @@ task('deploy:amplforce:testnet', 'Deploy ampleforth contract suite for testnet')
7773
console.log('Market oracle to:', marketOracle.address)
7874

7975
// deploy cpi oracle
80-
const cpiOracle = await deployContract(
81-
hre,
82-
'MedianOracle',
83-
deployer,
84-
)
76+
const cpiOracle = await deployContract(hre, 'MedianOracle', deployer)
8577
await cpiOracle.init(
8678
CPI_REPORT_EXPIRATION_SEC,
8779
CPI_REPORT_DELAY_SEC,

test/unit/MedianOracle.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,21 @@ describe('MedianOracle:removeProvider:accessControl', async function () {
218218
})
219219
})
220220

221+
describe('MedianOracle:setScalar:accessControl', async function () {
222+
beforeEach(async function () {
223+
await setupContractsAndAccounts()
224+
})
225+
226+
it('should be callable by owner', async function () {
227+
await oracle.setScalar(ethers.utils.parseUnits('1', 18))
228+
})
229+
230+
it('should NOT be callable by non-owner', async function () {
231+
await expect(oracle.connect(A).setScalar(ethers.utils.parseUnits('1', 18)))
232+
.to.be.reverted
233+
})
234+
})
235+
221236
describe('MedianOracle:getData', async function () {
222237
before(async function () {
223238
await setupContractsAndAccounts()
@@ -498,6 +513,35 @@ describe('MedianOracle:getData', async function () {
498513
})
499514
})
500515

516+
describe('MedianOracle:getData', async function () {
517+
before(async function () {
518+
await setupContractsAndAccounts()
519+
await setupCallerContract()
520+
521+
await oracle.setScalar(BigNumber.from('900000000000000000'))
522+
523+
await oracle.addProvider(await A.getAddress())
524+
await oracle.addProvider(await B.getAddress())
525+
await oracle.addProvider(await C.getAddress())
526+
await oracle.addProvider(await D.getAddress())
527+
528+
await oracle.connect(D).pushReport(BigNumber.from('1000000000000000000'))
529+
await oracle.connect(B).pushReport(BigNumber.from('1041000000000000000'))
530+
await oracle.connect(A).pushReport(BigNumber.from('1053200000000000000'))
531+
await oracle.connect(C).pushReport(BigNumber.from('2041000000000000000'))
532+
533+
await increaseTime(40)
534+
})
535+
536+
describe('when a scalar is set', function () {
537+
it('should scale the median', async function () {
538+
await expect(callerContract.getData())
539+
.to.emit(callerContract, 'ReturnValueUInt256Bool')
540+
.withArgs(BigNumber.from('1163444444444444444'), true)
541+
})
542+
})
543+
})
544+
501545
describe('MedianOracle:PurgeReports', async function () {
502546
before(async function () {
503547
await setupContractsAndAccounts()

0 commit comments

Comments
 (0)