Skip to content

Commit 525fefe

Browse files
committed
feat: nomismatokopio
1 parent 9e0ab97 commit 525fefe

File tree

10 files changed

+239
-14
lines changed

10 files changed

+239
-14
lines changed

l1-contracts/.solhint.json

+36-13
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
{
22
"extends": "solhint:recommended",
33
"rules": {
4-
"compiler-version": ["error", ">=0.8.27"],
4+
"compiler-version": [
5+
"error",
6+
">=0.8.27"
7+
],
58
"no-inline-assembly": "off",
6-
"gas-custom-errors": "off",
79
"func-visibility": [
810
"error",
911
{
1012
"ignoreConstructors": true
1113
}
1214
],
1315
"no-empty-blocks": "off",
14-
"no-unused-vars": ["error"],
15-
"state-visibility": ["error"],
16+
"no-unused-vars": [
17+
"error"
18+
],
19+
"state-visibility": [
20+
"error"
21+
],
1622
"not-rely-on-time": "off",
1723
"const-name-snakecase": [
1824
"error",
@@ -32,13 +38,30 @@
3238
"allowPrefix": true
3339
}
3440
],
35-
"private-func-leading-underscore": ["error"],
36-
"private-vars-no-leading-underscore": ["error"],
37-
"func-param-name-leading-underscore": ["error"],
38-
"func-param-name-mixedcase": ["error"],
39-
"strict-override": ["error"],
40-
"strict-import": ["error"],
41-
"ordering": ["error"],
42-
"comprehensive-interface": ["error"]
41+
"private-func-leading-underscore": [
42+
"error"
43+
],
44+
"private-vars-no-leading-underscore": [
45+
"error"
46+
],
47+
"func-param-name-leading-underscore": [
48+
"error"
49+
],
50+
"func-param-name-mixedcase": [
51+
"error"
52+
],
53+
"strict-override": [
54+
"error"
55+
],
56+
"strict-import": [
57+
"error"
58+
],
59+
"ordering": [
60+
"error"
61+
],
62+
"comprehensive-interface": [
63+
"error"
64+
],
65+
"custom-error-over-require": "off"
4366
}
44-
}
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright 2023 Aztec Labs.
3+
pragma solidity >=0.8.27;
4+
5+
import {Ownable} from "@oz/access/Ownable.sol";
6+
import {Errors} from "@aztec/governance/libraries/Errors.sol";
7+
import {IMintableERC20} from "@aztec/governance/interfaces/IMintableERC20.sol";
8+
import {INomismatokopio} from "@aztec/governance/interfaces/INomismatokopio.sol";
9+
10+
contract Nomismatokopio is INomismatokopio, Ownable {
11+
IMintableERC20 public immutable ASSET;
12+
uint256 public immutable RATE;
13+
uint256 public timeOfLastMint;
14+
15+
constructor(IMintableERC20 _asset, uint256 _rate, address _owner) Ownable(_owner) {
16+
ASSET = _asset;
17+
RATE = _rate;
18+
timeOfLastMint = block.timestamp;
19+
}
20+
21+
/**
22+
* @notice Mint tokens up to the `mintAvailable` limit
23+
* Beware that the mintAvailable will be reset to 0, and not just
24+
* reduced by the amount minted.
25+
*
26+
* @param _to - The address to receive the funds
27+
* @param _amount - The amount to mint
28+
*/
29+
function mint(address _to, uint256 _amount) external override(INomismatokopio) onlyOwner {
30+
uint256 maxMint = mintAvailable();
31+
require(_amount <= maxMint, Errors.Nomismatokopio__InssuficientMintAvailable(maxMint, _amount));
32+
timeOfLastMint = block.timestamp;
33+
ASSET.mint(_to, _amount);
34+
}
35+
36+
/**
37+
* @notice The amount of funds that is available for "minting"
38+
*
39+
* @return The amount mintable
40+
*/
41+
function mintAvailable() public view override(INomismatokopio) returns (uint256) {
42+
return RATE * (block.timestamp - timeOfLastMint);
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright 2023 Aztec Labs.
3+
pragma solidity >=0.8.27;
4+
5+
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
6+
7+
interface IMintableERC20 is IERC20 {
8+
function mint(address _to, uint256 _amount) external;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright 2023 Aztec Labs.
3+
pragma solidity >=0.8.27;
4+
5+
interface INomismatokopio {
6+
function mint(address _to, uint256 _amount) external;
7+
function mintAvailable() external view returns (uint256);
8+
}

l1-contracts/src/governance/libraries/Errors.sol

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pragma solidity >=0.8.27;
1111
*/
1212
library Errors {
1313
// Registry
14-
error Registry__RollupNotRegistered(address rollup); // 0xa1fee4cf
14+
error Nomismatokopio__InssuficientMintAvailable(uint256 available, uint256 needed); // 0xf268b931
15+
1516
error Registry__RollupAlreadyRegistered(address rollup); // 0x3c34eabf
17+
error Registry__RollupNotRegistered(address rollup); // 0xa1fee4cf
1618
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity >=0.8.27;
3+
4+
import {Test} from "forge-std/Test.sol";
5+
6+
import {IMintableERC20} from "@aztec/governance/interfaces/IMintableERC20.sol";
7+
8+
import {TestERC20} from "@aztec/mock/TestERC20.sol";
9+
import {Nomismatokopio} from "@aztec/governance/Nomismatokopio.sol";
10+
11+
contract NomismatokopioBase is Test {
12+
IMintableERC20 internal token;
13+
14+
Nomismatokopio internal nom;
15+
16+
function _deploy(uint256 _rate) internal {
17+
token = IMintableERC20(address(new TestERC20()));
18+
nom = new Nomismatokopio(token, _rate, address(this));
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity >=0.8.27;
3+
4+
import {Ownable} from "@oz/access/Ownable.sol";
5+
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
6+
import {Errors} from "@aztec/governance/libraries/Errors.sol";
7+
import {NomismatokopioBase} from "./Base.t.sol";
8+
9+
contract MintTest is NomismatokopioBase {
10+
uint256 internal constant RATE = 1e18;
11+
uint256 internal maxMint;
12+
13+
function setUp() public {
14+
_deploy(RATE);
15+
vm.warp(block.timestamp + 1000);
16+
17+
maxMint = nom.mintAvailable();
18+
19+
assertGt(maxMint, 0);
20+
}
21+
22+
function test_GivenCallerIsNotOwner(address _caller) external {
23+
// it reverts
24+
vm.assume(_caller != address(this));
25+
vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, _caller));
26+
vm.prank(_caller);
27+
nom.mint(address(0xdead), 1);
28+
}
29+
30+
modifier givenCallerIsOwner() {
31+
_;
32+
}
33+
34+
function test_GivenAmountLargerThanMaxMint(uint256 _amount) external givenCallerIsOwner {
35+
// it reverts
36+
uint256 amount = bound(_amount, maxMint + 1, type(uint256).max);
37+
vm.expectRevert(
38+
abi.encodeWithSelector(
39+
Errors.Nomismatokopio__InssuficientMintAvailable.selector, maxMint, amount
40+
)
41+
);
42+
nom.mint(address(0xdead), amount);
43+
}
44+
45+
function test_GivenAmountLessThanOrEqualMaxMint(uint256 _amount) external givenCallerIsOwner {
46+
// it updates timeOfLastMint
47+
// it mints amount
48+
// it emits a {Transfer} event
49+
// it will return 0 for mintAvailable in same block
50+
uint256 amount = bound(_amount, 1, maxMint);
51+
assertGt(amount, 0);
52+
uint256 balanceBefore = token.balanceOf(address(0xdead));
53+
54+
vm.expectEmit(true, true, true, false, address(token));
55+
emit IERC20.Transfer(address(0), address(0xdead), amount);
56+
nom.mint(address(0xdead), amount);
57+
58+
assertEq(token.balanceOf(address(0xdead)), balanceBefore + amount);
59+
assertEq(nom.mintAvailable(), 0);
60+
assertEq(nom.timeOfLastMint(), block.timestamp);
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
MintTest
2+
├── given caller is not owner
3+
│ └── it reverts
4+
└── given caller is owner
5+
├── given amount larger than maxMint
6+
│ └── it reverts
7+
└── given amount less than or equal maxMint
8+
├── it updates timeOfLastMint
9+
├── it mints amount
10+
├── it emits a {Transfer} event
11+
└── it will return 0 for mintAvailable in same block
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity >=0.8.27;
3+
4+
import {NomismatokopioBase} from "./Base.t.sol";
5+
6+
contract MintAvailableTest is NomismatokopioBase {
7+
function test_GivenRateIs0(uint256 _time) external {
8+
// it returns 0
9+
_deploy(0);
10+
uint256 timeJump = bound(_time, 0, type(uint128).max);
11+
vm.warp(block.timestamp + timeJump);
12+
13+
assertEq(nom.mintAvailable(), 0);
14+
}
15+
16+
modifier givenRateIsNot0(uint256 _rate) {
17+
uint256 rate = bound(_rate, 1, type(uint128).max);
18+
_deploy(rate);
19+
20+
assertEq(rate, nom.RATE());
21+
_;
22+
}
23+
24+
function test_GivenSameTimeAsDeployment(uint256 _rate) external givenRateIsNot0(_rate) {
25+
// it returns 0
26+
assertEq(nom.mintAvailable(), 0);
27+
}
28+
29+
function test_GivenAfterDeployment(uint256 _rate, uint256 _time) external givenRateIsNot0(_rate) {
30+
// it returns >0
31+
32+
uint256 timeJump = bound(_time, 1, type(uint128).max);
33+
vm.warp(block.timestamp + timeJump);
34+
35+
assertGt(nom.mintAvailable(), 0);
36+
assertEq(nom.mintAvailable(), nom.RATE() * timeJump);
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
MintAvailableTest
2+
├── given rate is 0
3+
│ └── it returns 0
4+
└── given rate is not 0
5+
├── given same time as deployment
6+
│ └── it returns 0
7+
└── given after deployment
8+
└── it returns >0

0 commit comments

Comments
 (0)