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: copy capped minter for v2 #3

Closed
wants to merge 3 commits into from
Closed
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
148 changes: 148 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
name: L2 contracts ci

on:
workflow_dispatch:
pull_request:
push:
branches:
- main

env:
FOUNDRY_PROFILE: ci

jobs:
build:
defaults:
run:
working-directory: ./l2-contracts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Install the dependencies
run: npm install

- name: Build contracts
run: npm run compile

test:
defaults:
run:
working-directory: ./l2-contracts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Install Foundry
uses: dutterbutter/foundry-zksync-toolchain@v1

- name: Install the dependencies
run: npm install

- name: Run tests
run: forge test --no-match-path .integration.t.sol --zksync

- name: Run Era test node
uses: dutterbutter/[email protected]

- name: Run tests
run: npx hardhat test

## coverage:
## defaults:
## run:
## working-directory: ./l2-contracts
## runs-on: ubuntu-latest
## env:
## SKIP_SAFETY_CHECK_IN_UPGRADE_TEST: true
## steps:
## - uses: actions/checkout@v3

## - name: Install Foundry
## uses: dutterbutter/foundry-zksync-toolchain@v1

## - name: Run coverage
## run: FOUNDRY_PROFILE=default npm run foundry-test && FOUNDRY_PROFILE=default forge coverage --report summary --report lcov --no-match-path .integration.t.sol --zksync

## # To ignore coverage for certain directories modify the paths in this step as needed. The
## # below default ignores coverage results for the test and script directories. Alternatively,
## # to include coverage in all directories, comment out this step. Note that because this
## # filtering applies to the lcov file, the summary table generated in the previous step will
## # still include all files and directories.
## # The `--rc lcov_branch_coverage=1` part keeps branch info in the filtered report, since lcov
## # defaults to removing branch info.
## - name: Filter directories
## run: |
## sudo apt update && sudo apt install -y lcov
## lcov --remove lcov.info 'test/*' 'script/*' 'src/lib/*' --output-file lcov.info --rc lcov_branch_coverage=1

## # This step posts a detailed coverage report as a comment and deletes previous comments on
## # each push. The below step is used to fail coverage if the specified coverage threshold is
## # not met. The below step can post a comment (when it's `github-token` is specified) but it's
## # not as useful, and this action cannot fail CI based on a minimum coverage threshold, which
## # is why we use both in this way.
## - name: Post coverage report
## if: github.event_name == 'pull_request' # This action fails when ran outside of a pull request.
## uses: romeovs/[email protected]
## with:
## delete-old-comments: true
## lcov-file: ./lcov.info
## github-token: ${{ secrets.GITHUB_TOKEN }} # Adds a coverage summary comment to the PR.

## - name: Verify minimum coverage
## uses: zgosalvez/github-actions-report-lcov@v2
## with:
## coverage-files: ./lcov.info
## minimum-coverage: 83 # Set coverage threshold.

lint:
defaults:
run:
shell: bash
working-directory: ./l2-contracts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Install scopelint
uses: engineerd/[email protected]
with:
name: scopelint
repo: ScopeLift/scopelint
fromGitHubReleases: true
version: latest
pathInArchive: scopelint-x86_64-linux/scopelint
urlTemplate: https://github.com/ScopeLift/scopelint/releases/download/{{version}}/scopelint-x86_64-linux.tar.xz
token: ${{ secrets.GITHUB_TOKEN }}

- name: Check formatting
run: |
scopelint --version
scopelint check
# DISABLED WHILE REPO IS PRIVATE
# slither-analyze:
# runs-on: ubuntu-latest
# permissions:
# contents: read
# security-events: write
# steps:
# - uses: actions/checkout@v3

# - name: Run Slither
# uses: crytic/[email protected]
# id: slither # Required to reference this step in the next step.
# with:
# fail-on: none # Required to avoid failing the CI run regardless of findings.
# sarif: results.sarif
# slither-args: --filter-paths "./lib|./test" --exclude naming-convention,solc-version

# - name: Upload SARIF file
# uses: github/codeql-action/upload-sarif@v2
# with:
# sarif_file: ${{ steps.slither.outputs.sarif }}
6 changes: 3 additions & 3 deletions l2-contracts/foundry.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[profile.default]
evm_version = "paris"
fs_permissions = [{ access = "read", path = "./zkout" }]
fuzz = { runs = 50 }
optimizer = true
optimizer_runs = 10_000_000
Expand All @@ -11,11 +12,10 @@
]
solc_version = "0.8.24"
verbosity = 3
fs_permissions = [{ access = "read", path = "./zkout" }]

[profile.ci]
fuzz = { runs = 5000 }
invariant = { runs = 1000 }
fuzz = { runs = 1000 }
invariant = { runs = 500 }

[profile.lite]
fuzz = { runs = 50 }
Expand Down
2 changes: 1 addition & 1 deletion l2-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"compile": "forge build && npx hardhat compile",
"local-node": "npx hardhat node-zksync",
"script": "node script/RunScript.js",
"foundry-test": "forge test --no-match-path .integration.t.sol",
"foundry-test": "forge test --no-match-path .integration.t.sol --zksync",
"test": "forge test && npx hardhat test",
"lint": "scopelint check",
"lint:fix": "scopelint fmt"
Expand Down
12 changes: 0 additions & 12 deletions l2-contracts/script/Counter.s.sol

This file was deleted.

14 changes: 0 additions & 14 deletions l2-contracts/src/Counter.sol

This file was deleted.

63 changes: 63 additions & 0 deletions l2-contracts/src/ZkCappedMinterV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {IMintableAndDelegatable} from "src/interfaces/IMintableAndDelegatable.sol";

/// @title ZkCappedMinterV2
/// @author [ScopeLift](https://scopelift.co)
/// @notice A contract to allow a permissioned entity to mint ZK tokens up to a given amount (the cap).
/// @custom:security-contact [email protected]
contract ZkCappedMinterV2 {
/// @notice The contract where the tokens will be minted by an authorized minter.
IMintableAndDelegatable public immutable TOKEN;

/// @notice The address that is allowed to mint tokens.
address public immutable ADMIN;

/// @notice The maximum number of tokens that may be minted by the ZkCappedMinter.
uint256 public immutable CAP;

/// @notice The cumulative number of tokens that have been minted by the ZkCappedMinter.
uint256 public minted = 0;

/// @notice Error for when the cap is exceeded.
error ZkCappedMinterV2__CapExceeded(address minter, uint256 amount);

/// @notice Error for when the caller is not the admin.
error ZkCappedMinterV2__Unauthorized(address account);

/// @notice Constructor for a new ZkCappedMinter contract
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should

Suggested change
/// @notice Constructor for a new ZkCappedMinter contract
/// @notice Constructor for a new ZkCappedMinterV2 contract

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's fix this in a followup

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool updating here:

e680c31

/// @param _token The token contract where tokens will be minted.
/// @param _admin The address that is allowed to mint tokens.
/// @param _cap The maximum number of tokens that may be minted by the ZkCappedMinter.
constructor(IMintableAndDelegatable _token, address _admin, uint256 _cap) {
TOKEN = _token;
ADMIN = _admin;
CAP = _cap;
}

/// @notice Mints a given amount of tokens to a given address, so long as the cap is not exceeded.
/// @param _to The address that will receive the new tokens.
/// @param _amount The quantity of tokens, in raw decimals, that will be created.
function mint(address _to, uint256 _amount) external {
_revertIfUnauthorized();
_revertIfCapExceeded(_amount);
minted += _amount;
TOKEN.mint(_to, _amount);
}

/// @notice Reverts if msg.sender is not the contract admin.
function _revertIfUnauthorized() internal view {
if (msg.sender != ADMIN) {
revert ZkCappedMinterV2__Unauthorized(msg.sender);
}
}

/// @notice Reverts if the amount of new tokens will increase the minted tokens beyond the mint cap.
/// @param _amount The quantity of tokens, in raw decimals, that will checked against the cap.
function _revertIfCapExceeded(uint256 _amount) internal view {
if (minted + _amount > CAP) {
revert ZkCappedMinterV2__CapExceeded(msg.sender, _amount);
}
}
}
70 changes: 70 additions & 0 deletions l2-contracts/src/ZkCappedMinterV2Factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {L2ContractHelper} from "src/lib/L2ContractHelper.sol";
import {ZkCappedMinterV2} from "src/ZkCappedMinterV2.sol";
import {IMintableAndDelegatable} from "src/interfaces/IMintableAndDelegatable.sol";

/// @title ZkCappedMinterV2Factory
/// @author [ScopeLift](https://scopelift.co)
/// @notice Factory contract to deploy ZkCappedMinterV2 contracts using CREATE2.
contract ZkCappedMinterV2Factory {
/// @dev Bytecode hash should be updated with the correct value from
/// ./zkout/ZkCappedMinterV2.sol/ZkCappedMinterV2.json.
bytes32 public immutable BYTECODE_HASH;

constructor(bytes32 _bytecodeHash) {
BYTECODE_HASH = _bytecodeHash;
}

/// @notice Emitted when a new ZkCappedMinterV2 is created.
/// @param minterAddress The address of the newly deployed ZkCappedMinterV2.
/// @param token The token contract where tokens will be minted.
/// @param admin The address authorized to mint tokens.
/// @param cap The maximum number of tokens that may be minted.
event CappedMinterV2Created(address indexed minterAddress, IMintableAndDelegatable token, address admin, uint256 cap);

/// @notice Deploys a new ZkCappedMinterV2 contract using CREATE2.
/// @param _token The token contract where tokens will be minted.
/// @param _admin The address authorized to mint tokens.
/// @param _cap The maximum number of tokens that may be minted.
/// @param _saltNonce A user-provided nonce for salt calculation.
/// @return minterAddress The address of the newly deployed ZkCappedMinterV2.
function createCappedMinter(IMintableAndDelegatable _token, address _admin, uint256 _cap, uint256 _saltNonce)
external
returns (address minterAddress)
{
bytes memory saltArgs = abi.encode(_token, _admin, _cap);
bytes32 salt = _calculateSalt(saltArgs, _saltNonce);
ZkCappedMinterV2 instance = new ZkCappedMinterV2{salt: salt}(_token, _admin, _cap);
minterAddress = address(instance);

emit CappedMinterV2Created(minterAddress, _token, _admin, _cap);
}

/// @notice Computes the address of a ZkCappedMinterV2 deployed via this factory.
/// @param _token The token contract where tokens will be minted.
/// @param _admin The address authorized to mint tokens.
/// @param _cap The maximum number of tokens that may be minted.
/// @param _saltNonce The nonce used for salt calculation.
/// @return addr The address of the ZkCappedMinterV2.
function getMinter(IMintableAndDelegatable _token, address _admin, uint256 _cap, uint256 _saltNonce)
external
view
returns (address addr)
{
bytes memory saltArgs = abi.encode(_token, _admin, _cap);
bytes32 salt = _calculateSalt(saltArgs, _saltNonce);
addr = L2ContractHelper.computeCreate2Address(
address(this), salt, BYTECODE_HASH, keccak256(abi.encode(_token, _admin, _cap))
);
}

/// @notice Calculates the salt for CREATE2 deployment.
/// @param _args The encoded arguments for the salt calculation.
/// @param _saltNonce A user-provided nonce for additional uniqueness.
/// @return The calculated salt as a bytes32 value.
function _calculateSalt(bytes memory _args, uint256 _saltNonce) internal view returns (bytes32) {
return keccak256(abi.encode(_args, block.chainid, _saltNonce));
}
}
24 changes: 0 additions & 24 deletions l2-contracts/test/Counter.t.sol

This file was deleted.

2 changes: 1 addition & 1 deletion l2-contracts/test/ZkCappedMinterFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ contract ZkCappedMinterFactoryTest is ZkTokenTest {

function setUp() public virtual override {
super.setUp();

// Read the bytecode hash from the JSON file
string memory root = vm.projectRoot();
string memory path = string.concat(root, "/zkout/ZkCappedMinter.sol/ZkCappedMinter.json");
Expand Down
Loading
Loading