Blockchain / Global / Address
Contract
0x6dA08697F8Fd58E2f8d9132e3928c21c537D5BAb
- Balance
- 0 SOMI
- Transactions sent
- 1
- Type
- Contract · 5,225 bytes of code
NativeDistributor Verified
- Compiler
- solc 0.8.25+commit.b61c2a91
- Language
- Solidity
- Optimization
- Yes, 200 runs
- EVM version
- cancun
- License
- —
- Verified
- 2026-10-12
src/sub/NativeDistributor.sol
and 13 more files
Solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import "solady/utils/MerkleProofLib.sol";
import "solady/utils/ECDSA.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {OwnableRoles} from "solady/auth/OwnableRoles.sol";
import {BaseDistributor} from "../sub/BaseDistributor.sol";
// ____ _ _
// / ___| (_) __ _ _ _ ___
// | | | | |/ _` | | | |/ _ \
// | |___| | | (_| | |_| | __/
// \____|_|_|\__, |\__,_|\___| _ _ __ __ ____
// | _ \(_)___| |_|_ __(_) |__ _ _| |_ ___ _ __ / | | \/ |/ ___|
// | | | | / __| __| '__| | '_ \| | | | __/ _ \| '__| | |_____| |\/| | |
// | |_| | \__ \ |_| | | | |_) | |_| | || (_) | | | |_____| | | | |___
// |____/|_|___/\__|_| |_|_.__/ \__,_|\__\___/|_| |_| |_| |_|\____|
interface IWithdrawable {
function withdraw(uint256) external;
}
/// @title NativeDistributor
/// @notice Native token distributor contract
/// @author Alan (@alannotnerd)
contract NativeDistributor is BaseDistributor {
using SafeERC20 for IERC20;
// token to be airdroppped
address public token;
uint256 public fee;
mapping(address => uint256) public nftAllocation;
mapping(address => mapping(uint256 => bool)) public nftClaimed;
error InvalidMerkleRoot();
error NoNftToClaim();
constructor(address _signer, address _token, address _projectAdmin, address _vault)
BaseDistributor(_signer, _projectAdmin, _vault)
{
if (_token == address(0)) revert ZeroAddress();
token = _token;
}
function version() external pure returns (string memory) {
return "2.0.0";
}
function setFee(uint256 _fee) external onlyRoles(PROJECT_ADMIN) {
fee = _fee;
emit FeeSet(_fee);
}
function setNftAllocation(address[] calldata _nfts, uint256[] calldata _allocations)
external
onlyRoles(PROJECT_ADMIN)
{
for (uint256 i = 0; i < _nfts.length; i++) {
address _nftAddress = _nfts[i];
uint256 _allocation = _allocations[i];
nftAllocation[_nftAddress] = _allocation;
}
}
function claimWithNft(address[] calldata _nfts, uint256[] calldata _tokenIds) external {
uint256 _totalAllocated = 0;
for (uint256 i = 0; i < _nfts.length; i++) {
address _nftAddress = _nfts[i];
uint256 _tokenId = _tokenIds[i];
uint256 _allocation = nftAllocation[_nftAddress];
bool _claimed = nftClaimed[_nftAddress][_tokenId];
if (_claimed) {
continue;
}
_totalAllocated += _allocation;
nftClaimed[_nftAddress][_tokenId] = true;
address owner = IERC721(_nftAddress).ownerOf(_tokenId);
bytes memory _extra = bytes("");
_execute(vault, owner, bytes32(uint256(1)), _allocation, _extra);
}
if (_totalAllocated == 0) {
revert NoNftToClaim();
}
}
function _execute(address _vault, address _recipient, bytes32 _configurator, uint256 _amount, bytes memory)
internal
override
{
if (_configurator != bytes32(uint256(1))) {
revert InvalidMerkleRoot();
}
IERC20(token).safeTransferFrom(_vault, address(this), _amount);
IWithdrawable(address(token)).withdraw(_amount);
(bool success,) = _recipient.call{value: _amount}("");
require(success, "Native token transfer failed");
}
receive() external payable {}
fallback() external payable {}
}
Other files
src/sub/BaseDistributor.sol
// SPDX-License-Identifier: MIT pragma solidity 0.8.25; import "solady/utils/ECDSA.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {OwnableRoles} from "solady/auth/OwnableRoles.sol"; import {StreamConfig, ICliqueLock} from "../ICliqueLock.sol"; struct ClaimInfo { uint256 lastClaimedBlock; uint256 claimedAmount; } /// @title Base Distributor /// @author Clique (@Clique2046) /// @author Alan (@alannotnerd) abstract contract BaseDistributor is OwnableRoles { using SafeERC20 for IERC20; // address signing the claims address public signer; // vault address address public vault; // whether the airdrop is active bool public active = false; mapping(bytes32 => mapping(address => ClaimInfo)) public claimed; mapping(bytes32 => bytes32) public merkleConfiguration; // errors error AlreadyClaimed(); error InvalidSignature(); error NotActive(); error ZeroAddress(); error InvalidMerkleProof(bytes32 root); error WithdrawFailed(); error InvalidReleasedAmount(); event AirdropClaimed(address indexed account, bytes32 indexed root, uint256 amount); event MerkleConfigurationUpdate(bytes32 indexed root, bytes32 indexed configurator); event Withdraw(address indexed to, uint256 amount); event FeeSet(uint256 fee); event VaultSet(address indexed vault); uint256 public constant PROJECT_ADMIN = _ROLE_0; /// @notice Construct a new Claim contract /// @param _signer address that can sign messages /// @param _projectAdmin address that can set the signer and claim root constructor(address _signer, address _projectAdmin, address _vault) { if (_signer == address(0)) revert ZeroAddress(); signer = _signer; vault = _vault; _initializeOwner(msg.sender); _setRoles(_projectAdmin, PROJECT_ADMIN); } /// @notice Modifier to check if the airdrop is active modifier whenActive() { if (!active) revert NotActive(); _; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ADMIN FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @notice Set the signer /// @param _signer address that can sign messages function setSigner(address _signer) external onlyRoles(PROJECT_ADMIN) { signer = _signer; } /// @notice Set the claim root /// @param _claimRoot root of the merkle tree function setClaimRoot(bytes32 _claimRoot, bytes32 _configurator) external onlyRoles(PROJECT_ADMIN) { merkleConfiguration[_claimRoot] = _configurator; emit MerkleConfigurationUpdate(_claimRoot, _configurator); } /// @notice Set the vault /// @param _vault address of the vault function setVault(address _vault) external onlyRoles(PROJECT_ADMIN) { vault = _vault; emit VaultSet(_vault); } /// @notice Toggle the active state function toggleActive() external onlyRoles(PROJECT_ADMIN) { active = !active; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* FEE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @notice Withdraw the fee /// @dev Only callable by the owner function withdrawFee(address _recipient) external onlyOwner { uint256 _balance = address(this).balance; if (_balance > 0) { (bool _success,) = payable(_recipient).call{value: _balance}(""); if (!_success) revert WithdrawFailed(); emit Withdraw(_recipient, _balance); } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EXTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @notice Claim airdrop tokens. Checks for both merkle proof // and signature validation /// @param _proof merkle proof of the claim /// @param _signature signature of the claim /// @param _amount amount of tokens to claim /// @param _released amount of tokens that released /// @param _onBehalfOf address to claim on behalf of /// @param _extra extra data to pass to the claim function function claim( bytes32[] calldata _proof, bytes calldata _signature, uint256 _amount, uint256 _released, address _onBehalfOf, bytes calldata _extra ) external payable whenActive { (bytes32 _root, bytes32 _configurator) = _rootCheck(_proof, _amount, _onBehalfOf); ClaimInfo memory _claimInfo = claimed[_root][_onBehalfOf]; uint256 claimedAmount_ = _claimInfo.claimedAmount; if (_amount < _released) { revert InvalidReleasedAmount(); } if (claimedAmount_ == _released) { revert AlreadyClaimed(); } claimed[_root][_onBehalfOf] = ClaimInfo({lastClaimedBlock: block.number, claimedAmount: _released}); _signatureCheck(_amount, _released, _onBehalfOf, _signature, _root, signer, _extra); uint256 _claimableAmount = _released - claimedAmount_; _execute(vault, _onBehalfOf, _configurator, _claimableAmount, _extra); emit AirdropClaimed(_onBehalfOf, _root, _claimableAmount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PRIVATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @notice Internal function to execute the claim /// @param _vault vault address /// @param _recipient recipient address /// @param _configurator configurator address /// @param _amount amount of tokens to claim /// @param _extra extra data to pass to the claim function function _execute(address _vault, address _recipient, bytes32 _configurator, uint256 _amount, bytes memory _extra) internal virtual; /// @notice Internal function to check the merkle proof /// @param _proof merkle proof of the claim /// @param _amount amount of tokens to claim /// @param _onBehalfOf address to claim on behalf of function _rootCheck(bytes32[] calldata _proof, uint256 _amount, address _onBehalfOf) internal view returns (bytes32 root, bytes32 configurator) { root = recover(_proof, keccak256(abi.encodePacked(_onBehalfOf, _amount))); configurator = merkleConfiguration[root]; if (configurator == bytes32(0)) revert InvalidMerkleProof(root); } /// @notice Internal function to check the signature /// @param _amount amount of tokens to claim /// @param _released amount of tokens that released /// @param _onBehalfOf address to claim on behalf of /// @param _signature signature of the claim /// @param _root root of the merkle tree /// @param _signer signer to check function _signatureCheck( uint256 _amount, uint256 _released, address _onBehalfOf, bytes calldata _signature, bytes32 _root, address _signer, bytes calldata _extra ) internal view { if (_signature.length == 0) revert InvalidSignature(); bytes32 messageHash = keccak256(abi.encodePacked(_onBehalfOf, _amount, _released, _root, address(this), block.chainid, _extra)); bytes32 prefixedHash = ECDSA.toEthSignedMessageHash(messageHash); address recoveredSigner = ECDSA.recoverCalldata(prefixedHash, _signature); if (recoveredSigner != _signer) revert InvalidSignature(); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MERKLE PROOF VERIFICATION OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. function recover(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32 root) { /// @solidity memory-safe-assembly assembly { if mload(proof) { // Initialize `offset` to the offset of `proof` elements in memory. let offset := add(proof, 0x20) // Left shift by 5 is equivalent to multiplying by 0x20. let end := add(offset, shl(5, mload(proof))) // Iterate over proof elements to compute root hash. for {} 1 {} { // Slot of `leaf` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(leaf, mload(offset))) // Store elements to hash contiguously in scratch space. // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes. mstore(scratch, leaf) mstore(xor(scratch, 0x20), mload(offset)) // Reuse `leaf` to store the hash to reduce stack operations. leaf := keccak256(0x00, 0x40) offset := add(offset, 0x20) if iszero(lt(offset, end)) { break } } } root := leaf } } }lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC165} from "./IERC165.sol"; /** * @title IERC1363 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363]. * * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction. */ interface IERC1363 is IERC20, IERC165 { /* * Note: the ERC-165 identifier for this interface is 0xb0202a11. * 0xb0202a11 === * bytes4(keccak256('transferAndCall(address,uint256)')) ^ * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^ * bytes4(keccak256('approveAndCall(address,uint256)')) ^ * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) */ /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @param data Additional data with no specified format, sent in call to `spender`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool); }lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "../utils/introspection/IERC165.sol";lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol";lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC1363} from "../../../interfaces/IERC1363.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC-20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { /** * @dev An operation with an ERC-20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. * * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client" * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. * * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client" * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. * * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being * set here. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { safeTransfer(token, to, value); } else if (!token.transferAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferFromAndCallRelaxed( IERC1363 token, address from, address to, uint256 value, bytes memory data ) internal { if (to.code.length == 0) { safeTransferFrom(token, from, to, value); } else if (!token.transferFromAndCall(from, to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}. * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall} * once without retrying, and relies on the returned value to be true. * * Reverts if the returned value is other than `true`. */ function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { forceApprove(token, to, value); } else if (!token.approveAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements. */ function _callOptionalReturn(IERC20 token, bytes memory data) private { uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) // bubble errors if iszero(success) { let ptr := mload(0x40) returndatacopy(ptr, 0, returndatasize()) revert(ptr, returndatasize()) } returnSize := returndatasize() returnValue := mload(0) } if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { bool success; uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) returnSize := returndatasize() returnValue := mload(0) } return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1); } }lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC-721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC-721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or * {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the address zero. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }lib/solady/src/auth/Ownable.sol
// SPDX-License-Identifier: MIT pragma solidity ^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. abstract contract Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The caller is not authorized to call the function. error Unauthorized(); /// @dev The `newOwner` cannot be the zero address. error NewOwnerIsZeroAddress(); /// @dev The `pendingOwner` does not have a valid handover request. error NoHandoverRequest(); /// @dev Cannot double-initialize. error AlreadyInitialized(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* 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. event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); /// @dev An ownership handover to `pendingOwner` has been requested. event OwnershipHandoverRequested(address indexed pendingOwner); /// @dev The ownership handover to `pendingOwner` has been canceled. event OwnershipHandoverCanceled(address indexed pendingOwner); /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. uint256 private constant _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. bytes32 internal constant _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. uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Override to return true to make `_initializeOwner` prevent double-initialization. function _guardInitializeOwner() internal pure virtual returns (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) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT if sload(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-assembly assembly { // 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) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { 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-assembly assembly { 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() internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner, revert. if iszero(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() internal view virtual returns (uint64) { return 48 * 3600; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to transfer the ownership to `newOwner`. function transferOwnership(address newOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { if iszero(shl(96, newOwner)) { mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`. revert(0x1c, 0x04) } } _setOwner(newOwner); } /// @dev Allows the owner to renounce their ownership. function renounceOwnership() public payable virtual onlyOwner { _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. function requestOwnershipHandover() public payable virtual { unchecked { uint256 expires = block.timestamp + _ownershipHandoverValidFor(); /// @solidity memory-safe-assembly assembly { // 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. function cancelOwnershipHandover() public payable virtual { /// @solidity memory-safe-assembly assembly { // 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`. function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { // 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. if gt(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. function owner() public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { result := sload(_OWNER_SLOT) } } /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // 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. modifier onlyOwner() virtual { _checkOwner(); _; } }lib/solady/src/auth/OwnableRoles.sol
// SPDX-License-Identifier: MIT pragma solidity ^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/OwnableRoles.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. abstract contract OwnableRoles is Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The `user`'s roles is updated to `roles`. /// Each bit of `roles` represents whether the role is set. event RolesUpdated(address indexed user, uint256 indexed roles); /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`. uint256 private constant _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")))`. uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Overwrite the roles directly without authorization guard. function _setRoles(address user, uint256 roles) internal virtual { /// @solidity memory-safe-assembly assembly { 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) internal virtual { /// @solidity memory-safe-assembly assembly { 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. if iszero(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) internal virtual { _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) internal virtual { _updateRoles(user, roles, false); } /// @dev Throws if the sender does not have any of the `roles`. function _checkRoles(uint256 roles) internal view virtual { /// @solidity memory-safe-assembly assembly { // 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. if iszero(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) internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner. // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`. if iszero(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. if iszero(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) internal view virtual { /// @solidity memory-safe-assembly assembly { // 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. if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { // If the caller is not the stored owner. // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`. if iszero(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) internal pure returns (uint256 roles) { /// @solidity memory-safe-assembly assembly { 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) internal pure returns (uint8[] memory ordinals) { /// @solidity memory-safe-assembly assembly { // 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) if iszero(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. function grantRoles(address user, uint256 roles) public payable virtual onlyOwner { _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. function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner { _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. function renounceRoles(uint256 roles) public payable virtual { _removeRoles(msg.sender, roles); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the roles of `user`. function rolesOf(address user) public view virtual returns (uint256 roles) { /// @solidity memory-safe-assembly assembly { // 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`. function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) { return rolesOf(user) & roles != 0; } /// @dev Returns whether `user` has all of `roles`. function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) { return rolesOf(user) & roles == roles; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by an account with `roles`. modifier onlyRoles(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. modifier onlyOwnerOrRoles(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. modifier onlyRolesOrOwner(uint256 roles) virtual { _checkRolesOrOwner(roles); _; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ROLE CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // IYKYK uint256 internal constant _ROLE_0 = 1 << 0; uint256 internal constant _ROLE_1 = 1 << 1; uint256 internal constant _ROLE_2 = 1 << 2; uint256 internal constant _ROLE_3 = 1 << 3; uint256 internal constant _ROLE_4 = 1 << 4; uint256 internal constant _ROLE_5 = 1 << 5; uint256 internal constant _ROLE_6 = 1 << 6; uint256 internal constant _ROLE_7 = 1 << 7; uint256 internal constant _ROLE_8 = 1 << 8; uint256 internal constant _ROLE_9 = 1 << 9; uint256 internal constant _ROLE_10 = 1 << 10; uint256 internal constant _ROLE_11 = 1 << 11; uint256 internal constant _ROLE_12 = 1 << 12; uint256 internal constant _ROLE_13 = 1 << 13; uint256 internal constant _ROLE_14 = 1 << 14; uint256 internal constant _ROLE_15 = 1 << 15; uint256 internal constant _ROLE_16 = 1 << 16; uint256 internal constant _ROLE_17 = 1 << 17; uint256 internal constant _ROLE_18 = 1 << 18; uint256 internal constant _ROLE_19 = 1 << 19; uint256 internal constant _ROLE_20 = 1 << 20; uint256 internal constant _ROLE_21 = 1 << 21; uint256 internal constant _ROLE_22 = 1 << 22; uint256 internal constant _ROLE_23 = 1 << 23; uint256 internal constant _ROLE_24 = 1 << 24; uint256 internal constant _ROLE_25 = 1 << 25; uint256 internal constant _ROLE_26 = 1 << 26; uint256 internal constant _ROLE_27 = 1 << 27; uint256 internal constant _ROLE_28 = 1 << 28; uint256 internal constant _ROLE_29 = 1 << 29; uint256 internal constant _ROLE_30 = 1 << 30; uint256 internal constant _ROLE_31 = 1 << 31; uint256 internal constant _ROLE_32 = 1 << 32; uint256 internal constant _ROLE_33 = 1 << 33; uint256 internal constant _ROLE_34 = 1 << 34; uint256 internal constant _ROLE_35 = 1 << 35; uint256 internal constant _ROLE_36 = 1 << 36; uint256 internal constant _ROLE_37 = 1 << 37; uint256 internal constant _ROLE_38 = 1 << 38; uint256 internal constant _ROLE_39 = 1 << 39; uint256 internal constant _ROLE_40 = 1 << 40; uint256 internal constant _ROLE_41 = 1 << 41; uint256 internal constant _ROLE_42 = 1 << 42; uint256 internal constant _ROLE_43 = 1 << 43; uint256 internal constant _ROLE_44 = 1 << 44; uint256 internal constant _ROLE_45 = 1 << 45; uint256 internal constant _ROLE_46 = 1 << 46; uint256 internal constant _ROLE_47 = 1 << 47; uint256 internal constant _ROLE_48 = 1 << 48; uint256 internal constant _ROLE_49 = 1 << 49; uint256 internal constant _ROLE_50 = 1 << 50; uint256 internal constant _ROLE_51 = 1 << 51; uint256 internal constant _ROLE_52 = 1 << 52; uint256 internal constant _ROLE_53 = 1 << 53; uint256 internal constant _ROLE_54 = 1 << 54; uint256 internal constant _ROLE_55 = 1 << 55; uint256 internal constant _ROLE_56 = 1 << 56; uint256 internal constant _ROLE_57 = 1 << 57; uint256 internal constant _ROLE_58 = 1 << 58; uint256 internal constant _ROLE_59 = 1 << 59; uint256 internal constant _ROLE_60 = 1 << 60; uint256 internal constant _ROLE_61 = 1 << 61; uint256 internal constant _ROLE_62 = 1 << 62; uint256 internal constant _ROLE_63 = 1 << 63; uint256 internal constant _ROLE_64 = 1 << 64; uint256 internal constant _ROLE_65 = 1 << 65; uint256 internal constant _ROLE_66 = 1 << 66; uint256 internal constant _ROLE_67 = 1 << 67; uint256 internal constant _ROLE_68 = 1 << 68; uint256 internal constant _ROLE_69 = 1 << 69; uint256 internal constant _ROLE_70 = 1 << 70; uint256 internal constant _ROLE_71 = 1 << 71; uint256 internal constant _ROLE_72 = 1 << 72; uint256 internal constant _ROLE_73 = 1 << 73; uint256 internal constant _ROLE_74 = 1 << 74; uint256 internal constant _ROLE_75 = 1 << 75; uint256 internal constant _ROLE_76 = 1 << 76; uint256 internal constant _ROLE_77 = 1 << 77; uint256 internal constant _ROLE_78 = 1 << 78; uint256 internal constant _ROLE_79 = 1 << 79; uint256 internal constant _ROLE_80 = 1 << 80; uint256 internal constant _ROLE_81 = 1 << 81; uint256 internal constant _ROLE_82 = 1 << 82; uint256 internal constant _ROLE_83 = 1 << 83; uint256 internal constant _ROLE_84 = 1 << 84; uint256 internal constant _ROLE_85 = 1 << 85; uint256 internal constant _ROLE_86 = 1 << 86; uint256 internal constant _ROLE_87 = 1 << 87; uint256 internal constant _ROLE_88 = 1 << 88; uint256 internal constant _ROLE_89 = 1 << 89; uint256 internal constant _ROLE_90 = 1 << 90; uint256 internal constant _ROLE_91 = 1 << 91; uint256 internal constant _ROLE_92 = 1 << 92; uint256 internal constant _ROLE_93 = 1 << 93; uint256 internal constant _ROLE_94 = 1 << 94; uint256 internal constant _ROLE_95 = 1 << 95; uint256 internal constant _ROLE_96 = 1 << 96; uint256 internal constant _ROLE_97 = 1 << 97; uint256 internal constant _ROLE_98 = 1 << 98; uint256 internal constant _ROLE_99 = 1 << 99; uint256 internal constant _ROLE_100 = 1 << 100; uint256 internal constant _ROLE_101 = 1 << 101; uint256 internal constant _ROLE_102 = 1 << 102; uint256 internal constant _ROLE_103 = 1 << 103; uint256 internal constant _ROLE_104 = 1 << 104; uint256 internal constant _ROLE_105 = 1 << 105; uint256 internal constant _ROLE_106 = 1 << 106; uint256 internal constant _ROLE_107 = 1 << 107; uint256 internal constant _ROLE_108 = 1 << 108; uint256 internal constant _ROLE_109 = 1 << 109; uint256 internal constant _ROLE_110 = 1 << 110; uint256 internal constant _ROLE_111 = 1 << 111; uint256 internal constant _ROLE_112 = 1 << 112; uint256 internal constant _ROLE_113 = 1 << 113; uint256 internal constant _ROLE_114 = 1 << 114; uint256 internal constant _ROLE_115 = 1 << 115; uint256 internal constant _ROLE_116 = 1 << 116; uint256 internal constant _ROLE_117 = 1 << 117; uint256 internal constant _ROLE_118 = 1 << 118; uint256 internal constant _ROLE_119 = 1 << 119; uint256 internal constant _ROLE_120 = 1 << 120; uint256 internal constant _ROLE_121 = 1 << 121; uint256 internal constant _ROLE_122 = 1 << 122; uint256 internal constant _ROLE_123 = 1 << 123; uint256 internal constant _ROLE_124 = 1 << 124; uint256 internal constant _ROLE_125 = 1 << 125; uint256 internal constant _ROLE_126 = 1 << 126; uint256 internal constant _ROLE_127 = 1 << 127; uint256 internal constant _ROLE_128 = 1 << 128; uint256 internal constant _ROLE_129 = 1 << 129; uint256 internal constant _ROLE_130 = 1 << 130; uint256 internal constant _ROLE_131 = 1 << 131; uint256 internal constant _ROLE_132 = 1 << 132; uint256 internal constant _ROLE_133 = 1 << 133; uint256 internal constant _ROLE_134 = 1 << 134; uint256 internal constant _ROLE_135 = 1 << 135; uint256 internal constant _ROLE_136 = 1 << 136; uint256 internal constant _ROLE_137 = 1 << 137; uint256 internal constant _ROLE_138 = 1 << 138; uint256 internal constant _ROLE_139 = 1 << 139; uint256 internal constant _ROLE_140 = 1 << 140; uint256 internal constant _ROLE_141 = 1 << 141; uint256 internal constant _ROLE_142 = 1 << 142; uint256 internal constant _ROLE_143 = 1 << 143; uint256 internal constant _ROLE_144 = 1 << 144; uint256 internal constant _ROLE_145 = 1 << 145; uint256 internal constant _ROLE_146 = 1 << 146; uint256 internal constant _ROLE_147 = 1 << 147; uint256 internal constant _ROLE_148 = 1 << 148; uint256 internal constant _ROLE_149 = 1 << 149; uint256 internal constant _ROLE_150 = 1 << 150; uint256 internal constant _ROLE_151 = 1 << 151; uint256 internal constant _ROLE_152 = 1 << 152; uint256 internal constant _ROLE_153 = 1 << 153; uint256 internal constant _ROLE_154 = 1 << 154; uint256 internal constant _ROLE_155 = 1 << 155; uint256 internal constant _ROLE_156 = 1 << 156; uint256 internal constant _ROLE_157 = 1 << 157; uint256 internal constant _ROLE_158 = 1 << 158; uint256 internal constant _ROLE_159 = 1 << 159; uint256 internal constant _ROLE_160 = 1 << 160; uint256 internal constant _ROLE_161 = 1 << 161; uint256 internal constant _ROLE_162 = 1 << 162; uint256 internal constant _ROLE_163 = 1 << 163; uint256 internal constant _ROLE_164 = 1 << 164; uint256 internal constant _ROLE_165 = 1 << 165; uint256 internal constant _ROLE_166 = 1 << 166; uint256 internal constant _ROLE_167 = 1 << 167; uint256 internal constant _ROLE_168 = 1 << 168; uint256 internal constant _ROLE_169 = 1 << 169; uint256 internal constant _ROLE_170 = 1 << 170; uint256 internal constant _ROLE_171 = 1 << 171; uint256 internal constant _ROLE_172 = 1 << 172; uint256 internal constant _ROLE_173 = 1 << 173; uint256 internal constant _ROLE_174 = 1 << 174; uint256 internal constant _ROLE_175 = 1 << 175; uint256 internal constant _ROLE_176 = 1 << 176; uint256 internal constant _ROLE_177 = 1 << 177; uint256 internal constant _ROLE_178 = 1 << 178; uint256 internal constant _ROLE_179 = 1 << 179; uint256 internal constant _ROLE_180 = 1 << 180; uint256 internal constant _ROLE_181 = 1 << 181; uint256 internal constant _ROLE_182 = 1 << 182; uint256 internal constant _ROLE_183 = 1 << 183; uint256 internal constant _ROLE_184 = 1 << 184; uint256 internal constant _ROLE_185 = 1 << 185; uint256 internal constant _ROLE_186 = 1 << 186; uint256 internal constant _ROLE_187 = 1 << 187; uint256 internal constant _ROLE_188 = 1 << 188; uint256 internal constant _ROLE_189 = 1 << 189; uint256 internal constant _ROLE_190 = 1 << 190; uint256 internal constant _ROLE_191 = 1 << 191; uint256 internal constant _ROLE_192 = 1 << 192; uint256 internal constant _ROLE_193 = 1 << 193; uint256 internal constant _ROLE_194 = 1 << 194; uint256 internal constant _ROLE_195 = 1 << 195; uint256 internal constant _ROLE_196 = 1 << 196; uint256 internal constant _ROLE_197 = 1 << 197; uint256 internal constant _ROLE_198 = 1 << 198; uint256 internal constant _ROLE_199 = 1 << 199; uint256 internal constant _ROLE_200 = 1 << 200; uint256 internal constant _ROLE_201 = 1 << 201; uint256 internal constant _ROLE_202 = 1 << 202; uint256 internal constant _ROLE_203 = 1 << 203; uint256 internal constant _ROLE_204 = 1 << 204; uint256 internal constant _ROLE_205 = 1 << 205; uint256 internal constant _ROLE_206 = 1 << 206; uint256 internal constant _ROLE_207 = 1 << 207; uint256 internal constant _ROLE_208 = 1 << 208; uint256 internal constant _ROLE_209 = 1 << 209; uint256 internal constant _ROLE_210 = 1 << 210; uint256 internal constant _ROLE_211 = 1 << 211; uint256 internal constant _ROLE_212 = 1 << 212; uint256 internal constant _ROLE_213 = 1 << 213; uint256 internal constant _ROLE_214 = 1 << 214; uint256 internal constant _ROLE_215 = 1 << 215; uint256 internal constant _ROLE_216 = 1 << 216; uint256 internal constant _ROLE_217 = 1 << 217; uint256 internal constant _ROLE_218 = 1 << 218; uint256 internal constant _ROLE_219 = 1 << 219; uint256 internal constant _ROLE_220 = 1 << 220; uint256 internal constant _ROLE_221 = 1 << 221; uint256 internal constant _ROLE_222 = 1 << 222; uint256 internal constant _ROLE_223 = 1 << 223; uint256 internal constant _ROLE_224 = 1 << 224; uint256 internal constant _ROLE_225 = 1 << 225; uint256 internal constant _ROLE_226 = 1 << 226; uint256 internal constant _ROLE_227 = 1 << 227; uint256 internal constant _ROLE_228 = 1 << 228; uint256 internal constant _ROLE_229 = 1 << 229; uint256 internal constant _ROLE_230 = 1 << 230; uint256 internal constant _ROLE_231 = 1 << 231; uint256 internal constant _ROLE_232 = 1 << 232; uint256 internal constant _ROLE_233 = 1 << 233; uint256 internal constant _ROLE_234 = 1 << 234; uint256 internal constant _ROLE_235 = 1 << 235; uint256 internal constant _ROLE_236 = 1 << 236; uint256 internal constant _ROLE_237 = 1 << 237; uint256 internal constant _ROLE_238 = 1 << 238; uint256 internal constant _ROLE_239 = 1 << 239; uint256 internal constant _ROLE_240 = 1 << 240; uint256 internal constant _ROLE_241 = 1 << 241; uint256 internal constant _ROLE_242 = 1 << 242; uint256 internal constant _ROLE_243 = 1 << 243; uint256 internal constant _ROLE_244 = 1 << 244; uint256 internal constant _ROLE_245 = 1 << 245; uint256 internal constant _ROLE_246 = 1 << 246; uint256 internal constant _ROLE_247 = 1 << 247; uint256 internal constant _ROLE_248 = 1 << 248; uint256 internal constant _ROLE_249 = 1 << 249; uint256 internal constant _ROLE_250 = 1 << 250; uint256 internal constant _ROLE_251 = 1 << 251; uint256 internal constant _ROLE_252 = 1 << 252; uint256 internal constant _ROLE_253 = 1 << 253; uint256 internal constant _ROLE_254 = 1 << 254; uint256 internal constant _ROLE_255 = 1 << 255; }lib/solady/src/utils/ECDSA.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Gas optimized ECDSA wrapper. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol) /// /// @dev Note: /// - The recovery functions use the ecrecover precompile (0x1). /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure. /// This is for more safety by default. /// Use the `tryRecover` variants if you need to get the zero address back /// upon recovery failure instead. /// - As of Solady version 0.0.134, all `bytes signature` variants accept both /// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures. /// See: https://eips.ethereum.org/EIPS/eip-2098 /// This is for calldata efficiency on smart accounts prevalent on L2s. /// /// WARNING! Do NOT directly use signatures as unique identifiers: /// - The recovery operations do NOT check if a signature is non-malleable. /// - Use a nonce in the digest to prevent replay attacks on the same contract. /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts. /// EIP-712 also enables readable signing of typed data for better user safety. /// - If you need a unique hash from a signature, please use the `canonicalHash` functions. library ECDSA { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The order of the secp256k1 elliptic curve. uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; /// @dev `N/2 + 1`. Used for checking the malleability of the signature. uint256 private constant _HALF_N_PLUS_1 = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The signature is invalid. error InvalidSignature(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RECOVERY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function recover(bytes32 hash, bytes memory signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { for { let m := mload(0x40) } 1 { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } { switch mload(signature) case 64 { let vs := mload(add(signature, 0x40)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x60, shr(1, shl(1, vs))) // `s`. } case 65 { mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. mstore(0x60, mload(add(signature, 0x40))) // `s`. } default { continue } mstore(0x00, hash) mstore(0x40, mload(add(signature, 0x20))) // `r`. result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20)) mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if returndatasize() { break } } } } /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function recoverCalldata(bytes32 hash, bytes calldata signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { for { let m := mload(0x40) } 1 { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } { switch signature.length case 64 { let vs := calldataload(add(signature.offset, 0x20)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, calldataload(signature.offset)) // `r`. mstore(0x60, shr(1, shl(1, vs))) // `s`. } case 65 { mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`. } default { continue } mstore(0x00, hash) result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20)) mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if returndatasize() { break } } } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the EIP-2098 short form signature defined by `r` and `vs`. function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, r) mstore(0x60, shr(1, shl(1, vs))) // `s`. result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20)) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the signature defined by `v`, `r`, `s`. function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, and(v, 0xff)) mstore(0x40, r) mstore(0x60, s) result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20)) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(returndatasize()) { mstore(0x00, 0x8baa579f) // `InvalidSignature()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* TRY-RECOVER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // WARNING! // These functions will NOT revert upon recovery failure. // Instead, they will return the zero address upon recovery failure. // It is critical that the returned address is NEVER compared against // a zero address (e.g. an uninitialized address variable). /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function tryRecover(bytes32 hash, bytes memory signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { for { let m := mload(0x40) } 1 {} { switch mload(signature) case 64 { let vs := mload(add(signature, 0x40)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x60, shr(1, shl(1, vs))) // `s`. } case 65 { mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. mstore(0x60, mload(add(signature, 0x40))) // `s`. } default { break } mstore(0x00, hash) mstore(0x40, mload(add(signature, 0x20))) // `r`. pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20)) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. break } } } /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. function tryRecoverCalldata(bytes32 hash, bytes calldata signature) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { for { let m := mload(0x40) } 1 {} { switch signature.length case 64 { let vs := calldataload(add(signature.offset, 0x20)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, calldataload(signature.offset)) // `r`. mstore(0x60, shr(1, shl(1, vs))) // `s`. } case 65 { mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`. } default { break } mstore(0x00, hash) pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20)) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. break } } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the EIP-2098 short form signature defined by `r` and `vs`. function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, r) mstore(0x60, shr(1, shl(1, vs))) // `s`. pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20)) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Recovers the signer's address from a message digest `hash`, /// and the signature defined by `v`, `r`, `s`. function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, hash) mstore(0x20, and(v, 0xff)) mstore(0x40, r) mstore(0x60, s) pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20)) mstore(0x60, 0) // Restore the zero slot. // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. result := mload(xor(0x60, returndatasize())) mstore(0x40, m) // Restore the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HASHING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an Ethereum Signed Message, created from a `hash`. /// This produces a hash corresponding to the one signed with the /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign) /// JSON-RPC method as part of EIP-191. function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { mstore(0x20, hash) // Store into scratch space for keccak256. mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes. result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`. } } /// @dev Returns an Ethereum Signed Message, created from `s`. /// This produces a hash corresponding to the one signed with the /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign) /// JSON-RPC method as part of EIP-191. /// Note: Supports lengths of `s` up to 999999 bytes. function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let sLength := mload(s) let o := 0x20 mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded. mstore(0x00, 0x00) // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`. for { let temp := sLength } 1 {} { o := sub(o, 1) mstore8(o, add(48, mod(temp, 10))) temp := div(temp, 10) if iszero(temp) { break } } let n := sub(0x3a, o) // Header length: `26 + 32 - o`. // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes. returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20)) mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header. result := keccak256(add(s, sub(0x20, n)), add(n, sLength)) mstore(s, sLength) // Restore the length. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CANONICAL HASH FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // The following functions returns the hash of the signature in it's canonicalized format, // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28. // If `s` is greater than `N / 2` then it will be converted to `N - s` // and the `v` value will be flipped. // If the signature has an invalid length, or if `v` is invalid, // a uniquely corrupt hash will be returned. // These functions are useful for "poor-mans-VRF". /// @dev Returns the canonical hash of `signature`. function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let l := mload(signature) for {} 1 {} { mstore(0x00, mload(add(signature, 0x20))) // `r`. let s := mload(add(signature, 0x40)) let v := mload(add(signature, 0x41)) if eq(l, 64) { v := add(shr(255, s), 27) s := shr(1, shl(1, s)) } if iszero(lt(s, _HALF_N_PLUS_1)) { v := xor(v, 7) s := sub(N, s) } mstore(0x21, v) mstore(0x20, s) result := keccak256(0x00, 0x41) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. break } // If the length is neither 64 nor 65, return a uniquely corrupted hash. if iszero(lt(sub(l, 64), 2)) { // `bytes4(keccak256("InvalidSignatureLength"))`. result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2) } } } /// @dev Returns the canonical hash of `signature`. function canonicalHashCalldata(bytes calldata signature) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { mstore(0x00, calldataload(signature.offset)) // `r`. let s := calldataload(add(signature.offset, 0x20)) let v := calldataload(add(signature.offset, 0x21)) if eq(signature.length, 64) { v := add(shr(255, s), 27) s := shr(1, shl(1, s)) } if iszero(lt(s, _HALF_N_PLUS_1)) { v := xor(v, 7) s := sub(N, s) } mstore(0x21, v) mstore(0x20, s) result := keccak256(0x00, 0x41) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. break } // If the length is neither 64 nor 65, return a uniquely corrupted hash. if iszero(lt(sub(signature.length, 64), 2)) { calldatacopy(mload(0x40), signature.offset, signature.length) // `bytes4(keccak256("InvalidSignatureLength"))`. result := xor(keccak256(mload(0x40), signature.length), 0xd62f1ab2) } } } /// @dev Returns the canonical hash of `signature`. function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, r) // `r`. let v := add(shr(255, vs), 27) let s := shr(1, shl(1, vs)) mstore(0x21, v) mstore(0x20, s) result := keccak256(0x00, 0x41) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. } } /// @dev Returns the canonical hash of `signature`. function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, r) // `r`. if iszero(lt(s, _HALF_N_PLUS_1)) { v := xor(v, 7) s := sub(N, s) } mstore(0x21, v) mstore(0x20, s) result := keccak256(0x00, 0x41) mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EMPTY CALLDATA HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an empty calldata bytes. function emptySignature() internal pure returns (bytes calldata signature) { /// @solidity memory-safe-assembly assembly { signature.length := 0 } } }lib/solady/src/utils/MerkleProofLib.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol) library MerkleProofLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MERKLE PROOF VERIFICATION OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool isValid) { /// @solidity memory-safe-assembly assembly { if mload(proof) { // Initialize `offset` to the offset of `proof` elements in memory. let offset := add(proof, 0x20) // Left shift by 5 is equivalent to multiplying by 0x20. let end := add(offset, shl(5, mload(proof))) // Iterate over proof elements to compute root hash. for {} 1 {} { // Slot of `leaf` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(leaf, mload(offset))) // Store elements to hash contiguously in scratch space. // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes. mstore(scratch, leaf) mstore(xor(scratch, 0x20), mload(offset)) // Reuse `leaf` to store the hash to reduce stack operations. leaf := keccak256(0x00, 0x40) offset := add(offset, 0x20) if iszero(lt(offset, end)) { break } } } isValid := eq(leaf, root) } } /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool isValid) { /// @solidity memory-safe-assembly assembly { if proof.length { // Left shift by 5 is equivalent to multiplying by 0x20. let end := add(proof.offset, shl(5, proof.length)) // Initialize `offset` to the offset of `proof` in the calldata. let offset := proof.offset // Iterate over proof elements to compute root hash. for {} 1 {} { // Slot of `leaf` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(leaf, calldataload(offset))) // Store elements to hash contiguously in scratch space. // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes. mstore(scratch, leaf) mstore(xor(scratch, 0x20), calldataload(offset)) // Reuse `leaf` to store the hash to reduce stack operations. leaf := keccak256(0x00, 0x40) offset := add(offset, 0x20) if iszero(lt(offset, end)) { break } } } isValid := eq(leaf, root) } } /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`, /// given `proof` and `flags`. /// /// Note: /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` /// will always return false. /// - The sum of the lengths of `proof` and `leaves` must never overflow. /// - Any non-zero word in the `flags` array is treated as true. /// - The memory offset of `proof` must be non-zero /// (i.e. `proof` is not pointing to the scratch space). function verifyMultiProof( bytes32[] memory proof, bytes32 root, bytes32[] memory leaves, bool[] memory flags ) internal pure returns (bool isValid) { // Rebuilds the root by consuming and producing values on a queue. // The queue starts with the `leaves` array, and goes into a `hashes` array. // After the process, the last element on the queue is verified // to be equal to the `root`. // // The `flags` array denotes whether the sibling // should be popped from the queue (`flag == true`), or // should be popped from the `proof` (`flag == false`). /// @solidity memory-safe-assembly assembly { // Cache the lengths of the arrays. let leavesLength := mload(leaves) let proofLength := mload(proof) let flagsLength := mload(flags) // Advance the pointers of the arrays to point to the data. leaves := add(0x20, leaves) proof := add(0x20, proof) flags := add(0x20, flags) // If the number of flags is correct. for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} { // For the case where `proof.length + leaves.length == 1`. if iszero(flagsLength) { // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`. isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root) break } // The required final proof offset if `flagsLength` is not zero, otherwise zero. let proofEnd := add(proof, shl(5, proofLength)) // We can use the free memory space for the queue. // We don't need to allocate, since the queue is temporary. let hashesFront := mload(0x40) // Copy the leaves into the hashes. // Sometimes, a little memory expansion costs less than branching. // Should cost less, even with a high free memory offset of 0x7d00. leavesLength := shl(5, leavesLength) for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } { mstore(add(hashesFront, i), mload(add(leaves, i))) } // Compute the back of the hashes. let hashesBack := add(hashesFront, leavesLength) // This is the end of the memory for the queue. // We recycle `flagsLength` to save on stack variables (sometimes save gas). flagsLength := add(hashesBack, shl(5, flagsLength)) for {} 1 {} { // Pop from `hashes`. let a := mload(hashesFront) // Pop from `hashes`. let b := mload(add(hashesFront, 0x20)) hashesFront := add(hashesFront, 0x40) // If the flag is false, load the next proof, // else, pops from the queue. if iszero(mload(flags)) { // Loads the next proof. b := mload(proof) proof := add(proof, 0x20) // Unpop from `hashes`. hashesFront := sub(hashesFront, 0x20) } // Advance to the next flag. flags := add(flags, 0x20) // Slot of `a` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(a, b)) // Hash the scratch space and push the result onto the queue. mstore(scratch, a) mstore(xor(scratch, 0x20), b) mstore(hashesBack, keccak256(0x00, 0x40)) hashesBack := add(hashesBack, 0x20) if iszero(lt(hashesBack, flagsLength)) { break } } isValid := and( // Checks if the last value in the queue is same as the root. eq(mload(sub(hashesBack, 0x20)), root), // And whether all the proofs are used, if required. eq(proofEnd, proof) ) break } } } /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`, /// given `proof` and `flags`. /// /// Note: /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` /// will always return false. /// - Any non-zero word in the `flags` array is treated as true. /// - The calldata offset of `proof` must be non-zero /// (i.e. `proof` is from a regular Solidity function with a 4-byte selector). function verifyMultiProofCalldata( bytes32[] calldata proof, bytes32 root, bytes32[] calldata leaves, bool[] calldata flags ) internal pure returns (bool isValid) { // Rebuilds the root by consuming and producing values on a queue. // The queue starts with the `leaves` array, and goes into a `hashes` array. // After the process, the last element on the queue is verified // to be equal to the `root`. // // The `flags` array denotes whether the sibling // should be popped from the queue (`flag == true`), or // should be popped from the `proof` (`flag == false`). /// @solidity memory-safe-assembly assembly { // If the number of flags is correct. for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} { // For the case where `proof.length + leaves.length == 1`. if iszero(flags.length) { // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`. // forgefmt: disable-next-item isValid := eq( calldataload( xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length)) ), root ) break } // The required final proof offset if `flagsLength` is not zero, otherwise zero. let proofEnd := add(proof.offset, shl(5, proof.length)) // We can use the free memory space for the queue. // We don't need to allocate, since the queue is temporary. let hashesFront := mload(0x40) // Copy the leaves into the hashes. // Sometimes, a little memory expansion costs less than branching. // Should cost less, even with a high free memory offset of 0x7d00. calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length)) // Compute the back of the hashes. let hashesBack := add(hashesFront, shl(5, leaves.length)) // This is the end of the memory for the queue. // We recycle `flagsLength` to save on stack variables (sometimes save gas). flags.length := add(hashesBack, shl(5, flags.length)) // We don't need to make a copy of `proof.offset` or `flags.offset`, // as they are pass-by-value (this trick may not always save gas). for {} 1 {} { // Pop from `hashes`. let a := mload(hashesFront) // Pop from `hashes`. let b := mload(add(hashesFront, 0x20)) hashesFront := add(hashesFront, 0x40) // If the flag is false, load the next proof, // else, pops from the queue. if iszero(calldataload(flags.offset)) { // Loads the next proof. b := calldataload(proof.offset) proof.offset := add(proof.offset, 0x20) // Unpop from `hashes`. hashesFront := sub(hashesFront, 0x20) } // Advance to the next flag offset. flags.offset := add(flags.offset, 0x20) // Slot of `a` in scratch space. // If the condition is true: 0x20, otherwise: 0x00. let scratch := shl(5, gt(a, b)) // Hash the scratch space and push the result onto the queue. mstore(scratch, a) mstore(xor(scratch, 0x20), b) mstore(hashesBack, keccak256(0x00, 0x40)) hashesBack := add(hashesBack, 0x20) if iszero(lt(hashesBack, flags.length)) { break } } isValid := and( // Checks if the last value in the queue is same as the root. eq(mload(sub(hashesBack, 0x20)), root), // And whether all the proofs are used, if required. eq(proofEnd, proof.offset) ) break } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EMPTY CALLDATA HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns an empty calldata bytes32 array. function emptyProof() internal pure returns (bytes32[] calldata proof) { /// @solidity memory-safe-assembly assembly { proof.length := 0 } } /// @dev Returns an empty calldata bytes32 array. function emptyLeaves() internal pure returns (bytes32[] calldata leaves) { /// @solidity memory-safe-assembly assembly { leaves.length := 0 } } /// @dev Returns an empty calldata bool array. function emptyFlags() internal pure returns (bool[] calldata flags) { /// @solidity memory-safe-assembly assembly { flags.length := 0 } } }src/ICliqueLock.sol
// SPDX-License-Identifier: MIT pragma solidity 0.8.25; struct StreamConfig { address recipient; address revokeAuthority; address token; uint256 amount; uint256 startTime; uint256 cliffTime; uint256 endTime; uint256 startUnlockPercentage; uint256 cliffUnlockPercentage; uint256 pieceDuration; address verifier; uint256 id; } /** * @dev Stream struct containing all parameters for a token stream * @param recipient Address that will receive the streamed tokens * @param revokeAuthority Address with permission to revoke the stream * @param token Address of the ERC20 token being streamed * @param amount Total amount of tokens in the stream * @param claimedAmount Amount of tokens already claimed * @param startTime Timestamp when the stream begins * @param cliffTime Timestamp when the cliff period ends * @param endTime Timestamp when the stream ends * @param startUnlockPercentage Percentage of tokens unlocked at start (18 decimals) * @param cliffUnlockPercentage Percentage of tokens unlocked at cliff (18 decimals) * @param pieceDuration Duration of each vesting piece (0 for linear vesting) */ struct Stream { address recipient; address revokeAuthority; address token; uint256 amount; uint256 claimedAmount; uint256 startTime; uint256 cliffTime; uint256 endTime; uint256 startUnlockPercentage; uint256 cliffUnlockPercentage; uint256 pieceDuration; } struct Badge { address verifier; uint256 id; } interface IVerifier { function ownerOf(uint256 id) external view returns (address); } interface ICliqueLock { // EXTERNAL FUNCTIONS - STREAM CREATION /** * @notice Creates a stream using standard ERC20 approve * @dev Requires prior approval of token transfer * @param config StreamConfig struct containing the stream parameters * @return streamId Unique identifier of the created stream */ function createStream(StreamConfig calldata config) external returns (uint256 streamId); // EXTERNAL FUNCTIONS - STREAM REVOCATION /** * @notice Revokes a stream, returning unclaimed tokens to the revokeAuthority * @dev Can only be called by the designated revokeAuthority * @param streamId ID of the stream to revoke */ function revokeStream(uint256 streamId) external; /** * @notice Returns the stream information for a given streamId * @param streamId ID of the stream to query * @return stream Stream struct containing the stream information */ function streams(uint256 streamId) external view returns (Stream memory stream); }
ABI · 59 entries
json
[
{
"type": "constructor",
"inputs": [
{
"name": "_signer",
"type": "address",
"internalType": "address"
},
{
"name": "_token",
"type": "address",
"internalType": "address"
},
{
"name": "_projectAdmin",
"type": "address",
"internalType": "address"
},
{
"name": "_vault",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "nonpayable"
},
{
"name": "AlreadyClaimed",
"type": "error",
"inputs": []
},
{
"name": "AlreadyInitialized",
"type": "error",
"inputs": []
},
{
"name": "InvalidMerkleProof",
"type": "error",
"inputs": [
{
"name": "root",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"name": "InvalidMerkleRoot",
"type": "error",
"inputs": []
},
{
"name": "InvalidReleasedAmount",
"type": "error",
"inputs": []
},
{
"name": "InvalidSignature",
"type": "error",
"inputs": []
},
{
"name": "NewOwnerIsZeroAddress",
"type": "error",
"inputs": []
},
{
"name": "NoHandoverRequest",
"type": "error",
"inputs": []
},
{
"name": "NoNftToClaim",
"type": "error",
"inputs": []
},
{
"name": "NotActive",
"type": "error",
"inputs": []
},
{
"name": "SafeERC20FailedOperation",
"type": "error",
"inputs": [
{
"name": "token",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "Unauthorized",
"type": "error",
"inputs": []
},
{
"name": "WithdrawFailed",
"type": "error",
"inputs": []
},
{
"name": "ZeroAddress",
"type": "error",
"inputs": []
},
{
"name": "AirdropClaimed",
"type": "event",
"inputs": [
{
"name": "account",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "root",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "FeeSet",
"type": "event",
"inputs": [
{
"name": "fee",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "MerkleConfigurationUpdate",
"type": "event",
"inputs": [
{
"name": "root",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "configurator",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
}
],
"anonymous": false
},
{
"name": "OwnershipHandoverCanceled",
"type": "event",
"inputs": [
{
"name": "pendingOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "OwnershipHandoverRequested",
"type": "event",
"inputs": [
{
"name": "pendingOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "OwnershipTransferred",
"type": "event",
"inputs": [
{
"name": "oldOwner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "newOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "RolesUpdated",
"type": "event",
"inputs": [
{
"name": "user",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "roles",
"type": "uint256",
"indexed": true,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "VaultSet",
"type": "event",
"inputs": [
{
"name": "vault",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "Withdraw",
"type": "event",
"inputs": [
{
"name": "to",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "fallback",
"stateMutability": "payable"
},
{
"name": "PROJECT_ADMIN",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "active",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "cancelOwnershipHandover",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "payable"
},
{
"name": "claim",
"type": "function",
"inputs": [
{
"name": "_proof",
"type": "bytes32[]",
"internalType": "bytes32[]"
},
{
"name": "_signature",
"type": "bytes",
"internalType": "bytes"
},
{
"name": "_amount",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_released",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_onBehalfOf",
"type": "address",
"internalType": "address"
},
{
"name": "_extra",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [],
"stateMutability": "payable"
},
{
"name": "claimWithNft",
"type": "function",
"inputs": [
{
"name": "_nfts",
"type": "address[]",
"internalType": "address[]"
},
{
"name": "_tokenIds",
"type": "uint256[]",
"internalType": "uint256[]"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "claimed",
"type": "function",
"inputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "lastClaimedBlock",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "claimedAmount",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "completeOwnershipHandover",
"type": "function",
"inputs": [
{
"name": "pendingOwner",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "payable"
},
{
"name": "fee",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "grantRoles",
"type": "function",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
},
{
"name": "roles",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "payable"
},
{
"name": "hasAllRoles",
"type": "function",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
},
{
"name": "roles",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "hasAnyRole",
"type": "function",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
},
{
"name": "roles",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "merkleConfiguration",
"type": "function",
"inputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"name": "nftAllocation",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "nftClaimed",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "owner",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "result",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "ownershipHandoverExpiresAt",
"type": "function",
"inputs": [
{
"name": "pendingOwner",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "result",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "renounceOwnership",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "payable"
},
{
"name": "renounceRoles",
"type": "function",
"inputs": [
{
"name": "roles",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "payable"
},
{
"name": "requestOwnershipHandover",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "payable"
},
{
"name": "revokeRoles",
"type": "function",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
},
{
"name": "roles",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "payable"
},
{
"name": "rolesOf",
"type": "function",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "roles",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "setClaimRoot",
"type": "function",
"inputs": [
{
"name": "_claimRoot",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "_configurator",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setFee",
"type": "function",
"inputs": [
{
"name": "_fee",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setNftAllocation",
"type": "function",
"inputs": [
{
"name": "_nfts",
"type": "address[]",
"internalType": "address[]"
},
{
"name": "_allocations",
"type": "uint256[]",
"internalType": "uint256[]"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setSigner",
"type": "function",
"inputs": [
{
"name": "_signer",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setVault",
"type": "function",
"inputs": [
{
"name": "_vault",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "signer",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "toggleActive",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "token",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "transferOwnership",
"type": "function",
"inputs": [
{
"name": "newOwner",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "payable"
},
{
"name": "vault",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "version",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "string",
"internalType": "string"
}
],
"stateMutability": "pure"
},
{
"name": "withdrawFee",
"type": "function",
"inputs": [
{
"name": "_recipient",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "receive",
"stateMutability": "payable"
}
]