Skip to content

Commit 7f3c17c

Browse files
authored
Merge pull request #34 from backed-fi/multiplier-update-nonce
feat: multiplier nonce.
2 parents f538c9a + 05e12bb commit 7f3c17c

File tree

2 files changed

+94
-22
lines changed

2 files changed

+94
-22
lines changed

contracts/BackedAutoFeeTokenImplementation.sol

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
7777

7878
uint256 internal _totalShares;
7979

80+
uint256 public multiplierNonce;
81+
8082
// Events:
8183

8284
/**
@@ -104,13 +106,37 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
104106
// Modifiers:
105107

106108
modifier updateMultiplier() {
107-
(uint256 newMultiplier, uint256 periodsPassed) = getCurrentMultiplier();
109+
(uint256 newMultiplier, uint256 periodsPassed, uint256 newMultiplierNonce) = getCurrentMultiplier();
108110
lastTimeFeeApplied = lastTimeFeeApplied + periodLength * periodsPassed;
109111
if (multiplier != newMultiplier) {
110-
_updateMultiplier(newMultiplier);
112+
_updateMultiplier(newMultiplier, newMultiplierNonce);
111113
}
112114
_;
113115
}
116+
117+
modifier onlyMultiplierUpdater() {
118+
require(
119+
_msgSender() == multiplierUpdater,
120+
"BackedToken: Only multiplier updater"
121+
);
122+
_;
123+
}
124+
125+
modifier onlyUpdatedMultiplier(uint256 oldMultiplier) {
126+
require(
127+
multiplier == oldMultiplier,
128+
"BackedToken: Multiplier changed in the meantime"
129+
);
130+
_;
131+
}
132+
133+
modifier onlyNewerMultiplierNonce(uint256 newMultiplierNonce) {
134+
require(
135+
multiplierNonce < newMultiplierNonce,
136+
"BackedToken: Multiplier nonce is outdated."
137+
);
138+
_;
139+
}
114140

115141
// constructor, set lastTimeFeeApplied to lock the implementation instance.
116142
constructor () {
@@ -155,6 +181,7 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
155181
require(_lastTimeFeeApplied != 0, "Invalid last time fee applied");
156182

157183
multiplier = 1e18;
184+
multiplierNonce = 0;
158185
periodLength = _periodLength;
159186
lastTimeFeeApplied = _lastTimeFeeApplied;
160187
feePerPeriod = _feePerPeriod;
@@ -164,7 +191,7 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
164191
* @dev See {IERC20-totalSupply}.
165192
*/
166193
function totalSupply() public view virtual override returns (uint256) {
167-
(uint256 newMultiplier, ) = getCurrentMultiplier();
194+
(uint256 newMultiplier, ,) = getCurrentMultiplier();
168195
return _getUnderlyingAmountByShares(_totalShares, newMultiplier);
169196
}
170197

@@ -174,7 +201,7 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
174201
function balanceOf(
175202
address account
176203
) public view virtual override returns (uint256) {
177-
(uint256 newMultiplier, ) = getCurrentMultiplier();
204+
(uint256 newMultiplier, ,) = getCurrentMultiplier();
178205
return _getUnderlyingAmountByShares(sharesOf(account), newMultiplier);
179206
}
180207

@@ -186,14 +213,16 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
186213
public
187214
view
188215
virtual
189-
returns (uint256 newMultiplier, uint256 periodsPassed)
216+
returns (uint256 newMultiplier, uint256 periodsPassed, uint256 newMultiplierNonce)
190217
{
191218
periodsPassed = (block.timestamp - lastTimeFeeApplied) / periodLength;
192219
newMultiplier = multiplier;
220+
newMultiplierNonce = multiplierNonce;
193221
if (feePerPeriod > 0) {
194222
for (uint256 index = 0; index < periodsPassed; index++) {
195223
newMultiplier = (newMultiplier * (1e18 - feePerPeriod)) / 1e18;
196224
}
225+
newMultiplierNonce += periodsPassed;
197226
}
198227
}
199228

@@ -210,7 +239,7 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
210239
function getSharesByUnderlyingAmount(
211240
uint256 _underlyingAmount
212241
) external view returns (uint256) {
213-
(uint256 newMultiplier, ) = getCurrentMultiplier();
242+
(uint256 newMultiplier, ,) = getCurrentMultiplier();
214243
return _getSharesByUnderlyingAmount(_underlyingAmount, newMultiplier);
215244
}
216245

@@ -220,7 +249,7 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
220249
function getUnderlyingAmountByShares(
221250
uint256 _sharesAmount
222251
) external view returns (uint256) {
223-
(uint256 newMultiplier, ) = getCurrentMultiplier();
252+
(uint256 newMultiplier, ,) = getCurrentMultiplier();
224253
return _getUnderlyingAmountByShares(_sharesAmount, newMultiplier);
225254
}
226255

@@ -319,7 +348,7 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
319348
}
320349

321350
/**
322-
* @dev Function to change the contract multiplier, only if oldMultiplier did not change in the meantime. Allowed only for owner
351+
* @dev Function to change the contract multiplier, only if oldMultiplier did not change in the meantime. Allowed only for multiplierUpdater
323352
*
324353
* Emits a { MultiplierChanged } event
325354
*
@@ -328,16 +357,24 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
328357
function updateMultiplierValue(
329358
uint256 newMultiplier,
330359
uint256 oldMultiplier
331-
) external updateMultiplier {
332-
require(
333-
_msgSender() == multiplierUpdater,
334-
"BackedToken: Only multiplier updater"
335-
);
336-
require(
337-
multiplier == oldMultiplier,
338-
"BackedToken: Multiplier changed in the meantime"
339-
);
340-
_updateMultiplier(newMultiplier);
360+
) public onlyMultiplierUpdater updateMultiplier onlyUpdatedMultiplier(oldMultiplier) {
361+
_updateMultiplier(newMultiplier, multiplierNonce + 1);
362+
}
363+
364+
/**
365+
* @dev Function to change the contract multiplier with nonce, only if oldMultiplier did not change in the meantime. Allowed only for multiplierUpdater
366+
*
367+
* Emits a { MultiplierChanged } event
368+
*
369+
* @param newMultiplier New multiplier value
370+
* @param newMultiplierNonce New multplier nonce
371+
*/
372+
function updateMultiplierWithNonce(
373+
uint256 newMultiplier,
374+
uint256 oldMultiplier,
375+
uint256 newMultiplierNonce
376+
) external onlyMultiplierUpdater updateMultiplier onlyUpdatedMultiplier(oldMultiplier) onlyNewerMultiplierNonce(newMultiplierNonce){
377+
_updateMultiplier(newMultiplier, newMultiplierNonce);
341378
}
342379

343380
/**
@@ -469,8 +506,9 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
469506
*
470507
* Emit an {MultiplierUpdated} event.
471508
*/
472-
function _updateMultiplier(uint256 newMultiplier) internal virtual {
509+
function _updateMultiplier(uint256 newMultiplier, uint256 newMultiplierNonce) internal virtual {
473510
multiplier = newMultiplier;
511+
multiplierNonce = newMultiplierNonce;
474512
emit MultiplierUpdated(newMultiplier);
475513
}
476514

test/BackedAutoFeeTokenImplementation.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable prettier/prettier */
12
import { ProxyAdmin__factory } from '../typechain/factories/ProxyAdmin__factory';
23
import { ProxyAdmin } from '../typechain/ProxyAdmin';
34
import { BackedAutoFeeTokenImplementation__factory } from '../typechain/factories/BackedAutoFeeTokenImplementation__factory';
@@ -184,15 +185,19 @@ describe("BackedAutoFeeTokenImplementation", function () {
184185
describe('when time moved by 365 days forward', () => {
185186
const periodsPassed = 365;
186187
let preMultiplier: BigNumber;
188+
let preMultiplierNonce: BigNumber;
187189
describe('and fee is set to non-zero value', () => {
188190
cacheBeforeEach(async () => {
189191
preMultiplier = await token.multiplier();
192+
preMultiplierNonce = await token.multiplierNonce();
190193
await helpers.time.setNextBlockTimestamp(baseTime + periodsPassed * accrualPeriodLength);
191194
await helpers.mine()
192195
})
193196

194197
it('should change current multiplier', async () => {
195-
expect((await token.getCurrentMultiplier()).newMultiplier).to.be.not.equal(preMultiplier)
198+
const currentMultiplier = await token.getCurrentMultiplier();
199+
expect(currentMultiplier.newMultiplier).to.be.not.equal(preMultiplier)
200+
expect(currentMultiplier.newMultiplierNonce).to.be.not.equal(preMultiplierNonce)
196201
})
197202
it('should not update stored multiplier', async () => {
198203
expect(await token.multiplier()).to.be.equal(preMultiplier)
@@ -202,13 +207,18 @@ describe("BackedAutoFeeTokenImplementation", function () {
202207
cacheBeforeEach(async () => {
203208
await token.updateFeePerPeriod('0');
204209
preMultiplier = await token.multiplier();
210+
preMultiplierNonce = await token.multiplierNonce();
205211
await helpers.time.setNextBlockTimestamp(baseTime + periodsPassed * accrualPeriodLength);
206212
await helpers.mine()
207213
})
208214

209215
it('should not change current multiplier', async () => {
210216
expect((await token.getCurrentMultiplier()).newMultiplier).to.be.equal(preMultiplier)
211217
})
218+
219+
it('should change current multiplier nonce', async () => {
220+
expect((await token.getCurrentMultiplier()).newMultiplierNonce).to.be.equal(preMultiplierNonce)
221+
})
212222
})
213223
})
214224
})
@@ -341,11 +351,12 @@ describe("BackedAutoFeeTokenImplementation", function () {
341351
})
342352

343353
describe('#updateMultiplierValue', () => {
344-
it('Should update stored multiplier value', async () => {
345-
const { newMultiplier: currentMultiplier } = await token.getCurrentMultiplier();
354+
it('Should update stored multiplier value and nonce', async () => {
355+
const { newMultiplier: currentMultiplier, newMultiplierNonce: currentMultiplierNonce } = await token.getCurrentMultiplier();
346356
const newMultiplierValue = currentMultiplier.div(2);
347357
await token.updateMultiplierValue(newMultiplierValue, currentMultiplier)
348358
expect(await token.multiplier()).to.be.equal(newMultiplierValue);
359+
expect(await token.multiplierNonce()).to.be.equal(currentMultiplierNonce.add(1));
349360
expect(await token.lastTimeFeeApplied()).to.be.equal(baseTime + periodsPassed * accrualPeriodLength);
350361
});
351362
it('Should reject update, if wrong past value was passed', async () => {
@@ -357,6 +368,29 @@ describe("BackedAutoFeeTokenImplementation", function () {
357368
});
358369
});
359370

371+
describe('#updateMultiplierWithNonce', () => {
372+
it('Should update stored multiplier value and nonce', async () => {
373+
const { newMultiplier: currentMultiplier, newMultiplierNonce: currentMultiplierNonce } = await token.getCurrentMultiplier();
374+
const newMultiplierValue = currentMultiplier.div(2);
375+
const newMultiplierNonce = currentMultiplierNonce.add(100);
376+
await token.updateMultiplierWithNonce(newMultiplierValue, currentMultiplier, newMultiplierNonce)
377+
expect(await token.multiplier()).to.be.equal(newMultiplierValue);
378+
expect(await token.multiplierNonce()).to.be.equal(newMultiplierNonce);
379+
expect(await token.lastTimeFeeApplied()).to.be.equal(baseTime + periodsPassed * accrualPeriodLength);
380+
});
381+
it('Should reject update, if wrong past value was passed', async () => {
382+
await expect(token.updateMultiplierWithNonce(0, 1, 1)).to.be.reverted;
383+
});
384+
it('Should reject update, if wrong account is used', async () => {
385+
const { newMultiplier: currentMultiplier, newMultiplierNonce: currentMultiplierNonce } = await token.getCurrentMultiplier();
386+
await expect(token.connect(actor.signer).updateMultiplierWithNonce(1, currentMultiplier, currentMultiplierNonce.add(1))).to.be.reverted
387+
});
388+
it('Should reject update, if wrong nonce is used', async () => {
389+
const { newMultiplier: currentMultiplier, newMultiplierNonce: currentMultiplierNonce } = await token.getCurrentMultiplier();
390+
await expect(token.connect(actor.signer).updateMultiplierValue(1, currentMultiplier, currentMultiplierNonce)).to.be.reverted
391+
});
392+
});
393+
360394
describe('#balanceOf', () => {
361395
it('Should decrease balance of the user by fee accrued in 365 days', async () => {
362396
expect((await token.balanceOf(owner.address)).sub(baseMintedAmount.mul(annualFee * 100).div(100)).abs()).to.lte(

0 commit comments

Comments
 (0)