Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@
[submodule "l1-contracts/lib/openzeppelin-contracts-upgradeable"]
path = l1-contracts/lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
[submodule "l2-contracts/lib/era-contracts"]
path = l2-contracts/lib/era-contracts
url = https://github.com/matter-labs/era-contracts
2 changes: 2 additions & 0 deletions l2-contracts/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ DEPLOYER_PRIVATE_KEY="0x3d3cbc973389cb26f657686445bcc75662b415b656078503592ac8c1
# for a real, funded account before deploying on zkSync Era or testnet.
TOKEN_SCRIPT_EXECUTOR_PRIVATE_KEY="0x509ca2e9e6acf0ba086477910950125e698d4ea70fa6f63e000c5a22bda9361c"

# RPC endpoint URL for the zkSync network
ZK_RPC_URL=

# This needs to be set for deploying locally on the 'zkSyncLocal' node
#ZK_LOCAL_NETWORK_URL=http:127.0.0.1:8011
Expand Down
2 changes: 1 addition & 1 deletion l2-contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dotenv.config();
const config: HardhatUserConfig = {
solidity: "0.8.24",
zksolc: {
version: "1.4.0",
version: "1.5.14",
settings: {
optimizer: {
enabled: true,
Expand Down
1 change: 1 addition & 0 deletions l2-contracts/lib/era-contracts
Submodule era-contracts added at 0ad33b
48 changes: 48 additions & 0 deletions l2-contracts/src/ZkTokenV3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {ZkTokenV2} from "src/ZkTokenV2.sol";

/// @title ZkTokenV3
/// @author [ScopeLift](https://scopelift.co)
/// @notice A proxy-upgradeable governance token with minting and burning capability gated by access controls.
/// @dev This contract introduces new functionality for burning tokens. It allows the caller to burn their own tokens
/// using the `burn` function and enables addresses with the burner role to burn tokens from other specified addresses
/// using the `burnFrom` function.
/// @dev The same incrementing nonce is used in `delegateBySig`/`delegateOnBehalf` and `permit` function. If a client is
/// calling these functions one after the other then they should use an incremented nonce for the subsequent call.
/// @custom:security-contact security@zksync.io
contract ZkTokenV3 is ZkTokenV2 {
/// @notice Constructor that disables initializers to prevent the implementation contract from being initialized.
constructor() {
_disableInitializers();
}

/// @notice Destroys tokens held by the caller and removes them from the total supply.
/// @param _amount The quantity of tokens, in raw decimals, that will be destroyed.
/// @dev The caller must have sufficient balance to burn the requested amount.
function burn(uint256 _amount) external {
_burn(msg.sender, _amount);
}

/// @notice Destroys tokens held by a given address and removes them from the total supply.
/// @param _from The address from which tokens will be removed and destroyed.
/// @param _amount The quantity of tokens, in raw decimals, that will be destroyed.
/// @dev This method may only be called by an address that has been assigned the burner role by the burner role
/// admin.
function burnFrom(address _from, uint256 _amount) external onlyRole(BURNER_ROLE) {
_burn(_from, _amount);
}

/// @notice Returns the maximum supply of tokens that can ever exist.
/// @return The maximum supply of tokens in raw decimals.
function maxSupply() external pure returns (uint224) {
return _maxSupply();
}

/// @notice Internal function that defines the maximum supply of tokens.
/// @return The maximum supply of tokens in raw decimals (21 billion tokens).
function _maxSupply() internal pure override returns (uint224) {
return 21_000_000_000e18;
}
}
3 changes: 2 additions & 1 deletion l2-contracts/test/ZkCappedMinterFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {IMintableAndDelegatable} from "src/interfaces/IMintableAndDelegatable.so
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {console2} from "forge-std/console2.sol";
import {stdJson} from "forge-std/StdJson.sol";
import {HashIsNonZero} from "era-contracts/system-contracts/contracts/SystemContractErrors.sol";

contract ZkCappedMinterFactoryTest is ZkTokenTest {
bytes32 bytecodeHash;
Expand Down Expand Up @@ -71,7 +72,7 @@ contract CreateCappedMinter is ZkCappedMinterFactoryTest {

factory.createCappedMinter(IMintableAndDelegatable(address(token)), _cappedMinterAdmin, _cap, _saltNonce);

vm.expectRevert("Code hash is non-zero");
vm.expectRevert(abi.encodeWithSelector(HashIsNonZero.selector, bytecodeHash));
factory.createCappedMinter(IMintableAndDelegatable(address(token)), _cappedMinterAdmin, _cap, _saltNonce);
}
}
Expand Down
3 changes: 2 additions & 1 deletion l2-contracts/test/ZkCappedMinterV2Factory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {IMintable} from "src/interfaces/IMintable.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {console2} from "forge-std/console2.sol";
import {stdJson} from "forge-std/StdJson.sol";
import {HashIsNonZero} from "era-contracts/system-contracts/contracts/SystemContractErrors.sol";

contract ZkCappedMinterV2FactoryTest is ZkTokenTest {
bytes32 bytecodeHash;
Expand Down Expand Up @@ -110,7 +111,7 @@ contract CreateCappedMinter is ZkCappedMinterV2FactoryTest {
IMintable(address(token)), _cappedMinterAdmin, _cap, _startTime, _expirationTime, _saltNonce
);

vm.expectRevert("Code hash is non-zero");
vm.expectRevert(abi.encodeWithSelector(HashIsNonZero.selector, bytecodeHash));
factory.createCappedMinter(
IMintable(address(token)), _cappedMinterAdmin, _cap, _startTime, _expirationTime, _saltNonce
);
Expand Down
6 changes: 2 additions & 4 deletions l2-contracts/test/ZkProtocolGovernor.integration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,15 @@ contract ZKProtocolTokenUpgradeTest is ZkProtocolGovernorIntegrationBase {

function _buildEmptyUpgradeCalldata() internal pure returns (bytes memory) {
UpgradeProposal memory proposal = _emptyProposal("1");
return (
abi.encodeWithSignature(
return (abi.encodeWithSignature(
"startUpgrade(uint256,uint256,uint16,bytes32[],address,bytes32[])",
0,
0,
0,
new bytes32[](0),
proposal.executor,
proposal.salt
)
);
));
}

function testFork_ProtocolUpgrade() public {
Expand Down
1 change: 0 additions & 1 deletion l2-contracts/test/ZkTokenV2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ contract Initialize is Test {
ZkTokenV2 tokenV2;
address admin = makeAddr("Admin");
address initMintReceiver = makeAddr("Init Mint Receiver");

uint256 INITIAL_MINT_AMOUNT = 1_000_000_000e18;

// As defined internally in ERC20Votes
Expand Down
Loading
Loading