// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.0; import "../../interfaces/IWhiteListManager.sol"; /// @notice Trident franchised pool ERC-20 with EIP-2612 extension. /// @author Adapted from RariCapital, https://github.com/Rari-Capital/solmate/blob/main/src/erc20/ERC20.sol, /// License-Identifier: AGPL-3.0-only. abstract contract TridentFranchisedERC20 { event Approval(address indexed owner, address indexed spender, uint256 amount); event Transfer(address indexed sender, address indexed recipient, uint256 amount); string public constant name = "Sushi Franchised LP Token"; string public constant symbol = "SLP"; uint8 public constant decimals = 18; address public whiteListManager; address public operator; bool public level2; uint256 public totalSupply; /// @notice owner -> balance mapping. mapping(address => uint256) public balanceOf; /// @notice owner -> spender -> allowance mapping. mapping(address => mapping(address => uint256)) public allowance; /// @notice The EIP-712 typehash for this contract's {permit} struct. bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); /// @notice The EIP-712 typehash for this contract's domain. bytes32 public immutable DOMAIN_SEPARATOR; /// @notice owner -> nonce mapping used in {permit}. mapping(address => uint256) public nonces; constructor() { DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256(bytes("1")), block.chainid, address(this) ) ); } /// @dev Initializes whitelist settings from pool. function initialize( address _whiteListManager, address _operator, bool _level2 ) internal { whiteListManager = _whiteListManager; operator = _operator; if (_level2) level2 = true; } /// @notice Approves `amount` from `msg.sender` to be spent by `spender`. /// @param spender Address of the party that can pull tokens from `msg.sender`'s account. /// @param amount The maximum collective `amount` that `spender` can pull. /// @return (bool) Returns 'true' if succeeded. function approve(address spender, uint256 amount) external returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } /// @notice Transfers `amount` tokens from `msg.sender` to `recipient`. /// @param recipient The address to move tokens to. /// @param amount The token `amount` to move. /// @return (bool) Returns 'true' if succeeded. function transfer(address recipient, uint256 amount) external returns (bool) { if (level2) _checkWhiteList(recipient); balanceOf[msg.sender] -= amount; // @dev This is safe from overflow - the sum of all user // balances can't exceed 'type(uint256).max'. unchecked { balanceOf[recipient] += amount; } emit Transfer(msg.sender, recipient, amount); return true; } /// @notice Transfers `amount` tokens from `sender` to `recipient`. Caller needs approval from `from`. /// @param sender Address to pull tokens `from`. /// @param recipient The address to move tokens to. /// @param amount The token `amount` to move. /// @return (bool) Returns 'true' if succeeded. function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool) { if (level2) _checkWhiteList(recipient); if (allowance[sender][msg.sender] != type(uint256).max) { allowance[sender][msg.sender] -= amount; } balanceOf[sender] -= amount; // @dev This is safe from overflow - the sum of all user // balances can't exceed 'type(uint256).max'. unchecked { balanceOf[recipient] += amount; } emit Transfer(sender, recipient, amount); return true; } /// @notice Triggers an approval from `owner` to `spender`. /// @param owner The address to approve from. /// @param spender The address to be approved. /// @param amount The number of tokens that are approved (2^256-1 means infinite). /// @param deadline The time at which to expire the signature. /// @param v The recovery byte of the signature. /// @param r Half of the ECDSA signature pair. /// @param s Half of the ECDSA signature pair. function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, nonces[owner]++, deadline)) ) ); address recoveredAddress = ecrecover(digest, v, r, s); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_PERMIT_SIGNATURE"); allowance[recoveredAddress][spender] = amount; emit Approval(owner, spender, amount); } function _mint(address recipient, uint256 amount) internal { totalSupply += amount; // @dev This is safe from overflow - the sum of all user // balances can't exceed 'type(uint256).max'. unchecked { balanceOf[recipient] += amount; } emit Transfer(address(0), recipient, amount); } function _burn(address sender, uint256 amount) internal { balanceOf[sender] -= amount; // @dev This is safe from underflow - users won't ever // have a balance larger than `totalSupply`. unchecked { totalSupply -= amount; } emit Transfer(sender, address(0), amount); } /// @dev Checks `whiteListManager` for pool `operator` and given user `account`. function _checkWhiteList(address account) internal view { (, bytes memory _whitelisted) = whiteListManager.staticcall( abi.encodeWithSelector(IWhiteListManager.whitelistedAccounts.selector, operator, account) ); bool whitelisted = abi.decode(_whitelisted, (bool)); require(whitelisted, "NOT_WHITELISTED"); } }