// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import './interfaces/IVertoV2ERC20.sol'; import './libraries/SafeMath.sol'; abstract contract VertoV2ERC20 is IVertoV2ERC20 { using SafeMath for uint; string internal constant _name = 'V-Token'; string internal _symbol = 'VTK'; uint8 internal constant _decimals = 18; uint internal _totalSupply; mapping(address => uint) internal _balanceOf; mapping(address => mapping(address => uint)) internal _allowance; bytes32 internal _DOMAIN_SEPARATOR; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 internal constant _PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; mapping(address => uint) internal _nonces; constructor() { uint chainId; assembly { chainId := chainid() } _DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256('EIP712Domain(string _name,string version,uint256 chainId,address verifyingContract)'), keccak256(bytes(_name)), keccak256(bytes('1')), chainId, address(this) ) ); } function name() external pure returns (string memory){ return _name; } function symbol() external view returns (string memory){ return _symbol; } function decimals() external pure returns (uint8){ return _decimals; } function totalSupply() external view returns (uint){ return _totalSupply; } function balanceOf(address owner) external view returns (uint){ return _balanceOf[owner]; } function _mint(address to, uint value) internal { _totalSupply = _totalSupply.add(value); _balanceOf[to] = _balanceOf[to].add(value); emit Transfer(address(0), to, value); } function _burn(address from, uint value) internal { _balanceOf[from] = _balanceOf[from].sub(value); _totalSupply = _totalSupply.sub(value); emit Transfer(from, address(0), value); } function _approve(address owner, address spender, uint value) private { _allowance[owner][spender] = value; emit Approval(owner, spender, value); } function allowance(address owner, address spender) virtual external view returns (uint){ return _allowance[owner][spender]; } function _transfer(address from, address to, uint value) private { _balanceOf[from] = _balanceOf[from].sub(value); _balanceOf[to] = _balanceOf[to].add(value); emit Transfer(from, to, value); } function approve(address spender, uint value) virtual external returns (bool) { _approve(msg.sender, spender, value); return true; } function transfer(address to, uint value) virtual external returns (bool) { _transfer(msg.sender, to, value); return true; } function transferFrom(address from, address to, uint value) external virtual returns (bool) { if (_allowance[from][msg.sender] != type(uint256).max) { _allowance[from][msg.sender] = _allowance[from][msg.sender].sub(value); } _transfer(from, to, value); return true; } function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) virtual external { require(deadline >= block.timestamp, 'VertoV2: EXPIRED'); bytes32 digest = keccak256( abi.encodePacked( '\x19\x01', _DOMAIN_SEPARATOR, keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _nonces[owner]++, deadline)) ) ); address recoveredAddress = ecrecover(digest, v, r, s); require(recoveredAddress != address(0) && recoveredAddress == owner, 'VertoV2: INVALID_SIGNATURE'); _approve(owner, spender, value); } function DOMAIN_SEPARATOR() virtual external view returns (bytes32){ return _DOMAIN_SEPARATOR; } function PERMIT_TYPEHASH() virtual external view returns (bytes32){ return _PERMIT_TYPEHASH; } function nonces(address owner) virtual external view returns (uint){ return _nonces[owner]; } }