// SPDX-License-Identifier: MIT // Compatible with OpenZeppelin Contracts ^5.0.0 pragma solidity ^0.8.28; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {ERC20FlashMint} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol"; import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; import {Nonces} from "@openzeppelin/contracts/utils/Nonces.sol"; import {ERC20Capped} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol"; import { IERC7802, IERC165 } from "./interfaces/IERC7802.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Predeploys } from "./libraries/SuperChainPredeploys.sol"; import { TransitionMintLib } from "./libraries/TransitionMintLib.sol"; /// @notice Error for an unauthorized CALLER. error Unauthorized(); /// @notice Error for an invalid signature. error InvalidSignature(); /// @notice Error for an already minted address. error AlreadyMinted(); /// @notice Error for an invalid chain. error InvalidChain(); /// @notice Error for an invalid initial supply. error InvalidInitialSupply(); /// @custom:security-contact moon.eth contract TenGransToken is ERC20, ERC20Permit, ERC20Votes, ERC20FlashMint, ERC20Capped, IERC7802 { address public signer; mapping(address => bool) public transitionMinted; event TransitionMint(address indexed holder, uint256 amount); constructor( string memory name, string memory symbol, uint256 initialSupplyWei, uint256 capWei, address _signer ) ERC20(name, symbol) ERC20Permit(name) ERC20Capped(capWei) { require(initialSupplyWei <= capWei, InvalidInitialSupply()); _mint(msg.sender, initialSupplyWei); signer = _signer; } /// @notice Allows the SuperchainTokenBridge to mint tokens. /// @param _to Address to mint tokens to. /// @param _amount Amount of tokens to mint. function crosschainMint(address _to, uint256 _amount) external { if (msg.sender != Predeploys.SUPERCHAIN_TOKEN_BRIDGE) revert Unauthorized(); _mint(_to, _amount); emit CrosschainMint(_to, _amount, msg.sender); } /// @notice Allows the SuperchainTokenBridge to burn tokens. /// @param _from Address to burn tokens from. /// @param _amount Amount of tokens to burn. function crosschainBurn(address _from, uint256 _amount) external { if (msg.sender != Predeploys.SUPERCHAIN_TOKEN_BRIDGE) revert Unauthorized(); _burn(_from, _amount); emit CrosschainBurn(_from, _amount, msg.sender); } /// @inheritdoc IERC165 function supportsInterface(bytes4 _interfaceId) public view virtual returns (bool) { return _interfaceId == type(IERC7802).interfaceId || _interfaceId == type(IERC20).interfaceId || _interfaceId == type(IERC165).interfaceId; } /// @notice Allows a wallet to mint tokens using a signature, based on the snapshot quantity. /// @param holder Address to mint tokens to. /// @param snapshotQuantity Amount of tokens to mint. /// @param signature Signature of the holder. function transitionMint(address holder, uint256 snapshotQuantity, bytes memory signature) external { require(block.chainid == 8453, InvalidChain()); require(!transitionMinted[holder], AlreadyMinted()); require(TransitionMintLib.verifyMintSignature(holder, snapshotQuantity, signature, signer), InvalidSignature()); transitionMinted[holder] = true; _mint(holder, snapshotQuantity); emit TransitionMint(holder, snapshotQuantity); } // The following functions are overrides required by Solidity. function _update(address from, address to, uint256 value) internal override(ERC20, ERC20Votes, ERC20Capped) { super._update(from, to, value); } function nonces(address owner) public view override(ERC20Permit, Nonces) returns (uint256) { return super.nonces(owner); } }