Files
linea-monorepo/contracts/src/tokens/TokenMintingRateLimiter.sol
The Dark Jester e66abc64fd Feat/1076 refactor and allow overriding (#1079)
* allow tokenbridge overrides

* add L1MessageService overrides

* refactor L2 MessageService

* refactor L2 MessageService V1

* use correct modifier

* refactor LineaRollup for overriding

* allow other overrides

* reinstate general in pause on tokenbridge

* add missing NatSpec

* sample overrides

* add generic bridge and document placeholder

* documentation and folder placement

* documentation cleanup

* use imported references

* use variable pragma for inherited contracts

* reset pragmas for some

* use base abstract contracts with version overrides

* use TokenBridgeBase as abstract

* Update contracts/src/bridging/token/TokenBridgeBase.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/bridging/token/TokenBridgeBase.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/bridging/token/TokenBridgeBase.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/bridging/token/interfaces/ITokenBridge.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/messaging/l2/L2MessageServiceBase.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/messaging/l2/v1/interfaces/IL2MessageServiceV1.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/rollup/interfaces/ILineaRollup.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/rollup/LineaRollupBase.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/_testing/unit/bridging/InheritingTokenBridge.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/verifiers/PlonkVerifierDev.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/verifiers/PlonkVerifierForDataAggregation.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/verifiers/PlonkVerifierForMultiTypeDataAggregation.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/verifiers/PlonkVerifierMainnetFull.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* Update contracts/src/verifiers/PlonkVerifierSepoliaFull.sol

Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>

* linting

* allow submitDataAsCalldata overriding

* address missing test coverage

* adjust gap name for storage clarity

---------

Signed-off-by: The Dark Jester <thedarkjester@users.noreply.github.com>
Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
2025-06-17 09:26:24 -07:00

189 lines
5.9 KiB
Solidity

// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.30;
import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
import { ITokenMinter } from "./interfaces/ITokenMinter.sol";
import { ITokenMintingRateLimiter } from "./interfaces/ITokenMintingRateLimiter.sol";
/**
* @title Token minting rate limiter.
* @author ConsenSys Software Inc.
* @custom:security-contact security-report@linea.build
* @dev State variables are public for ease of consumption.
*/
contract TokenMintingRateLimiter is ITokenMinter, ITokenMintingRateLimiter, AccessControl {
bytes32 public constant RATE_LIMIT_SETTER_ROLE = keccak256("RATE_LIMIT_SETTER_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
// @notice How much time before limit resets.
uint256 public mintingPeriodInSeconds;
// @notice Max minted tokens in the time period.
uint256 public mintingLimit;
// @notice The address of the token being minted.
ITokenMinter public tokenAddress;
// @notice The time at which the current period ends at.
uint256 public currentPeriodEnd;
// @notice Amounts already withdrawn this period.
uint256 public mintedAmountInPeriod;
/**
* @notice Constructs the smart contract.
* @param _tokenAddress The address of the token being minted.
* @param _mintingPeriodInSeconds The minting period in seconds.
* @param _mintingLimit The minting limit.
* @param _defaultAdmin The default admin address.
* @param _defaultMinter The default address allowed to mint.
*/
constructor(
address _tokenAddress,
uint256 _mintingPeriodInSeconds,
uint256 _mintingLimit,
address _defaultAdmin,
address _defaultMinter
) {
if (_tokenAddress == address(0)) {
revert ZeroAddressNotAllowed();
}
if (_defaultAdmin == address(0)) {
revert ZeroAddressNotAllowed();
}
if (_defaultMinter == address(0)) {
revert ZeroAddressNotAllowed();
}
if (_mintingPeriodInSeconds == 0) {
revert PeriodIsZero();
}
if (_mintingLimit == 0) {
revert LimitIsZero();
}
tokenAddress = ITokenMinter(_tokenAddress);
mintingPeriodInSeconds = _mintingPeriodInSeconds;
mintingLimit = _mintingLimit;
_grantRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
_grantRole(RATE_LIMIT_SETTER_ROLE, _defaultAdmin);
_grantRole(MINTER_ROLE, _defaultMinter);
uint256 mintingPeriodEnd = block.timestamp + _mintingPeriodInSeconds;
currentPeriodEnd = mintingPeriodEnd;
emit RateLimitInitialized(_mintingPeriodInSeconds, _mintingLimit, mintingPeriodEnd);
}
/**
* @notice Mints a single token amount for a single recipient.
* @param _to The address receiving the token amount.
* @param _amount The amount of token to receive.
* @dev Only the MINTER_ROLE can mint these tokens
*/
function mint(address _to, uint256 _amount) external onlyRole(MINTER_ROLE) {
_addUsedAmount(_amount);
tokenAddress.mint(_to, _amount);
}
/**
* @notice Mints a single token amount for a multiple recipients.
* @param _to The addresses receiving the token amount.
* @param _amount The amount of token to receive.
* @dev Only the MINTER_ROLE can mint these tokens
* @dev Always do an eth_call simular
*/
function batchMint(address[] calldata _to, uint256 _amount) external onlyRole(MINTER_ROLE) {
_addUsedAmount(_to.length * _amount);
tokenAddress.batchMint(_to, _amount);
}
/**
* @notice Mints a 1:1 amounts for multiple recipients.
* @param _to The addresses receiving the token amount.
* @param _amounts The amounts of token to receive.
* @dev Only the MINTER_ROLE can mint these tokens
*/
function batchMintMultiple(address[] calldata _to, uint256[] calldata _amounts) external onlyRole(MINTER_ROLE) {
uint256 addressLength = _to.length;
if (addressLength != _amounts.length) {
revert ArrayLengthsDoNotMatch();
}
uint256 mintAmount;
for (uint256 i; i < addressLength; ) {
mintAmount += _amounts[i];
unchecked {
++i;
}
}
_addUsedAmount(mintAmount);
tokenAddress.batchMintMultiple(_to, _amounts);
}
/**
* @notice Increments the amount used in the period.
* @dev The amount determining logic is external to this (e.g. fees are included when calling here).
* @dev Reverts if the limit is breached.
* @param _usedAmount The amount used to be added.
*/
function _addUsedAmount(uint256 _usedAmount) internal {
uint256 currentPeriodAmountTemp;
if (currentPeriodEnd < block.timestamp) {
currentPeriodEnd = block.timestamp + mintingPeriodInSeconds;
currentPeriodAmountTemp = _usedAmount;
} else {
currentPeriodAmountTemp = mintedAmountInPeriod + _usedAmount;
}
if (currentPeriodAmountTemp > mintingLimit) {
revert RateLimitExceeded();
}
mintedAmountInPeriod = currentPeriodAmountTemp;
}
/**
* @notice Resets the rate limit amount.
* @dev If the used amount is higher, it is set to the limit to avoid confusion/issues.
* @dev Only the RATE_LIMIT_SETTER_ROLE is allowed to execute this function.
* @dev Emits the LimitAmountChanged event.
* @dev usedLimitAmountToSet will use the default value of zero if period has expired
* @param _amount The amount to reset the limit to.
*/
function resetRateLimitAmount(uint256 _amount) external onlyRole(RATE_LIMIT_SETTER_ROLE) {
uint256 usedLimitAmountToSet;
bool amountUsedLoweredToLimit;
bool usedAmountResetToZero;
if (currentPeriodEnd < block.timestamp) {
currentPeriodEnd = block.timestamp + mintingPeriodInSeconds;
usedAmountResetToZero = true;
} else {
if (_amount < mintedAmountInPeriod) {
usedLimitAmountToSet = _amount;
amountUsedLoweredToLimit = true;
}
}
mintingLimit = _amount;
if (usedAmountResetToZero || amountUsedLoweredToLimit) {
mintedAmountInPeriod = usedLimitAmountToSet;
}
emit LimitAmountChanged(_msgSender(), _amount, amountUsedLoweredToLimit, usedAmountResetToZero);
}
}