From 34cc1bf9c297dedaafaed98a040917e1e6e80b5c Mon Sep 17 00:00:00 2001 From: Kristen Pire Date: Thu, 27 Jun 2024 09:36:50 +0200 Subject: [PATCH] feat(IXERC20/MintAllowance): duplicating minting allowance to enable burn allowance. Implement IXERC20 interface in token. there are still some function un-coded --- src/MintAllowanceUpgradeable.sol | 119 +++++++++++++++++++----- src/Token.sol | 85 ++++++++++++++++-- src/interfaces/IXERC20.sol | 150 +++++++++++++++++++++++++++++++ 3 files changed, 326 insertions(+), 28 deletions(-) create mode 100644 src/interfaces/IXERC20.sol diff --git a/src/MintAllowanceUpgradeable.sol b/src/MintAllowanceUpgradeable.sol index 2e54a3d..65d2c82 100644 --- a/src/MintAllowanceUpgradeable.sol +++ b/src/MintAllowanceUpgradeable.sol @@ -1,25 +1,43 @@ pragma solidity ^0.8.20; contract MintAllowanceUpgradeable { - struct MintAllowanceStorage { - mapping(address => uint256) _mintAllowance; - uint256 _maxMintAllowance; + struct Allowance { + uint256 max; + mapping(address => uint256) allowances; + } + + struct AllowanceStorage { + Allowance _mint; + Allowance _burn; } //keccak256("Monerium.MintAllowanceStorage") - bytes32 private constant MintAllowanceStorageLocation = - 0xb337526095403ef89c7becef1792605e55dadf16cfa1d0df874fad9581a6937d; + bytes32 private constant AllowanceStorageLocation = + 0x7dfa07e9bc075623c605ec9614e9976bbce827d371ce2ecdb28aa7ae3f76de54; - function _getMintAllowanceStorage() + function _getAllowanceStorage() private pure - returns (MintAllowanceStorage storage $) + returns (AllowanceStorage storage $) { assembly { - $.slot := MintAllowanceStorageLocation + $.slot := AllowanceStorageLocation } } + /** + * @dev Emitted when allowance is set. + * @param account The address of the account. + * @param amount The amount of allowance. + */ + event BurnAllowance(address indexed account, uint256 amount); + + /** + * @dev Emitted when max allowance is set. + * @param amount The amount of allowance. + */ + event MaxBurnAllowance(uint256 amount); + /** * @dev Emitted when allowance is set. * @param account The address of the account. @@ -33,46 +51,105 @@ contract MintAllowanceUpgradeable { */ event MaxMintAllowance(uint256 amount); + ///////////// Burn + function getBurnAllowance(address account) public view returns (uint256) { + Allowance storage b = _getAllowanceStorage()._burn; + return b.allowances[account]; + } + + function _setBurnAllowance(address account, uint256 amount) internal { + Allowance storage b = _getAllowanceStorage()._burn; + + require( + amount <= b.max, + "BurnAllowance: cannot set allowance higher than max" + ); + + b.allowances[account] = amount; + + emit BurnAllowance(account, amount); + } + + function _setBurnAllowance(address account, uint256 amount) internal { + Allowance storage b = _getAllowanceStorage()._burn; + + require( + amount <= b.max, + "BurnAllowance: cannot set allowance higher than max" + ); + + b.allowances[account] = amount; + + emit BurnAllowance(account, amount); + } + + function getMaxBurnAllowance() public view returns (uint256) { + Allowance storage b = _getAllowanceStorage()._burn; + return b.max; + } + + function _setMaxBurnAllowance(uint256 amount) internal { + Allowance storage b = _getAllowanceStorage()._burn; + + b.max = amount; + + emit MaxBurnAllowance(amount); + } + + function _useBurnAllowance(address account, uint256 amount) internal { + Allowance storage b = _getAllowanceStorage()._burn; + + require( + b.allowances[account] >= amount, + "BurnAllowance: not allowed to burn more than allowed" + ); + + b.allowances[account] -= amount; + + emit BurnAllowance(account, b.allowances[account]); + } + + ///////////// Mint function getMintAllowance(address account) public view returns (uint256) { - MintAllowanceStorage storage s = _getMintAllowanceStorage(); - return s._mintAllowance[account]; + Allowance storage m = _getAllowanceStorage()._mint; + return m.allowances[account]; } function _setMintAllowance(address account, uint256 amount) internal { - MintAllowanceStorage storage s = _getMintAllowanceStorage(); + Allowance storage m = _getAllowanceStorage()._mint; require( - amount <= s._maxMintAllowance, + amount <= m.max, "MintAllowance: cannot set allowance higher than max" ); - s._mintAllowance[account] = amount; + m.allowances[account] = amount; emit MintAllowance(account, amount); } function _useMintAllowance(address account, uint256 amount) internal { - MintAllowanceStorage storage s = _getMintAllowanceStorage(); + Allowance storage m = _getAllowanceStorage()._mint; require( - s._mintAllowance[account] >= amount, + m.allowances[account] >= amount, "MintAllowance: not allowed to mint more than allowed" ); - s._mintAllowance[account] -= amount; + m.allowances[account] -= amount; - emit MintAllowance(account, s._mintAllowance[account]); + emit MintAllowance(account, m.allowances[account]); } function getMaxMintAllowance() public view returns (uint256) { - MintAllowanceStorage storage s = _getMintAllowanceStorage(); - return s._maxMintAllowance; + Allowance storage m = _getAllowanceStorage()._mint; + return m.max; } function _setMaxMintAllowance(uint256 amount) internal { - MintAllowanceStorage storage s = _getMintAllowanceStorage(); + Allowance storage m = _getAllowanceStorage()._mint; - s._maxMintAllowance = amount; + m.max = amount; emit MaxMintAllowance(amount); } diff --git a/src/Token.sol b/src/Token.sol index 9edb38e..9b66ce3 100644 --- a/src/Token.sol +++ b/src/Token.sol @@ -14,6 +14,8 @@ import "./MintAllowanceUpgradeable.sol"; import "./SystemRoleUpgradeable.sol"; import "./IValidator.sol"; +import "./interfaces/IXERC20.sol"; + /** * @dev Token contract with upgradeable patterns, mint allowance, and system roles. */ @@ -22,7 +24,8 @@ contract Token is ERC20PermitUpgradeable, UUPSUpgradeable, MintAllowanceUpgradeable, - SystemRoleUpgradeable + SystemRoleUpgradeable, + IXERC20 { // Subsequent contract versions must retain this variable to avoid storage conflicts with the proxy. IValidator public validator; @@ -56,7 +59,10 @@ contract Token is __UUPSUpgradeable_init(); __SystemRole_init(); validator = IValidator(_validator); - require(validator.CONTRACT_ID() == keccak256("monerium.validator"), "Not Monerium Validator Contract"); + require( + validator.CONTRACT_ID() == keccak256("monerium.validator"), + "Not Monerium Validator Contract" + ); } // _authorizeUpgrade is a crucial part of the UUPS upgrade pattern in OpenZeppelin. @@ -71,14 +77,20 @@ contract Token is _mint(to, amount); } + /** + * @dev Burns tokens from a specified address. This function is reserved for internal use by Monerium's infrastructure. + */ function burn( address from, uint256 amount, - bytes32 , + bytes32, bytes memory signature ) public onlySystemAccounts { require( - from.isValidSignatureNow(0xb77c35c892a1b24b10a2ce49b424e578472333ee8d2456234fff90626332c50f, signature), + from.isValidSignatureNow( + 0xb77c35c892a1b24b10a2ce49b424e578472333ee8d2456234fff90626332c50f, + signature + ), "signature/hash does not match" ); _burn(from, amount); @@ -87,7 +99,7 @@ contract Token is function recover( address from, address to, - bytes32 , + bytes32, uint8 v, bytes32 r, bytes32 s @@ -97,7 +109,10 @@ contract Token is signature = abi.encodePacked(r, s, v); } require( - from.isValidSignatureNow(0xb77c35c892a1b24b10a2ce49b424e578472333ee8d2456234fff90626332c50f, signature), + from.isValidSignatureNow( + 0xb77c35c892a1b24b10a2ce49b424e578472333ee8d2456234fff90626332c50f, + signature + ), "signature/hash does not match" ); uint256 amount = balanceOf(from); @@ -110,7 +125,10 @@ contract Token is // Function to set the validator, restricted to owner function setValidator(address _validator) public onlyOwner { validator = IValidator(_validator); - require(validator.CONTRACT_ID() == keccak256("monerium.validator"), "Not Monerium Validator Contract"); + require( + validator.CONTRACT_ID() == keccak256("monerium.validator"), + "Not Monerium Validator Contract" + ); } // Override transfer function to invoke validator @@ -177,6 +195,59 @@ contract Token is ); } + //--> IXERC20 functions + function setLockbox(address) external view onlyOwner { + revert("not implemented"); // todo : return error + } + + function burn(address user, uint256 amount) external onlySystemAccounts { + //#1 spend allowance + _burn(user, amount); + } + + function setLimits( + address bridge, + uint256 mintingLimit, + uint256 burningLimit + ) external onlyAdminAccounts { + if ( + mintingLimit > (type(uint256).max / 2) || + burningLimit > (type(uint256).max / 2) + ) { + revert IXERC20_LimitsTooHigh(); + } + + _setMaxMintAllowance(mintingLimit); + _setMintAllowance(bridge, mintingLimit); + + _setMaxBurnAllowance(bridge, ) + // burnAllowance? + emit BridgeLimitsSet(mintingLimit, burningLimit, bridge); + } + + function burningCurrentLimitOf( + address bridge + ) external view returns (uint256 limit) { + return 0; + } + + function burningMaxLimitOf( + address bridge + ) external view returns (uint256 limit) { + return 0; + } + + function mintingCurrentLimitOf( + address minter + ) external view returns (uint256 limit) { + return 0; + } + + function mintingMaxLimitOf( + address minter + ) external view returns (uint256 limit) { + return 0; + } } diff --git a/src/interfaces/IXERC20.sol b/src/interfaces/IXERC20.sol new file mode 100644 index 0000000..30421b6 --- /dev/null +++ b/src/interfaces/IXERC20.sol @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.4 <0.9.0; + +interface IXERC20 { + /** + * @notice Emits when a lockbox is set + * + * @param _lockbox The address of the lockbox + */ + + event LockboxSet(address _lockbox); + + /** + * @notice Emits when a limit is set + * + * @param _mintingLimit The updated minting limit we are setting to the bridge + * @param _burningLimit The updated burning limit we are setting to the bridge + * @param _bridge The address of the bridge we are setting the limit too + */ + event BridgeLimitsSet( + uint256 _mintingLimit, + uint256 _burningLimit, + address indexed _bridge + ); + + /** + * @notice Reverts when a user with too low of a limit tries to call mint/burn + */ + + error IXERC20_NotHighEnoughLimits(); + + /** + * @notice Reverts when caller is not the factory + */ + + error IXERC20_NotFactory(); + + /** + * @notice Reverts when limits are too high + */ + error IXERC20_LimitsTooHigh(); + + /** + * @notice Contains the full minting and burning data for a particular bridge + * + * @param minterParams The minting parameters for the bridge + * @param burnerParams The burning parameters for the bridge + */ + struct Bridge { + BridgeParameters minterParams; + BridgeParameters burnerParams; + } + + /** + * @notice Contains the mint or burn parameters for a bridge + * + * @param timestamp The timestamp of the last mint/burn + * @param ratePerSecond The rate per second of the bridge + * @param maxLimit The max limit of the bridge + * @param currentLimit The current limit of the bridge + */ + struct BridgeParameters { + uint256 timestamp; + uint256 ratePerSecond; + uint256 maxLimit; + uint256 currentLimit; + } + + /** + * @notice Sets the lockbox address + * + * @param _lockbox The address of the lockbox + */ + + function setLockbox(address _lockbox) external; + + /** + * @notice Updates the limits of any bridge + * @dev Can only be called by the owner + * @param _mintingLimit The updated minting limit we are setting to the bridge + * @param _burningLimit The updated burning limit we are setting to the bridge + * @param _bridge The address of the bridge we are setting the limits too + */ + function setLimits( + address _bridge, + uint256 _mintingLimit, + uint256 _burningLimit + ) external; + + /** + * @notice Returns the max limit of a minter + * + * @param _minter The minter we are viewing the limits of + * @return _limit The limit the minter has + */ + function mintingMaxLimitOf( + address _minter + ) external view returns (uint256 _limit); + + /** + * @notice Returns the max limit of a bridge + * + * @param _bridge the bridge we are viewing the limits of + * @return _limit The limit the bridge has + */ + + function burningMaxLimitOf( + address _bridge + ) external view returns (uint256 _limit); + + /** + * @notice Returns the current limit of a minter + * + * @param _minter The minter we are viewing the limits of + * @return _limit The limit the minter has + */ + + function mintingCurrentLimitOf( + address _minter + ) external view returns (uint256 _limit); + + /** + * @notice Returns the current limit of a bridge + * + * @param _bridge the bridge we are viewing the limits of + * @return _limit The limit the bridge has + */ + + function burningCurrentLimitOf( + address _bridge + ) external view returns (uint256 _limit); + + /** + * @notice Mints tokens for a user + * @dev Can only be called by a minter + * @param _user The address of the user who needs tokens minted + * @param _amount The amount of tokens being minted + */ + + function mint(address _user, uint256 _amount) external; + + /** + * @notice Burns tokens for a user + * @dev Can only be called by a minter + * @param _user The address of the user who needs tokens burned + * @param _amount The amount of tokens being burned + */ + + function burn(address _user, uint256 _amount) external; +}