Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: multiplier nonce. #34

Merged
merged 1 commit into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
jdrskr marked this conversation as resolved.
Show resolved Hide resolved
}
}

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
Loading