// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.abstractcontractERC20{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256 amount);
eventApproval(addressindexed owner, addressindexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
uint8publicimmutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/uint256public totalSupply;
mapping(address=>uint256) public balanceOf;
mapping(address=>mapping(address=>uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/uint256internalimmutable INITIAL_CHAIN_ID;
bytes32internalimmutable INITIAL_DOMAIN_SEPARATOR;
mapping(address=>uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID =block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 amount) publicvirtualreturns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
returntrue;
}
functiontransfer(address to, uint256 amount) publicvirtualreturns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
returntrue;
}
functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualreturns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
returntrue;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/functionpermit(address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) publicvirtual{
require(deadline >=block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing// the owner's nonce which cannot realistically overflow.unchecked {
address recoveredAddress =ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress !=address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
functionDOMAIN_SEPARATOR() publicviewvirtualreturns (bytes32) {
returnblock.chainid== INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
functioncomputeDomainSeparator() internalviewvirtualreturns (bytes32) {
returnkeccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to, uint256 amount) internalvirtual{
totalSupply += amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function_burn(addressfrom, uint256 amount) internalvirtual{
balanceOf[from] -= amount;
// Cannot underflow because a user's balance// will never be larger than the total supply.unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
Contract Source Code
File 2 of 4: NektarToken.sol
// SPDX-License-Identifier: MITpragmasolidity 0.8.26;import {ERC20} from"solmate/tokens/ERC20.sol";
import {OwnableRoles} from"solady/auth/OwnableRoles.sol";
/**
* @title NektarToken
* @notice Implementation of the Nektar Token with transfer restrictions and whitelist functionality
* @dev Extends ERC20 and OwnableRoles to provide a token with time-based transfer restrictions and whitelisting
*/contractNektarTokenisERC20, OwnableRoles{
/**
* @notice The role for addresses that are allowed to transfer tokens before the timelock expires
* @dev Uses the first available role from OwnableRoles
*/uint256internalconstant _TRANSFER_ROLE = _ROLE_0;
/**
* @notice The timestamp after which all transfers are allowed
* @dev Set during contract deployment and cannot be changed afterwards
*/boolpublic isTransferable;
/**
* @notice Error thrown when a transfer is attempted before the timelock expires by a non-whitelisted address
*/errorTransferNotAllowed();
/**
* @notice Error thrown when an invalid address is provided
*/errorInvalidAddress();
/**
* @notice Error thrown when transferability is already enabled
*/errorTransferabilityAlreadyEnabled();
/**
* @notice Event emitted when transferability is enabled
*/eventTransferabilityEnabled();
/**
* @notice Initializes the NektarToken contract
* @dev Sets up the token with initial supply, transfer timelock, and whitelist
* @param _transferWhitelist Array of addresses initially whitelisted for transfers
* @param _owner The address to receive the initial token supply, allowed to make the distribution and enable transferability
*/constructor(address[] memory _transferWhitelist,
address _owner
) ERC20("Nektar Token", "NET", 18) {
_mint(_owner, 1_000_000_000ether);
_initializeOwner(_owner);
for (uint256 i; i < _transferWhitelist.length; i++) {
_grantRoles(_transferWhitelist[i], _TRANSFER_ROLE);
}
}
/**
* @notice Transfers tokens to a specified address
* @dev Overrides ERC20 transfer function to include transfer restrictions
* @param to The address to transfer tokens to
* @param amount The amount of tokens to transfer
* @return bool Returns true if the transfer was successful
*/functiontransfer(address to,
uint256 amount
) publicoverridereturns (bool) {
_onlyTransferable();
returnsuper.transfer(to, amount);
}
/**
* @notice Transfers tokens from one address to another
* @dev Overrides ERC20 transferFrom function to include transfer restrictions
* @param from The address to transfer tokens from
* @param to The address to transfer tokens to
* @param amount The amount of tokens to transfer
* @return bool Returns true if the transfer was successful
*/functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicoverridereturns (bool) {
_onlyTransferable();
returnsuper.transferFrom(from, to, amount);
}
/**
* @notice Grants the specified address the ability to transfer tokens before the timelock expires
* @param _address The address to grant the transfer role
*/functiongrantTransferRole(address _address) externalonlyOwner{
if (_address ==address(0)) {
revert InvalidAddress();
}
_grantRoles(_address, _TRANSFER_ROLE);
}
/**
* @notice Enables token transferability for all holders
* @dev Can only be called by the contract owner. Once enabled, cannot be disabled.
* @dev Reverts with TransferabilityAlreadyEnabled if transferability is already enabled
* @custom:emits TransferabilityEnabled when transferability is successfully enabled
*/functionenableTransferability() externalonlyOwner{
if (isTransferable) {
revert TransferabilityAlreadyEnabled();
}
isTransferable =true;
emit TransferabilityEnabled();
}
/**
* @notice Checks if the caller is allowed to transfer tokens
* @dev Reverts if the transfer is not allowed based on timelock and whitelist status
*/function_onlyTransferable() internalview{
if (isTransferable) {
return;
}
if (msg.sender== owner()) return;
if (hasAnyRole(msg.sender, _TRANSFER_ROLE)) {
return;
}
revert TransferNotAllowed();
}
}
Contract Source Code
File 3 of 4: Ownable.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;/// @notice Simple single owner authorization mixin./// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)////// @dev Note:/// This implementation does NOT auto-initialize the owner to `msg.sender`./// You MUST call the `_initializeOwner` in the constructor / initializer.////// While the ownable portion follows/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,/// the nomenclature for the 2-step ownership handover may be unique to this codebase.abstractcontractOwnable{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CUSTOM ERRORS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The caller is not authorized to call the function.errorUnauthorized();
/// @dev The `newOwner` cannot be the zero address.errorNewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.errorNoHandoverRequest();
/// @dev Cannot double-initialize.errorAlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* EVENTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The ownership is transferred from `oldOwner` to `newOwner`./// This event is intentionally kept the same as OpenZeppelin's Ownable to be/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),/// despite it not being as lightweight as a single argument event.eventOwnershipTransferred(addressindexed oldOwner, addressindexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.eventOwnershipHandoverRequested(addressindexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.eventOwnershipHandoverCanceled(addressindexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.uint256privateconstant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.uint256privateconstant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.uint256privateconstant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* STORAGE *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The owner slot is given by:/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`./// It is intentionally chosen to be a high value/// to avoid collision with lower slots./// The choice of manual storage layout is to enable compatibility/// with both regular and upgradeable contracts.bytes32internalconstant _OWNER_SLOT =0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:/// ```/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))/// let handoverSlot := keccak256(0x00, 0x20)/// ```/// It stores the expiry timestamp of the two-step ownership handover.uint256privateconstant _HANDOVER_SLOT_SEED =0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* INTERNAL FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Override to return true to make `_initializeOwner` prevent double-initialization.function_guardInitializeOwner() internalpurevirtualreturns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard./// This function must be called upon initialization,/// regardless of whether the contract is upgradeable or not./// This is to enable generalization to both regular and upgradeable contracts,/// and to save gas in case the initial owner is not the caller./// For performance reasons, this function will not check if there/// is an existing owner.function_initializeOwner(address newOwner) internalvirtual{
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assemblyassembly {
let ownerSlot := _OWNER_SLOT
ifsload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner :=shr(96, shl(96, newOwner))
// Store the new value.sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assemblyassembly {
// Clean the upper 96 bits.
newOwner :=shr(96, shl(96, newOwner))
// Store the new value.sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.function_setOwner(address newOwner) internalvirtual{
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assemblyassembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner :=shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assemblyassembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner :=shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.function_checkOwner() internalviewvirtual{
/// @solidity memory-safe-assemblyassembly {
// If the caller is not the stored owner, revert.ifiszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds./// Override to return a different value if needed./// Made internal to conserve bytecode. Wrap it in a public function if needed.function_ownershipHandoverValidFor() internalviewvirtualreturns (uint64) {
return48*3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* PUBLIC UPDATE FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Allows the owner to transfer the ownership to `newOwner`.functiontransferOwnership(address newOwner) publicpayablevirtualonlyOwner{
/// @solidity memory-safe-assemblyassembly {
ifiszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.functionrenounceOwnership() publicpayablevirtualonlyOwner{
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller./// The request will automatically expire in 48 hours (172800 seconds) by default.functionrequestOwnershipHandover() publicpayablevirtual{
unchecked {
uint256 expires =block.timestamp+ _ownershipHandoverValidFor();
/// @solidity memory-safe-assemblyassembly {
// Compute and set the handover slot to `expires`.mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.functioncancelOwnershipHandover() publicpayablevirtual{
/// @solidity memory-safe-assemblyassembly {
// Compute and set the handover slot to 0.mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`./// Reverts if there is no existing ownership handover requested by `pendingOwner`.functioncompleteOwnershipHandover(address pendingOwner) publicpayablevirtualonlyOwner{
/// @solidity memory-safe-assemblyassembly {
// Compute and set the handover slot to 0.mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot :=keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.ifgt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.revert(0x1c, 0x04)
}
// Set the handover slot to 0.sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* PUBLIC READ FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Returns the owner of the contract.functionowner() publicviewvirtualreturns (address result) {
/// @solidity memory-safe-assemblyassembly {
result :=sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.functionownershipHandoverExpiresAt(address pendingOwner)
publicviewvirtualreturns (uint256 result)
{
/// @solidity memory-safe-assemblyassembly {
// Compute the handover slot.mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result :=sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* MODIFIERS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Marks a function as only callable by the owner.modifieronlyOwner() virtual{
_checkOwner();
_;
}
}
Contract Source Code
File 4 of 4: OwnableRoles.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;import {Ownable} from"./Ownable.sol";
/// @notice Simple single owner and multiroles authorization mixin./// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)/// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)/// for compatibility, the nomenclature for the 2-step ownership handover and roles/// may be unique to this codebase.abstractcontractOwnableRolesisOwnable{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* EVENTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The `user`'s roles is updated to `roles`./// Each bit of `roles` represents whether the role is set.eventRolesUpdated(addressindexed user, uint256indexed roles);
/// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.uint256privateconstant _ROLES_UPDATED_EVENT_SIGNATURE =0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* STORAGE *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The role slot of `user` is given by:/// ```/// mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED))/// let roleSlot := keccak256(0x00, 0x20)/// ```/// This automatically ignores the upper bits of the `user` in case/// they are not clean, as well as keep the `keccak256` under 32-bytes.////// Note: This is equivalent to `uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))`.uint256privateconstant _ROLE_SLOT_SEED =0x8b78c6d8;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* INTERNAL FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Overwrite the roles directly without authorization guard.function_setRoles(address user, uint256 roles) internalvirtual{
/// @solidity memory-safe-assemblyassembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Store the new value.sstore(keccak256(0x0c, 0x20), roles)
// Emit the {RolesUpdated} event.log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
}
}
/// @dev Updates the roles directly without authorization guard./// If `on` is true, each set bit of `roles` will be turned on,/// otherwise, each set bit of `roles` will be turned off.function_updateRoles(address user, uint256 roles, bool on) internalvirtual{
/// @solidity memory-safe-assemblyassembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
let roleSlot :=keccak256(0x0c, 0x20)
// Load the current value.let current :=sload(roleSlot)
// Compute the updated roles if `on` is true.let updated :=or(current, roles)
// Compute the updated roles if `on` is false.// Use `and` to compute the intersection of `current` and `roles`,// `xor` it with `current` to flip the bits in the intersection.ifiszero(on) { updated :=xor(current, and(current, roles)) }
// Then, store the new value.sstore(roleSlot, updated)
// Emit the {RolesUpdated} event.log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated)
}
}
/// @dev Grants the roles directly without authorization guard./// Each bit of `roles` represents the role to turn on.function_grantRoles(address user, uint256 roles) internalvirtual{
_updateRoles(user, roles, true);
}
/// @dev Removes the roles directly without authorization guard./// Each bit of `roles` represents the role to turn off.function_removeRoles(address user, uint256 roles) internalvirtual{
_updateRoles(user, roles, false);
}
/// @dev Throws if the sender does not have any of the `roles`.function_checkRoles(uint256 roles) internalviewvirtual{
/// @solidity memory-safe-assemblyassembly {
// Compute the role slot.mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection// of the value and `roles` is zero, revert.ifiszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.revert(0x1c, 0x04)
}
}
}
/// @dev Throws if the sender is not the owner,/// and does not have any of the `roles`./// Checks for ownership first, then lazily checks for roles.function_checkOwnerOrRoles(uint256 roles) internalviewvirtual{
/// @solidity memory-safe-assemblyassembly {
// If the caller is not the stored owner.// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.ifiszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
// Compute the role slot.mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection// of the value and `roles` is zero, revert.ifiszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.revert(0x1c, 0x04)
}
}
}
}
/// @dev Throws if the sender does not have any of the `roles`,/// and is not the owner./// Checks for roles first, then lazily checks for ownership.function_checkRolesOrOwner(uint256 roles) internalviewvirtual{
/// @solidity memory-safe-assemblyassembly {
// Compute the role slot.mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection// of the value and `roles` is zero, revert.ifiszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
// If the caller is not the stored owner.// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.ifiszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.revert(0x1c, 0x04)
}
}
}
}
/// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`./// This is meant for frontends like Etherscan, and is therefore not fully optimized./// Not recommended to be called on-chain./// Made internal to conserve bytecode. Wrap it in a public function if needed.function_rolesFromOrdinals(uint8[] memory ordinals) internalpurereturns (uint256 roles) {
/// @solidity memory-safe-assemblyassembly {
for { let i :=shl(5, mload(ordinals)) } i { i :=sub(i, 0x20) } {
// We don't need to mask the values of `ordinals`, as Solidity// cleans dirty upper bits when storing variables into memory.
roles :=or(shl(mload(add(ordinals, i)), 1), roles)
}
}
}
/// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap./// This is meant for frontends like Etherscan, and is therefore not fully optimized./// Not recommended to be called on-chain./// Made internal to conserve bytecode. Wrap it in a public function if needed.function_ordinalsFromRoles(uint256 roles) internalpurereturns (uint8[] memory ordinals) {
/// @solidity memory-safe-assemblyassembly {
// Grab the pointer to the free memory.
ordinals :=mload(0x40)
let ptr :=add(ordinals, 0x20)
let o :=0// The absence of lookup tables, De Bruijn, etc., here is intentional for// smaller bytecode, as this function is not meant to be called on-chain.for { let t := roles } 1 {} {
mstore(ptr, o)
// `shr` 5 is equivalent to multiplying by 0x20.// Push back into the ordinals array if the bit is set.
ptr :=add(ptr, shl(5, and(t, 1)))
o :=add(o, 1)
t :=shr(o, roles)
ifiszero(t) { break }
}
// Store the length of `ordinals`.mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
// Allocate the memory.mstore(0x40, ptr)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* PUBLIC UPDATE FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Allows the owner to grant `user` `roles`./// If the `user` already has a role, then it will be an no-op for the role.functiongrantRoles(address user, uint256 roles) publicpayablevirtualonlyOwner{
_grantRoles(user, roles);
}
/// @dev Allows the owner to remove `user` `roles`./// If the `user` does not have a role, then it will be an no-op for the role.functionrevokeRoles(address user, uint256 roles) publicpayablevirtualonlyOwner{
_removeRoles(user, roles);
}
/// @dev Allow the caller to remove their own roles./// If the caller does not have a role, then it will be an no-op for the role.functionrenounceRoles(uint256 roles) publicpayablevirtual{
_removeRoles(msg.sender, roles);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* PUBLIC READ FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Returns the roles of `user`.functionrolesOf(address user) publicviewvirtualreturns (uint256 roles) {
/// @solidity memory-safe-assemblyassembly {
// Compute the role slot.mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Load the stored value.
roles :=sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns whether `user` has any of `roles`.functionhasAnyRole(address user, uint256 roles) publicviewvirtualreturns (bool) {
return rolesOf(user) & roles !=0;
}
/// @dev Returns whether `user` has all of `roles`.functionhasAllRoles(address user, uint256 roles) publicviewvirtualreturns (bool) {
return rolesOf(user) & roles == roles;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* MODIFIERS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Marks a function as only callable by an account with `roles`.modifieronlyRoles(uint256 roles) virtual{
_checkRoles(roles);
_;
}
/// @dev Marks a function as only callable by the owner or by an account/// with `roles`. Checks for ownership first, then lazily checks for roles.modifieronlyOwnerOrRoles(uint256 roles) virtual{
_checkOwnerOrRoles(roles);
_;
}
/// @dev Marks a function as only callable by an account with `roles`/// or the owner. Checks for roles first, then lazily checks for ownership.modifieronlyRolesOrOwner(uint256 roles) virtual{
_checkRolesOrOwner(roles);
_;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* ROLE CONSTANTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/// IYKYKuint256internalconstant _ROLE_0 =1<<0;
uint256internalconstant _ROLE_1 =1<<1;
uint256internalconstant _ROLE_2 =1<<2;
uint256internalconstant _ROLE_3 =1<<3;
uint256internalconstant _ROLE_4 =1<<4;
uint256internalconstant _ROLE_5 =1<<5;
uint256internalconstant _ROLE_6 =1<<6;
uint256internalconstant _ROLE_7 =1<<7;
uint256internalconstant _ROLE_8 =1<<8;
uint256internalconstant _ROLE_9 =1<<9;
uint256internalconstant _ROLE_10 =1<<10;
uint256internalconstant _ROLE_11 =1<<11;
uint256internalconstant _ROLE_12 =1<<12;
uint256internalconstant _ROLE_13 =1<<13;
uint256internalconstant _ROLE_14 =1<<14;
uint256internalconstant _ROLE_15 =1<<15;
uint256internalconstant _ROLE_16 =1<<16;
uint256internalconstant _ROLE_17 =1<<17;
uint256internalconstant _ROLE_18 =1<<18;
uint256internalconstant _ROLE_19 =1<<19;
uint256internalconstant _ROLE_20 =1<<20;
uint256internalconstant _ROLE_21 =1<<21;
uint256internalconstant _ROLE_22 =1<<22;
uint256internalconstant _ROLE_23 =1<<23;
uint256internalconstant _ROLE_24 =1<<24;
uint256internalconstant _ROLE_25 =1<<25;
uint256internalconstant _ROLE_26 =1<<26;
uint256internalconstant _ROLE_27 =1<<27;
uint256internalconstant _ROLE_28 =1<<28;
uint256internalconstant _ROLE_29 =1<<29;
uint256internalconstant _ROLE_30 =1<<30;
uint256internalconstant _ROLE_31 =1<<31;
uint256internalconstant _ROLE_32 =1<<32;
uint256internalconstant _ROLE_33 =1<<33;
uint256internalconstant _ROLE_34 =1<<34;
uint256internalconstant _ROLE_35 =1<<35;
uint256internalconstant _ROLE_36 =1<<36;
uint256internalconstant _ROLE_37 =1<<37;
uint256internalconstant _ROLE_38 =1<<38;
uint256internalconstant _ROLE_39 =1<<39;
uint256internalconstant _ROLE_40 =1<<40;
uint256internalconstant _ROLE_41 =1<<41;
uint256internalconstant _ROLE_42 =1<<42;
uint256internalconstant _ROLE_43 =1<<43;
uint256internalconstant _ROLE_44 =1<<44;
uint256internalconstant _ROLE_45 =1<<45;
uint256internalconstant _ROLE_46 =1<<46;
uint256internalconstant _ROLE_47 =1<<47;
uint256internalconstant _ROLE_48 =1<<48;
uint256internalconstant _ROLE_49 =1<<49;
uint256internalconstant _ROLE_50 =1<<50;
uint256internalconstant _ROLE_51 =1<<51;
uint256internalconstant _ROLE_52 =1<<52;
uint256internalconstant _ROLE_53 =1<<53;
uint256internalconstant _ROLE_54 =1<<54;
uint256internalconstant _ROLE_55 =1<<55;
uint256internalconstant _ROLE_56 =1<<56;
uint256internalconstant _ROLE_57 =1<<57;
uint256internalconstant _ROLE_58 =1<<58;
uint256internalconstant _ROLE_59 =1<<59;
uint256internalconstant _ROLE_60 =1<<60;
uint256internalconstant _ROLE_61 =1<<61;
uint256internalconstant _ROLE_62 =1<<62;
uint256internalconstant _ROLE_63 =1<<63;
uint256internalconstant _ROLE_64 =1<<64;
uint256internalconstant _ROLE_65 =1<<65;
uint256internalconstant _ROLE_66 =1<<66;
uint256internalconstant _ROLE_67 =1<<67;
uint256internalconstant _ROLE_68 =1<<68;
uint256internalconstant _ROLE_69 =1<<69;
uint256internalconstant _ROLE_70 =1<<70;
uint256internalconstant _ROLE_71 =1<<71;
uint256internalconstant _ROLE_72 =1<<72;
uint256internalconstant _ROLE_73 =1<<73;
uint256internalconstant _ROLE_74 =1<<74;
uint256internalconstant _ROLE_75 =1<<75;
uint256internalconstant _ROLE_76 =1<<76;
uint256internalconstant _ROLE_77 =1<<77;
uint256internalconstant _ROLE_78 =1<<78;
uint256internalconstant _ROLE_79 =1<<79;
uint256internalconstant _ROLE_80 =1<<80;
uint256internalconstant _ROLE_81 =1<<81;
uint256internalconstant _ROLE_82 =1<<82;
uint256internalconstant _ROLE_83 =1<<83;
uint256internalconstant _ROLE_84 =1<<84;
uint256internalconstant _ROLE_85 =1<<85;
uint256internalconstant _ROLE_86 =1<<86;
uint256internalconstant _ROLE_87 =1<<87;
uint256internalconstant _ROLE_88 =1<<88;
uint256internalconstant _ROLE_89 =1<<89;
uint256internalconstant _ROLE_90 =1<<90;
uint256internalconstant _ROLE_91 =1<<91;
uint256internalconstant _ROLE_92 =1<<92;
uint256internalconstant _ROLE_93 =1<<93;
uint256internalconstant _ROLE_94 =1<<94;
uint256internalconstant _ROLE_95 =1<<95;
uint256internalconstant _ROLE_96 =1<<96;
uint256internalconstant _ROLE_97 =1<<97;
uint256internalconstant _ROLE_98 =1<<98;
uint256internalconstant _ROLE_99 =1<<99;
uint256internalconstant _ROLE_100 =1<<100;
uint256internalconstant _ROLE_101 =1<<101;
uint256internalconstant _ROLE_102 =1<<102;
uint256internalconstant _ROLE_103 =1<<103;
uint256internalconstant _ROLE_104 =1<<104;
uint256internalconstant _ROLE_105 =1<<105;
uint256internalconstant _ROLE_106 =1<<106;
uint256internalconstant _ROLE_107 =1<<107;
uint256internalconstant _ROLE_108 =1<<108;
uint256internalconstant _ROLE_109 =1<<109;
uint256internalconstant _ROLE_110 =1<<110;
uint256internalconstant _ROLE_111 =1<<111;
uint256internalconstant _ROLE_112 =1<<112;
uint256internalconstant _ROLE_113 =1<<113;
uint256internalconstant _ROLE_114 =1<<114;
uint256internalconstant _ROLE_115 =1<<115;
uint256internalconstant _ROLE_116 =1<<116;
uint256internalconstant _ROLE_117 =1<<117;
uint256internalconstant _ROLE_118 =1<<118;
uint256internalconstant _ROLE_119 =1<<119;
uint256internalconstant _ROLE_120 =1<<120;
uint256internalconstant _ROLE_121 =1<<121;
uint256internalconstant _ROLE_122 =1<<122;
uint256internalconstant _ROLE_123 =1<<123;
uint256internalconstant _ROLE_124 =1<<124;
uint256internalconstant _ROLE_125 =1<<125;
uint256internalconstant _ROLE_126 =1<<126;
uint256internalconstant _ROLE_127 =1<<127;
uint256internalconstant _ROLE_128 =1<<128;
uint256internalconstant _ROLE_129 =1<<129;
uint256internalconstant _ROLE_130 =1<<130;
uint256internalconstant _ROLE_131 =1<<131;
uint256internalconstant _ROLE_132 =1<<132;
uint256internalconstant _ROLE_133 =1<<133;
uint256internalconstant _ROLE_134 =1<<134;
uint256internalconstant _ROLE_135 =1<<135;
uint256internalconstant _ROLE_136 =1<<136;
uint256internalconstant _ROLE_137 =1<<137;
uint256internalconstant _ROLE_138 =1<<138;
uint256internalconstant _ROLE_139 =1<<139;
uint256internalconstant _ROLE_140 =1<<140;
uint256internalconstant _ROLE_141 =1<<141;
uint256internalconstant _ROLE_142 =1<<142;
uint256internalconstant _ROLE_143 =1<<143;
uint256internalconstant _ROLE_144 =1<<144;
uint256internalconstant _ROLE_145 =1<<145;
uint256internalconstant _ROLE_146 =1<<146;
uint256internalconstant _ROLE_147 =1<<147;
uint256internalconstant _ROLE_148 =1<<148;
uint256internalconstant _ROLE_149 =1<<149;
uint256internalconstant _ROLE_150 =1<<150;
uint256internalconstant _ROLE_151 =1<<151;
uint256internalconstant _ROLE_152 =1<<152;
uint256internalconstant _ROLE_153 =1<<153;
uint256internalconstant _ROLE_154 =1<<154;
uint256internalconstant _ROLE_155 =1<<155;
uint256internalconstant _ROLE_156 =1<<156;
uint256internalconstant _ROLE_157 =1<<157;
uint256internalconstant _ROLE_158 =1<<158;
uint256internalconstant _ROLE_159 =1<<159;
uint256internalconstant _ROLE_160 =1<<160;
uint256internalconstant _ROLE_161 =1<<161;
uint256internalconstant _ROLE_162 =1<<162;
uint256internalconstant _ROLE_163 =1<<163;
uint256internalconstant _ROLE_164 =1<<164;
uint256internalconstant _ROLE_165 =1<<165;
uint256internalconstant _ROLE_166 =1<<166;
uint256internalconstant _ROLE_167 =1<<167;
uint256internalconstant _ROLE_168 =1<<168;
uint256internalconstant _ROLE_169 =1<<169;
uint256internalconstant _ROLE_170 =1<<170;
uint256internalconstant _ROLE_171 =1<<171;
uint256internalconstant _ROLE_172 =1<<172;
uint256internalconstant _ROLE_173 =1<<173;
uint256internalconstant _ROLE_174 =1<<174;
uint256internalconstant _ROLE_175 =1<<175;
uint256internalconstant _ROLE_176 =1<<176;
uint256internalconstant _ROLE_177 =1<<177;
uint256internalconstant _ROLE_178 =1<<178;
uint256internalconstant _ROLE_179 =1<<179;
uint256internalconstant _ROLE_180 =1<<180;
uint256internalconstant _ROLE_181 =1<<181;
uint256internalconstant _ROLE_182 =1<<182;
uint256internalconstant _ROLE_183 =1<<183;
uint256internalconstant _ROLE_184 =1<<184;
uint256internalconstant _ROLE_185 =1<<185;
uint256internalconstant _ROLE_186 =1<<186;
uint256internalconstant _ROLE_187 =1<<187;
uint256internalconstant _ROLE_188 =1<<188;
uint256internalconstant _ROLE_189 =1<<189;
uint256internalconstant _ROLE_190 =1<<190;
uint256internalconstant _ROLE_191 =1<<191;
uint256internalconstant _ROLE_192 =1<<192;
uint256internalconstant _ROLE_193 =1<<193;
uint256internalconstant _ROLE_194 =1<<194;
uint256internalconstant _ROLE_195 =1<<195;
uint256internalconstant _ROLE_196 =1<<196;
uint256internalconstant _ROLE_197 =1<<197;
uint256internalconstant _ROLE_198 =1<<198;
uint256internalconstant _ROLE_199 =1<<199;
uint256internalconstant _ROLE_200 =1<<200;
uint256internalconstant _ROLE_201 =1<<201;
uint256internalconstant _ROLE_202 =1<<202;
uint256internalconstant _ROLE_203 =1<<203;
uint256internalconstant _ROLE_204 =1<<204;
uint256internalconstant _ROLE_205 =1<<205;
uint256internalconstant _ROLE_206 =1<<206;
uint256internalconstant _ROLE_207 =1<<207;
uint256internalconstant _ROLE_208 =1<<208;
uint256internalconstant _ROLE_209 =1<<209;
uint256internalconstant _ROLE_210 =1<<210;
uint256internalconstant _ROLE_211 =1<<211;
uint256internalconstant _ROLE_212 =1<<212;
uint256internalconstant _ROLE_213 =1<<213;
uint256internalconstant _ROLE_214 =1<<214;
uint256internalconstant _ROLE_215 =1<<215;
uint256internalconstant _ROLE_216 =1<<216;
uint256internalconstant _ROLE_217 =1<<217;
uint256internalconstant _ROLE_218 =1<<218;
uint256internalconstant _ROLE_219 =1<<219;
uint256internalconstant _ROLE_220 =1<<220;
uint256internalconstant _ROLE_221 =1<<221;
uint256internalconstant _ROLE_222 =1<<222;
uint256internalconstant _ROLE_223 =1<<223;
uint256internalconstant _ROLE_224 =1<<224;
uint256internalconstant _ROLE_225 =1<<225;
uint256internalconstant _ROLE_226 =1<<226;
uint256internalconstant _ROLE_227 =1<<227;
uint256internalconstant _ROLE_228 =1<<228;
uint256internalconstant _ROLE_229 =1<<229;
uint256internalconstant _ROLE_230 =1<<230;
uint256internalconstant _ROLE_231 =1<<231;
uint256internalconstant _ROLE_232 =1<<232;
uint256internalconstant _ROLE_233 =1<<233;
uint256internalconstant _ROLE_234 =1<<234;
uint256internalconstant _ROLE_235 =1<<235;
uint256internalconstant _ROLE_236 =1<<236;
uint256internalconstant _ROLE_237 =1<<237;
uint256internalconstant _ROLE_238 =1<<238;
uint256internalconstant _ROLE_239 =1<<239;
uint256internalconstant _ROLE_240 =1<<240;
uint256internalconstant _ROLE_241 =1<<241;
uint256internalconstant _ROLE_242 =1<<242;
uint256internalconstant _ROLE_243 =1<<243;
uint256internalconstant _ROLE_244 =1<<244;
uint256internalconstant _ROLE_245 =1<<245;
uint256internalconstant _ROLE_246 =1<<246;
uint256internalconstant _ROLE_247 =1<<247;
uint256internalconstant _ROLE_248 =1<<248;
uint256internalconstant _ROLE_249 =1<<249;
uint256internalconstant _ROLE_250 =1<<250;
uint256internalconstant _ROLE_251 =1<<251;
uint256internalconstant _ROLE_252 =1<<252;
uint256internalconstant _ROLE_253 =1<<253;
uint256internalconstant _ROLE_254 =1<<254;
uint256internalconstant _ROLE_255 =1<<255;
}