Skip to content

Instantly share code, notes, and snippets.

@z0r0z
Created October 15, 2021 17:11
Show Gist options
  • Save z0r0z/b9c7f05d6c1cc47a2529a1b60a3b64ee to your computer and use it in GitHub Desktop.
Save z0r0z/b9c7f05d6c1cc47a2529a1b60a3b64ee to your computer and use it in GitHub Desktop.
ross screwed up modules
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.0;
/// @notice Single owner function access control module.
abstract contract LexOwnable {
event TransferOwner(address indexed from, address indexed to);
event TransferOwnerClaim(address indexed from, address indexed to);
address public owner;
address public pendingOwner;
/// @notice Initialize ownership module for function access control.
/// @param _owner Account to grant ownership.
constructor(address _owner) {
owner = _owner;
emit TransferOwner(address(0), _owner);
}
/// @notice Access control modifier that conditions function to be restricted to `owner` account.
modifier onlyOwner() {
require(msg.sender == owner, "NOT_OWNER");
_;
}
/// @notice `pendingOwner` can claim `owner` account.
function claimOwner() external {
require(msg.sender == pendingOwner, "NOT_PENDING_OWNER");
emit TransferOwner(owner, msg.sender);
owner = msg.sender;
pendingOwner = address(0);
}
/// @notice Transfer `owner` account.
/// @param to Account granted `owner` access control.
/// @param direct If 'true', ownership is directly transferred.
function transferOwner(address to, bool direct) external onlyOwner {
require(to != address(0), "ZERO_ADDRESS");
if (direct) {
owner = to;
emit TransferOwner(msg.sender, to);
} else {
pendingOwner = to;
emit TransferOwnerClaim(msg.sender, to);
}
}
}
/// @notice Ownable function pausing module.
abstract contract LexPausable is LexOwnable {
event TogglePause(bool indexed paused);
bool public paused;
/// @notice Initialize pausing module.
/// @param _paused If 'true', modified functions are paused.
/// @param _owner Account to grant ownership of this module.
constructor(bool _paused, address _owner) LexOwnable(_owner) {
paused = _paused;
emit TogglePause(_paused);
}
/// @notice Function pausing modifier that conditions functions to be called when `paused` is not enabled.
modifier notPaused() {
require(!paused, "PAUSED");
_;
}
/// @notice Toggle `paused` conditions on/off.
/// @param _paused If 'true', modified functions are paused.
function togglePause(bool _paused) external onlyOwner {
paused = _paused;
emit TogglePause(_paused);
}
}
/// @notice Ownable function time restriction module.
abstract contract LexTimeRestricted is LexOwnable {
event ToggleTimeRestriction(bool indexed timeRestrictionEnabled);
event UpdateTimeRestriction(uint256 indexed timeRestriction);
uint256 public timeRestriction;
bool public timeRestrictionEnabled;
/// @notice Initialize time restriction module.
/// @param _timeRestriction Point when restriction ends in Unix time.
/// @param _timeRestrictionEnabled If 'true', modified functions are restricted.
/// @param _owner Account to grant ownership of this module.
constructor(uint256 _timeRestriction, bool _timeRestrictionEnabled, address _owner) LexOwnable(_owner) {
timeRestriction = _timeRestriction;
timeRestrictionEnabled = _timeRestrictionEnabled;
emit ToggleTimeRestriction(_timeRestrictionEnabled);
}
/// @notice Time restriction modifier that conditions function to be called at `timeRestriction` or after.
modifier timeRestricted {
if (timeRestrictionEnabled)
require(block.timestamp >= timeRestriction, "TIME_RESTRICTED");
_;
}
/// @notice Toggle `timeRestriction` conditions on/off.
/// @param _timeRestrictionEnabled If 'true', `timeRestriction` conditions are on.
function toggleTimeRestriction(bool _timeRestrictionEnabled) external onlyOwner {
timeRestrictionEnabled = _timeRestrictionEnabled;
emit ToggleTimeRestriction(_timeRestrictionEnabled);
}
/// @notice Update `timeRestriction` in Unix time.
/// @param _timeRestriction Point when restriction ends in Unix time.
function updateTimeRestriction(uint256 _timeRestriction) external onlyOwner {
timeRestriction = _timeRestriction;
emit UpdateTimeRestriction(_timeRestriction);
}
}
/// @notice Ownable function whitelisting module.
abstract contract LexWhitelistable is LexOwnable {
event ToggleWhiteList(bool indexed whitelistEnabled);
event UpdateWhitelist(address indexed account, bool indexed whitelisted);
bool public whitelistEnabled;
mapping(address => bool) public whitelisted;
/// @notice Initialize contract with `whitelistEnabled` status.
/// @param _whitelistEnabled If 'true', `whitelisted` conditions are on.
/// @param _owner Account to grant ownership of this module.
constructor(bool _whitelistEnabled, address _owner) LexOwnable(_owner) {
whitelistEnabled = _whitelistEnabled;
emit ToggleWhiteList(_whitelistEnabled);
}
/// @notice Whitelisting modifier that conditions function to be called between `whitelisted` accounts.
modifier onlyWhitelisted(address from, address to) {
if (whitelistEnabled)
require(whitelisted[from] && whitelisted[to], "NOT_WHITELISTED");
_;
}
/// @notice Toggle `whitelisted` conditions on/off.
/// @param _whitelistEnabled If 'true', `whitelisted` conditions are on.
function toggleWhitelist(bool _whitelistEnabled) external onlyOwner {
whitelistEnabled = _whitelistEnabled;
emit ToggleWhiteList(_whitelistEnabled);
}
/// @notice Update account `whitelisted` status.
/// @param account Account to update.
/// @param _whitelisted If 'true', `account` is `whitelisted`.
function updateWhitelist(address account, bool _whitelisted) external onlyOwner {
whitelisted[account] = _whitelisted;
emit UpdateWhitelist(account, _whitelisted);
}
}
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Adapted from RariCapital, https://github.com/Rari-Capital/solmate/blob/main/src/erc20/ERC20.sol,
// License-Identifier: AGPL-3.0-only.
abstract contract LexToken {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
bytes32 public constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = _calculateDomainSeparator();
}
function _calculateDomainSeparator() internal view returns (bytes32 domainSeperator) {
domainSeperator = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256(bytes("1")),
block.chainid,
address(this)
)
);
}
function DOMAIN_SEPARATOR() public view returns (bytes32 domainSeperator) {
domainSeperator = block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : _calculateDomainSeparator();
}
function approve(address spender, uint256 value) public virtual returns (bool) {
allowance[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}
function transfer(address to, uint256 value) public virtual returns (bool) {
balanceOf[msg.sender] -= value;
/// @dev This is safe because the sum of all user
// balances can't exceed 'type(uint256).max'.
unchecked {
balanceOf[to] += value;
}
emit Transfer(msg.sender, to, value);
return true;
}
function transferFrom(
address from,
address to,
uint256 value
) public virtual returns (bool) {
if (allowance[from][msg.sender] != type(uint256).max) {
allowance[from][msg.sender] -= value;
}
balanceOf[from] -= value;
/// @dev This is safe because the sum of all user
// balances can't exceed 'type(uint256).max'.
unchecked {
balanceOf[to] += value;
}
emit Transfer(from, to, value);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
/// @dev This is reasonably safe from overflow because incrementing `nonces` beyond
// 'type(uint256).max' is exceedingly unlikely compared to optimization benefits.
unchecked {
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, "INVALID_PERMIT_SIGNATURE");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function _mint(address to, uint256 value) internal {
totalSupply += value;
/// @dev This is safe because the sum of all user
// balances can't exceed 'type(uint256).max'.
unchecked {
balanceOf[to] += value;
}
emit Transfer(address(0), to, value);
}
function _burn(address from, uint256 value) internal {
balanceOf[from] -= value;
/// @dev This is safe because a user won't ever
// have a balance larger than `totalSupply`.
unchecked {
totalSupply -= value;
}
emit Transfer(from, address(0), value);
}
}
contract LexTokenRestricted is LexOwnable, LexPausable, LexTimeRestricted, LexWhitelistable {
constructor(address _owner, uint256 _timeRestriction)
LexOwnable(_owner)
LexPausable(_owner, true)
LexTimeRestricted(_timeRestriction, true, _owner)
LexWhitelistable(true, _owner) {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment