// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import {Ownable} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/Ownable.sol'; import {ERC20} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/ERC20.sol'; import {IERC20WithPermit} from '@aave/core-v3/contracts/interfaces/IERC20WithPermit.sol'; /** * @title TestnetERC20 * @dev ERC20 minting logic */ contract TestnetERC20 is IERC20WithPermit, ERC20, Ownable { bytes public constant EIP712_REVISION = bytes('1'); bytes32 internal constant EIP712_DOMAIN = keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'); bytes32 public constant PERMIT_TYPEHASH = keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)'); // Map of address nonces (address => nonce) mapping(address => uint256) internal _nonces; bytes32 public DOMAIN_SEPARATOR; bool internal _protected; /** * @dev Function modifier, if _protected is enabled then msg.sender is required to be the owner */ modifier onlyOwnerIfProtected() { if (_protected == true) { require(owner() == _msgSender(), 'Ownable: caller is not the owner'); } _; } constructor( string memory name, string memory symbol, uint8 decimals, address owner ) ERC20(name, symbol) { uint256 chainId = block.chainid; DOMAIN_SEPARATOR = keccak256( abi.encode( EIP712_DOMAIN, keccak256(bytes(name)), keccak256(EIP712_REVISION), chainId, address(this) ) ); _setupDecimals(decimals); require(owner != address(0)); transferOwnership(owner); _protected = true; } /// @inheritdoc IERC20WithPermit function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override { require(owner != address(0), 'INVALID_OWNER'); //solium-disable-next-line require(block.timestamp <= deadline, 'INVALID_EXPIRATION'); uint256 currentValidNonce = _nonces[owner]; bytes32 digest = keccak256( abi.encodePacked( '\x19\x01', DOMAIN_SEPARATOR, keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline)) ) ); require(owner == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE'); _nonces[owner] = currentValidNonce + 1; _approve(owner, spender, value); } /** * @dev Function to mint tokens * @param value The amount of tokens to mint. * @return A boolean that indicates if the operation was successful. */ function mint(uint256 value) public virtual onlyOwnerIfProtected returns (bool) { _mint(_msgSender(), value); return true; } /** * @dev Function to mint tokens to address * @param account The account to mint tokens. * @param value The amount of tokens to mint. * @return A boolean that indicates if the operation was successful. */ function mint(address account, uint256 value) public virtual onlyOwnerIfProtected returns (bool) { _mint(account, value); return true; } function nonces(address owner) public view returns (uint256) { return _nonces[owner]; } function setProtected(bool state) public onlyOwner { _protected = state; } function isProtected() public view returns (bool) { return _protected; } }