base-10grans/contracts/TenGransToken.sol

115 lines
4.2 KiB
Solidity

// 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;
uint256 public immutable nativeChainId;
event TransitionMint(address indexed holder, uint256 amount);
constructor(
string memory name,
string memory symbol,
uint256 initialSupplyWei,
uint256 capWei,
address _signer,
uint256 _nativeChainId
) ERC20(name, symbol) ERC20Permit(name) ERC20Capped(capWei) {
require(initialSupplyWei <= capWei, InvalidInitialSupply());
_mint(msg.sender, initialSupplyWei);
signer = _signer;
nativeChainId = _nativeChainId;
}
/// @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 == nativeChainId, 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);
}
}