Skip to content

Commit

Permalink
feat: nomismatokopio
Browse files Browse the repository at this point in the history
  • Loading branch information
LHerskind committed Oct 8, 2024
1 parent 9e0ab97 commit 525fefe
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 14 deletions.
49 changes: 36 additions & 13 deletions l1-contracts/.solhint.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
{
"extends": "solhint:recommended",
"rules": {
"compiler-version": ["error", ">=0.8.27"],
"compiler-version": [
"error",
">=0.8.27"
],
"no-inline-assembly": "off",
"gas-custom-errors": "off",
"func-visibility": [
"error",
{
"ignoreConstructors": true
}
],
"no-empty-blocks": "off",
"no-unused-vars": ["error"],
"state-visibility": ["error"],
"no-unused-vars": [
"error"
],
"state-visibility": [
"error"
],
"not-rely-on-time": "off",
"const-name-snakecase": [
"error",
Expand All @@ -32,13 +38,30 @@
"allowPrefix": true
}
],
"private-func-leading-underscore": ["error"],
"private-vars-no-leading-underscore": ["error"],
"func-param-name-leading-underscore": ["error"],
"func-param-name-mixedcase": ["error"],
"strict-override": ["error"],
"strict-import": ["error"],
"ordering": ["error"],
"comprehensive-interface": ["error"]
"private-func-leading-underscore": [
"error"
],
"private-vars-no-leading-underscore": [
"error"
],
"func-param-name-leading-underscore": [
"error"
],
"func-param-name-mixedcase": [
"error"
],
"strict-override": [
"error"
],
"strict-import": [
"error"
],
"ordering": [
"error"
],
"comprehensive-interface": [
"error"
],
"custom-error-over-require": "off"
}
}
}
44 changes: 44 additions & 0 deletions l1-contracts/src/governance/Nomismatokopio.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Aztec Labs.
pragma solidity >=0.8.27;

import {Ownable} from "@oz/access/Ownable.sol";
import {Errors} from "@aztec/governance/libraries/Errors.sol";
import {IMintableERC20} from "@aztec/governance/interfaces/IMintableERC20.sol";
import {INomismatokopio} from "@aztec/governance/interfaces/INomismatokopio.sol";

contract Nomismatokopio is INomismatokopio, Ownable {
IMintableERC20 public immutable ASSET;
uint256 public immutable RATE;
uint256 public timeOfLastMint;

constructor(IMintableERC20 _asset, uint256 _rate, address _owner) Ownable(_owner) {
ASSET = _asset;
RATE = _rate;
timeOfLastMint = block.timestamp;
}

/**
* @notice Mint tokens up to the `mintAvailable` limit
* Beware that the mintAvailable will be reset to 0, and not just
* reduced by the amount minted.
*
* @param _to - The address to receive the funds
* @param _amount - The amount to mint
*/
function mint(address _to, uint256 _amount) external override(INomismatokopio) onlyOwner {
uint256 maxMint = mintAvailable();
require(_amount <= maxMint, Errors.Nomismatokopio__InssuficientMintAvailable(maxMint, _amount));
timeOfLastMint = block.timestamp;
ASSET.mint(_to, _amount);
}

/**
* @notice The amount of funds that is available for "minting"
*
* @return The amount mintable
*/
function mintAvailable() public view override(INomismatokopio) returns (uint256) {
return RATE * (block.timestamp - timeOfLastMint);
}
}
9 changes: 9 additions & 0 deletions l1-contracts/src/governance/interfaces/IMintableERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Aztec Labs.
pragma solidity >=0.8.27;

import {IERC20} from "@oz/token/ERC20/IERC20.sol";

interface IMintableERC20 is IERC20 {
function mint(address _to, uint256 _amount) external;
}
8 changes: 8 additions & 0 deletions l1-contracts/src/governance/interfaces/INomismatokopio.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Aztec Labs.
pragma solidity >=0.8.27;

interface INomismatokopio {
function mint(address _to, uint256 _amount) external;
function mintAvailable() external view returns (uint256);
}
4 changes: 3 additions & 1 deletion l1-contracts/src/governance/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pragma solidity >=0.8.27;
*/
library Errors {
// Registry
error Registry__RollupNotRegistered(address rollup); // 0xa1fee4cf
error Nomismatokopio__InssuficientMintAvailable(uint256 available, uint256 needed); // 0xf268b931

error Registry__RollupAlreadyRegistered(address rollup); // 0x3c34eabf
error Registry__RollupNotRegistered(address rollup); // 0xa1fee4cf
}
20 changes: 20 additions & 0 deletions l1-contracts/test/governance/nomismatokopio/Base.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.27;

import {Test} from "forge-std/Test.sol";

import {IMintableERC20} from "@aztec/governance/interfaces/IMintableERC20.sol";

import {TestERC20} from "@aztec/mock/TestERC20.sol";
import {Nomismatokopio} from "@aztec/governance/Nomismatokopio.sol";

contract NomismatokopioBase is Test {
IMintableERC20 internal token;

Nomismatokopio internal nom;

function _deploy(uint256 _rate) internal {
token = IMintableERC20(address(new TestERC20()));
nom = new Nomismatokopio(token, _rate, address(this));
}
}
62 changes: 62 additions & 0 deletions l1-contracts/test/governance/nomismatokopio/mint.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.27;

import {Ownable} from "@oz/access/Ownable.sol";
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
import {Errors} from "@aztec/governance/libraries/Errors.sol";
import {NomismatokopioBase} from "./Base.t.sol";

contract MintTest is NomismatokopioBase {
uint256 internal constant RATE = 1e18;
uint256 internal maxMint;

function setUp() public {
_deploy(RATE);
vm.warp(block.timestamp + 1000);

maxMint = nom.mintAvailable();

assertGt(maxMint, 0);
}

function test_GivenCallerIsNotOwner(address _caller) external {
// it reverts
vm.assume(_caller != address(this));
vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, _caller));
vm.prank(_caller);
nom.mint(address(0xdead), 1);
}

modifier givenCallerIsOwner() {
_;
}

function test_GivenAmountLargerThanMaxMint(uint256 _amount) external givenCallerIsOwner {
// it reverts
uint256 amount = bound(_amount, maxMint + 1, type(uint256).max);
vm.expectRevert(
abi.encodeWithSelector(
Errors.Nomismatokopio__InssuficientMintAvailable.selector, maxMint, amount
)
);
nom.mint(address(0xdead), amount);
}

function test_GivenAmountLessThanOrEqualMaxMint(uint256 _amount) external givenCallerIsOwner {
// it updates timeOfLastMint
// it mints amount
// it emits a {Transfer} event
// it will return 0 for mintAvailable in same block
uint256 amount = bound(_amount, 1, maxMint);
assertGt(amount, 0);
uint256 balanceBefore = token.balanceOf(address(0xdead));

vm.expectEmit(true, true, true, false, address(token));
emit IERC20.Transfer(address(0), address(0xdead), amount);
nom.mint(address(0xdead), amount);

assertEq(token.balanceOf(address(0xdead)), balanceBefore + amount);
assertEq(nom.mintAvailable(), 0);
assertEq(nom.timeOfLastMint(), block.timestamp);
}
}
11 changes: 11 additions & 0 deletions l1-contracts/test/governance/nomismatokopio/mint.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
MintTest
├── given caller is not owner
│ └── it reverts
└── given caller is owner
├── given amount larger than maxMint
│ └── it reverts
└── given amount less than or equal maxMint
├── it updates timeOfLastMint
├── it mints amount
├── it emits a {Transfer} event
└── it will return 0 for mintAvailable in same block
38 changes: 38 additions & 0 deletions l1-contracts/test/governance/nomismatokopio/mintAvailable.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.27;

import {NomismatokopioBase} from "./Base.t.sol";

contract MintAvailableTest is NomismatokopioBase {
function test_GivenRateIs0(uint256 _time) external {
// it returns 0
_deploy(0);
uint256 timeJump = bound(_time, 0, type(uint128).max);
vm.warp(block.timestamp + timeJump);

assertEq(nom.mintAvailable(), 0);
}

modifier givenRateIsNot0(uint256 _rate) {
uint256 rate = bound(_rate, 1, type(uint128).max);
_deploy(rate);

assertEq(rate, nom.RATE());
_;
}

function test_GivenSameTimeAsDeployment(uint256 _rate) external givenRateIsNot0(_rate) {
// it returns 0
assertEq(nom.mintAvailable(), 0);
}

function test_GivenAfterDeployment(uint256 _rate, uint256 _time) external givenRateIsNot0(_rate) {
// it returns >0

uint256 timeJump = bound(_time, 1, type(uint128).max);
vm.warp(block.timestamp + timeJump);

assertGt(nom.mintAvailable(), 0);
assertEq(nom.mintAvailable(), nom.RATE() * timeJump);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
MintAvailableTest
├── given rate is 0
│ └── it returns 0
└── given rate is not 0
├── given same time as deployment
│ └── it returns 0
└── given after deployment
└── it returns >0

0 comments on commit 525fefe

Please sign in to comment.