Skip to content

Commit

Permalink
Merge pull request #34 from backed-fi/multiplier-update-nonce
Browse files Browse the repository at this point in the history
feat: multiplier nonce.
  • Loading branch information
jdrskr authored Oct 9, 2024
2 parents f538c9a + 05e12bb commit 7f3c17c
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 22 deletions.
76 changes: 57 additions & 19 deletions contracts/BackedAutoFeeTokenImplementation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {

uint256 internal _totalShares;

uint256 public multiplierNonce;

// Events:

/**
Expand Down Expand Up @@ -104,13 +106,37 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
// Modifiers:

modifier updateMultiplier() {
(uint256 newMultiplier, uint256 periodsPassed) = getCurrentMultiplier();
(uint256 newMultiplier, uint256 periodsPassed, uint256 newMultiplierNonce) = getCurrentMultiplier();
lastTimeFeeApplied = lastTimeFeeApplied + periodLength * periodsPassed;
if (multiplier != newMultiplier) {
_updateMultiplier(newMultiplier);
_updateMultiplier(newMultiplier, newMultiplierNonce);
}
_;
}

modifier onlyMultiplierUpdater() {
require(
_msgSender() == multiplierUpdater,
"BackedToken: Only multiplier updater"
);
_;
}

modifier onlyUpdatedMultiplier(uint256 oldMultiplier) {
require(
multiplier == oldMultiplier,
"BackedToken: Multiplier changed in the meantime"
);
_;
}

modifier onlyNewerMultiplierNonce(uint256 newMultiplierNonce) {
require(
multiplierNonce < newMultiplierNonce,
"BackedToken: Multiplier nonce is outdated."
);
_;
}

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

multiplier = 1e18;
multiplierNonce = 0;
periodLength = _periodLength;
lastTimeFeeApplied = _lastTimeFeeApplied;
feePerPeriod = _feePerPeriod;
Expand All @@ -164,7 +191,7 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
(uint256 newMultiplier, ) = getCurrentMultiplier();
(uint256 newMultiplier, ,) = getCurrentMultiplier();
return _getUnderlyingAmountByShares(_totalShares, newMultiplier);
}

Expand All @@ -174,7 +201,7 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
function balanceOf(
address account
) public view virtual override returns (uint256) {
(uint256 newMultiplier, ) = getCurrentMultiplier();
(uint256 newMultiplier, ,) = getCurrentMultiplier();
return _getUnderlyingAmountByShares(sharesOf(account), newMultiplier);
}

Expand All @@ -186,14 +213,16 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
public
view
virtual
returns (uint256 newMultiplier, uint256 periodsPassed)
returns (uint256 newMultiplier, uint256 periodsPassed, uint256 newMultiplierNonce)
{
periodsPassed = (block.timestamp - lastTimeFeeApplied) / periodLength;
newMultiplier = multiplier;
newMultiplierNonce = multiplierNonce;
if (feePerPeriod > 0) {
for (uint256 index = 0; index < periodsPassed; index++) {
newMultiplier = (newMultiplier * (1e18 - feePerPeriod)) / 1e18;
}
newMultiplierNonce += periodsPassed;
}
}

Expand All @@ -210,7 +239,7 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
function getSharesByUnderlyingAmount(
uint256 _underlyingAmount
) external view returns (uint256) {
(uint256 newMultiplier, ) = getCurrentMultiplier();
(uint256 newMultiplier, ,) = getCurrentMultiplier();
return _getSharesByUnderlyingAmount(_underlyingAmount, newMultiplier);
}

Expand All @@ -220,7 +249,7 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
function getUnderlyingAmountByShares(
uint256 _sharesAmount
) external view returns (uint256) {
(uint256 newMultiplier, ) = getCurrentMultiplier();
(uint256 newMultiplier, ,) = getCurrentMultiplier();
return _getUnderlyingAmountByShares(_sharesAmount, newMultiplier);
}

Expand Down Expand Up @@ -319,7 +348,7 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
}

/**
* @dev Function to change the contract multiplier, only if oldMultiplier did not change in the meantime. Allowed only for owner
* @dev Function to change the contract multiplier, only if oldMultiplier did not change in the meantime. Allowed only for multiplierUpdater
*
* Emits a { MultiplierChanged } event
*
Expand All @@ -328,16 +357,24 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
function updateMultiplierValue(
uint256 newMultiplier,
uint256 oldMultiplier
) external updateMultiplier {
require(
_msgSender() == multiplierUpdater,
"BackedToken: Only multiplier updater"
);
require(
multiplier == oldMultiplier,
"BackedToken: Multiplier changed in the meantime"
);
_updateMultiplier(newMultiplier);
) public onlyMultiplierUpdater updateMultiplier onlyUpdatedMultiplier(oldMultiplier) {
_updateMultiplier(newMultiplier, multiplierNonce + 1);
}

/**
* @dev Function to change the contract multiplier with nonce, only if oldMultiplier did not change in the meantime. Allowed only for multiplierUpdater
*
* Emits a { MultiplierChanged } event
*
* @param newMultiplier New multiplier value
* @param newMultiplierNonce New multplier nonce
*/
function updateMultiplierWithNonce(
uint256 newMultiplier,
uint256 oldMultiplier,
uint256 newMultiplierNonce
) external onlyMultiplierUpdater updateMultiplier onlyUpdatedMultiplier(oldMultiplier) onlyNewerMultiplierNonce(newMultiplierNonce){
_updateMultiplier(newMultiplier, newMultiplierNonce);
}

/**
Expand Down Expand Up @@ -469,8 +506,9 @@ contract BackedAutoFeeTokenImplementation is BackedTokenImplementation {
*
* Emit an {MultiplierUpdated} event.
*/
function _updateMultiplier(uint256 newMultiplier) internal virtual {
function _updateMultiplier(uint256 newMultiplier, uint256 newMultiplierNonce) internal virtual {
multiplier = newMultiplier;
multiplierNonce = newMultiplierNonce;
emit MultiplierUpdated(newMultiplier);
}

Expand Down
40 changes: 37 additions & 3 deletions test/BackedAutoFeeTokenImplementation.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable prettier/prettier */
import { ProxyAdmin__factory } from '../typechain/factories/ProxyAdmin__factory';
import { ProxyAdmin } from '../typechain/ProxyAdmin';
import { BackedAutoFeeTokenImplementation__factory } from '../typechain/factories/BackedAutoFeeTokenImplementation__factory';
Expand Down Expand Up @@ -184,15 +185,19 @@ describe("BackedAutoFeeTokenImplementation", function () {
describe('when time moved by 365 days forward', () => {
const periodsPassed = 365;
let preMultiplier: BigNumber;
let preMultiplierNonce: BigNumber;
describe('and fee is set to non-zero value', () => {
cacheBeforeEach(async () => {
preMultiplier = await token.multiplier();
preMultiplierNonce = await token.multiplierNonce();
await helpers.time.setNextBlockTimestamp(baseTime + periodsPassed * accrualPeriodLength);
await helpers.mine()
})

it('should change current multiplier', async () => {
expect((await token.getCurrentMultiplier()).newMultiplier).to.be.not.equal(preMultiplier)
const currentMultiplier = await token.getCurrentMultiplier();
expect(currentMultiplier.newMultiplier).to.be.not.equal(preMultiplier)
expect(currentMultiplier.newMultiplierNonce).to.be.not.equal(preMultiplierNonce)
})
it('should not update stored multiplier', async () => {
expect(await token.multiplier()).to.be.equal(preMultiplier)
Expand All @@ -202,13 +207,18 @@ describe("BackedAutoFeeTokenImplementation", function () {
cacheBeforeEach(async () => {
await token.updateFeePerPeriod('0');
preMultiplier = await token.multiplier();
preMultiplierNonce = await token.multiplierNonce();
await helpers.time.setNextBlockTimestamp(baseTime + periodsPassed * accrualPeriodLength);
await helpers.mine()
})

it('should not change current multiplier', async () => {
expect((await token.getCurrentMultiplier()).newMultiplier).to.be.equal(preMultiplier)
})

it('should change current multiplier nonce', async () => {
expect((await token.getCurrentMultiplier()).newMultiplierNonce).to.be.equal(preMultiplierNonce)
})
})
})
})
Expand Down Expand Up @@ -341,11 +351,12 @@ describe("BackedAutoFeeTokenImplementation", function () {
})

describe('#updateMultiplierValue', () => {
it('Should update stored multiplier value', async () => {
const { newMultiplier: currentMultiplier } = await token.getCurrentMultiplier();
it('Should update stored multiplier value and nonce', async () => {
const { newMultiplier: currentMultiplier, newMultiplierNonce: currentMultiplierNonce } = await token.getCurrentMultiplier();
const newMultiplierValue = currentMultiplier.div(2);
await token.updateMultiplierValue(newMultiplierValue, currentMultiplier)
expect(await token.multiplier()).to.be.equal(newMultiplierValue);
expect(await token.multiplierNonce()).to.be.equal(currentMultiplierNonce.add(1));
expect(await token.lastTimeFeeApplied()).to.be.equal(baseTime + periodsPassed * accrualPeriodLength);
});
it('Should reject update, if wrong past value was passed', async () => {
Expand All @@ -357,6 +368,29 @@ describe("BackedAutoFeeTokenImplementation", function () {
});
});

describe('#updateMultiplierWithNonce', () => {
it('Should update stored multiplier value and nonce', async () => {
const { newMultiplier: currentMultiplier, newMultiplierNonce: currentMultiplierNonce } = await token.getCurrentMultiplier();
const newMultiplierValue = currentMultiplier.div(2);
const newMultiplierNonce = currentMultiplierNonce.add(100);
await token.updateMultiplierWithNonce(newMultiplierValue, currentMultiplier, newMultiplierNonce)
expect(await token.multiplier()).to.be.equal(newMultiplierValue);
expect(await token.multiplierNonce()).to.be.equal(newMultiplierNonce);
expect(await token.lastTimeFeeApplied()).to.be.equal(baseTime + periodsPassed * accrualPeriodLength);
});
it('Should reject update, if wrong past value was passed', async () => {
await expect(token.updateMultiplierWithNonce(0, 1, 1)).to.be.reverted;
});
it('Should reject update, if wrong account is used', async () => {
const { newMultiplier: currentMultiplier, newMultiplierNonce: currentMultiplierNonce } = await token.getCurrentMultiplier();
await expect(token.connect(actor.signer).updateMultiplierWithNonce(1, currentMultiplier, currentMultiplierNonce.add(1))).to.be.reverted
});
it('Should reject update, if wrong nonce is used', async () => {
const { newMultiplier: currentMultiplier, newMultiplierNonce: currentMultiplierNonce } = await token.getCurrentMultiplier();
await expect(token.connect(actor.signer).updateMultiplierValue(1, currentMultiplier, currentMultiplierNonce)).to.be.reverted
});
});

describe('#balanceOf', () => {
it('Should decrease balance of the user by fee accrued in 365 days', async () => {
expect((await token.balanceOf(owner.address)).sub(baseMintedAmount.mul(annualFee * 100).div(100)).abs()).to.lte(
Expand Down

0 comments on commit 7f3c17c

Please sign in to comment.