Feature Tip: Add private address tag to any address under My Name Tag !
More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 8 internal transactions
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
20937736 | 73 days ago | 0.01361874 ETH | ||||
20937661 | 73 days ago | 0.01583012 ETH | ||||
20931529 | 74 days ago | 0.013566 ETH | ||||
20931489 | 74 days ago | 0.0043067 ETH | ||||
20931480 | 74 days ago | 0.00191035 ETH | ||||
20931377 | 74 days ago | 0.04016936 ETH | ||||
20931377 | 74 days ago | Contract Creation | 0 ETH | |||
20918313 | 76 days ago | 40.72180527 ETH |
Loading...
Loading
Minimal Proxy Contract for 0x15ec0fa66a0d96a64d67c58368641fbe9325f3fa
Contract Name:
SmartVault
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 5000000 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.23; import { UserOperationLib } from "../library/UserOperationLib.sol"; import { MultiSignerLib } from "../signers/MultiSigner.sol"; import { Signer } from "../signers/Signer.sol"; import { ERC1271 } from "../utils/ERC1271.sol"; import { FallbackManager } from "../utils/FallbackManager.sol"; import { ModuleManager } from "../utils/ModuleManager.sol"; import { MultiSignerAuth } from "../utils/MultiSignerAuth.sol"; import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; import { IAccount } from "account-abstraction/interfaces/IAccount.sol"; import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol"; import { Ownable } from "solady/auth/Ownable.sol"; import { UUPSUpgradeable } from "solady/utils/UUPSUpgradeable.sol"; /** * @title Splits Smart Accounts/Vaults * @custom:security-contract [email protected] * @author Splits (https://splits.org) * @dev Based on Coinbase's Smart Wallet (https://github.com/coinbase/smart-wallet) and Solady's Smart Wallet. */ contract SmartVault is IAccount, Ownable, UUPSUpgradeable, MultiSignerAuth, ERC1271, FallbackManager, ModuleManager { using UserOperationLib for PackedUserOperation; /* -------------------------------------------------------------------------- */ /* STRUCTS */ /* -------------------------------------------------------------------------- */ /// @notice Primary Signature types enum SignatureTypes { SingleUserOp, MerkelizedUserOp, ERC1271 } /// @notice Upper limits for maxPriorityFeePerGas, preVerificationGas, verificationGasLimit, callGasLimit, /// paymasterVerificationGasLimit and paymasterPostOpGasLimit that should be charged by the userOp. This is included /// in the light userOp hash to ensure last signer does not exceed the specified gas price/limits. These values will /// be ignored when threshold is 1. paymaster, paymasterVerificationGasLimit and paymasterPostOpGasLimit will be /// ignored if paymasterAndData is empty. struct LightUserOpGasLimits { uint256 maxPriorityFeePerGas; uint256 preVerificationGas; uint256 callGasLimit; uint256 verificationGasLimit; address paymaster; uint256 paymasterVerificationGasLimit; uint256 paymasterPostOpGasLimit; } /// @notice Single User Op Signature Scheme. struct SingleUserOpSignature { /// @notice light user op gas limits. LightUserOpGasLimits gasLimits; /// @notice list of signatures where threshold - 1 /// signatures will be verified against the light userOp hash and the final signature will be verified /// against the userOp hash. MultiSignerLib.SignatureWrapper[] signatures; } /// @notice Merkelized User Op Signature Scheme. struct MerkelizedUserOpSignature { /// @notice light user op gas limits. LightUserOpGasLimits gasLimits; /// @notice merkleRoot of all the light(userOp) in the Merkle Tree. If threshold is 1, this will be /// bytes32(0). bytes32 lightMerkleTreeRoot; /// @notice Proof to verify if the light userOp hash is present in the light merkle tree root. If /// threshold is 1, this will be empty. bytes32[] lightMerkleProof; /// @notice merkleRoot of all the user ops in the Merkle Tree. bytes32 merkleTreeRoot; /// @notice Proof to verify if the userOp hash is present in the merkle tree. bytes32[] merkleProof; /// @notice list of signatures where threshold - 1 /// signatures will be verified against the `lightMerkleTreeRoot` and the final signature will be verified /// against the `merkleTreeRoot`. MultiSignerLib.SignatureWrapper[] signatures; } /// @notice ERC1271 Signature scheme struct ERC1271Signature { MultiSignerLib.SignatureWrapper[] signatures; } /* -------------------------------------------------------------------------- */ /* CONSTANTS */ /* -------------------------------------------------------------------------- */ /// @notice Splits smart vaults factory. address public immutable FACTORY; /* -------------------------------------------------------------------------- */ /* ERRORS */ /* -------------------------------------------------------------------------- */ /// @notice Thrown when caller is not entry point. error OnlyEntryPoint(); /// @notice Thrown when caller is not factory. error OnlyFactory(); /// @notice Thrown when caller is not address(this). error OnlySelf(); /// @notice Thrown when contract creation has failed. error FailedContractCreation(); /// @notice Thrown when Signature is of unknown type. error InvalidSignatureType(); /// @notice Thrown when LightUserOpGasLimits have been breached. error InvalidGasLimits(); /// @notice Thrown when Paymaster LightUserOpGasLimits have been breached. error InvalidPaymasterData(); /* -------------------------------------------------------------------------- */ /* MODIFIERS */ /* -------------------------------------------------------------------------- */ /// @notice Reverts if the caller is not the EntryPoint. modifier onlyEntryPoint() virtual { if (msg.sender != entryPoint()) { revert OnlyEntryPoint(); } _; } /// @notice Reverts if the caller is neither the EntryPoint or the owner. modifier onlyEntryPointOrOwner() virtual { if (msg.sender != entryPoint()) { _checkOwner(); } _; } /// @notice Reverts when caller is not this account. modifier onlySelf() virtual { if (msg.sender != address(this)) { revert OnlySelf(); } _; } /** * @notice Sends to the EntryPoint (i.e. `msg.sender`) the missing funds for this transaction. * * @dev Subclass MAY override this modifier for better funds management (e.g. send to the * EntryPoint more than the minimum required, so that in future transactions it will not * be required to send again). * * @param missingAccountFunds_ The minimum value this modifier should send the EntryPoint which * MAY be zero, in case there is enough deposit, or the userOp has a * paymaster. */ modifier payPrefund(uint256 missingAccountFunds_) virtual { _; assembly ("memory-safe") { if missingAccountFunds_ { // Ignore failure (it's EntryPoint's job to verify, not the account's). pop(call(gas(), caller(), missingAccountFunds_, codesize(), 0x00, codesize(), 0x00)) } } } /* -------------------------------------------------------------------------- */ /* CONSTRUCTOR */ /* -------------------------------------------------------------------------- */ constructor() ERC1271("splitsSmartVault", "1") { FACTORY = msg.sender; } /* -------------------------------------------------------------------------- */ /* EXTERNAL/PUBLIC FUNCTIONS */ /* -------------------------------------------------------------------------- */ /** * @notice Initializes the account with the `signers` and `threshold`. * * @dev Reverts if caller is not factory. * @dev Reverts if signers or threshold is invalid. * * @param owner_ Owner of the smart account. * @param signers_ Array of initial signers. Each signer is of type `Signer`. * @param threshold_ Number of signers required to approve a signature. */ function initialize(address owner_, Signer[] calldata signers_, uint8 threshold_) external payable { if (msg.sender != FACTORY) revert OnlyFactory(); _initializeOwner(owner_); _initializeMultiSignerAuth(signers_, threshold_); } /** * Validate user's signature and nonce * the entryPoint will make the call to the recipient only if this validation call returns successfully. * signature failure should be reported by returning SIG_VALIDATION_FAILED (1). * This allows making a "simulation call" without a valid signature * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert. * * @dev Must validate caller is the entryPoint. * Must validate the signature and nonce * * @param userOp_ - The operation that is about to be executed. * @param userOpHash_ - Hash of the user's request data. can be used as the basis for signature. * @param missingAccountFunds_ - Missing funds on the account's deposit in the entrypoint. * This is the minimum amount to transfer to the sender(entryPoint) to be * able to make the call. The excess is left as a deposit in the entrypoint * for future calls. Can be withdrawn anytime using "entryPoint.withdrawTo()". * In case there is a paymaster in the request (or the current deposit is high * enough), this value will be zero. * @return validationData - Packaged ValidationData structure. use `_packValidationData` and * `_unpackValidationData` to encode and decode. * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, * otherwise, an address of an "authorizer" contract. * <6-byte> validUntil - Last timestamp this operation is valid. 0 for "indefinite" * <6-byte> validAfter - First timestamp this operation is valid * If an account doesn't use time-range, it is enough to * return SIG_VALIDATION_FAILED value (1) for signature failure. * Note that the validation code cannot use block.timestamp (or block.number) directly. */ function validateUserOp( PackedUserOperation calldata userOp_, bytes32 userOpHash_, uint256 missingAccountFunds_ ) external onlyEntryPoint payPrefund(missingAccountFunds_) returns (uint256 validationData) { SignatureTypes signatureType = _getSignatureType(userOp_.signature[0]); bytes32 lightHash; if (signatureType == SignatureTypes.SingleUserOp) { SingleUserOpSignature memory signature = abi.decode(userOp_.signature[1:], (SingleUserOpSignature)); // if threshold is greater than 1, `threshold - 1` signers will sign over the light userOp hash. We lazily // calculate light userOp hash based on number of signatures. If threshold is 1 then light userOp hash // won't be needed. if (signature.signatures.length > 1) { _verifyGasLimits(userOp_, signature.gasLimits); lightHash = _getLightUserOpHash(userOp_, signature.gasLimits); } return _validateSingleUserOp(lightHash, userOpHash_, signature); } else if (signatureType == SignatureTypes.MerkelizedUserOp) { MerkelizedUserOpSignature memory signature = abi.decode(userOp_.signature[1:], (MerkelizedUserOpSignature)); // if threshold is greater than 1, `threshold - 1` signers will sign over the merkle tree root of light user // op hash(s). We lazily calculate light userOp hash based on number of signatures. If threshold // is 1 then light userOp hash won't be needed. if (signature.signatures.length > 1) { _verifyGasLimits(userOp_, signature.gasLimits); lightHash = _getLightUserOpHash(userOp_, signature.gasLimits); } return _validateMerkelizedUserOp(lightHash, userOpHash_, signature); } else { revert InvalidSignatureType(); } } /** * @notice Executes the given call from this account. * * @dev Can only be called by the Entrypoint or owner of this account. * * @param call_ The `Call` to execute. */ function execute(Call calldata call_) external payable onlyEntryPointOrOwner { _call(call_); } /** * @notice Executes batch of `Call`s. * * @dev Can only be called by the Entrypoint or owner of this account. * * @param calls_ The list of `Call`s to execute. */ function executeBatch(Call[] calldata calls_) external payable onlyEntryPointOrOwner { uint256 numCalls = calls_.length; for (uint256 i; i < numCalls; i++) { _call(calls_[i]); } } /// @notice Returns the address of the EntryPoint v0.7. function entryPoint() public view virtual returns (address) { return 0x0000000071727De22E5E9d8BAf0edAc6f37da032; } /** * @notice Returns the implementation of the ERC1967 proxy. * * @return implementation The address of implementation contract. */ function getImplementation() public view returns (address implementation) { assembly { implementation := sload(_ERC1967_IMPLEMENTATION_SLOT) } } /** * @notice Forked from CreateX. * * @dev Deploys a new contract using the `CREATE` opcode and using the creation * bytecode `initCode` and `msg.value` as inputs. In order to save deployment costs, * we do not sanity check the `initCode` length. Note that if `msg.value` is non-zero, * `initCode` must have a `payable` constructor. * @dev Can only be called by this contract. * @dev Reverts when new contract is address(0) or code.length is zero. * * @param initCode_ The creation bytecode. * @return newContract The 20-byte address where the contract was deployed. */ function deployCreate(bytes memory initCode_) external payable onlySelf returns (address newContract) { assembly ("memory-safe") { newContract := create(callvalue(), add(initCode_, 0x20), mload(initCode_)) } if (newContract == address(0) || newContract.code.length == 0) { revert FailedContractCreation(); } } /* -------------------------------------------------------------------------- */ /* INTERNAL FUNCTIONS */ /* -------------------------------------------------------------------------- */ /// @dev authorizes caller to upgrade the implementation of this contract. function _authorizeUpgrade(address) internal view virtual override(UUPSUpgradeable) onlyOwner { } /// @dev authorizes caller to update signer set, fallback handlers and modules. /// @dev can only be called by this contract. function _authorize() internal view override(MultiSignerAuth, FallbackManager, ModuleManager) onlySelf { } /** * @dev Conditions for a valid owner check: * if owner is non zero, caller must be owner. * If owner is address(0), contract can call itself. */ function _checkOwner() internal view override { address owner; address caller = msg.sender; assembly ("memory-safe") { owner := sload(_OWNER_SLOT) } if (owner == caller) return; if (owner == address(0) && caller == address(this)) return; revert Unauthorized(); } /// @dev Get light userOp hash of the Packed user operation. function _getLightUserOpHash( PackedUserOperation calldata userOp_, LightUserOpGasLimits memory gasLimits_ ) internal view returns (bytes32) { return keccak256(abi.encode(userOp_.hashLight(), gasLimits_, entryPoint(), block.chainid)); } /// @dev validates if the given hash (ERC1271) was signed by the signers. function _isValidSignature(bytes32 hash_, bytes calldata signature_) internal view override returns (bool) { return _getMultiSignerStorage().isValidSignature(hash_, abi.decode(signature_, (ERC1271Signature)).signatures); } /// @dev validates single userOp signature. function _validateSingleUserOp( bytes32 lightHash_, bytes32 userOpHash_, SingleUserOpSignature memory signature ) internal view returns (uint256) { return _isValidSignature(lightHash_, userOpHash_, signature.signatures); } /** * @dev validates merkelized userOp signature. */ function _validateMerkelizedUserOp( bytes32 lightHash_, bytes32 userOpHash_, MerkelizedUserOpSignature memory signature ) internal view returns (uint256) { bool isValidMerkleProof = MerkleProof.verify(signature.merkleProof, signature.merkleTreeRoot, userOpHash_); if (signature.signatures.length > 1) { isValidMerkleProof = isValidMerkleProof && MerkleProof.verify(signature.lightMerkleProof, signature.lightMerkleTreeRoot, lightHash_); } uint256 isValidSig = _isValidSignature(signature.lightMerkleTreeRoot, signature.merkleTreeRoot, signature.signatures); return isValidMerkleProof ? isValidSig : UserOperationLib.INVALID_SIGNATURE; } function _isValidSignature( bytes32 lightHash_, bytes32 hash_, MultiSignerLib.SignatureWrapper[] memory signatures ) internal view returns (uint256 validationData) { return _getMultiSignerStorage().isValidSignature(lightHash_, hash_, signatures) ? UserOperationLib.VALID_SIGNATURE : UserOperationLib.INVALID_SIGNATURE; } function _getSignatureType(bytes1 signatureType_) internal pure returns (SignatureTypes) { return SignatureTypes(uint8(signatureType_)); } function _verifyGasLimits( PackedUserOperation calldata userOp_, LightUserOpGasLimits memory gasLimits_ ) internal pure { (uint256 userOpMaxPriorityFeePerGas,) = UserOperationLib.unpackUints(userOp_.gasFees); (uint256 verificationGasLimit, uint256 callGasLimit) = UserOperationLib.unpackUints(userOp_.accountGasLimits); if ( userOpMaxPriorityFeePerGas > gasLimits_.maxPriorityFeePerGas || callGasLimit > gasLimits_.callGasLimit || userOp_.preVerificationGas > gasLimits_.preVerificationGas || verificationGasLimit > gasLimits_.verificationGasLimit ) revert InvalidGasLimits(); if (userOp_.paymasterAndData.length > 0) { (address paymaster, uint256 paymasterVerificationGasLimit, uint256 paymasterPostOpGasLimit) = UserOperationLib.unpackPaymasterStaticFields(userOp_.paymasterAndData); if ( gasLimits_.paymaster != paymaster || paymasterVerificationGasLimit > gasLimits_.paymasterVerificationGasLimit || paymasterPostOpGasLimit > gasLimits_.paymasterPostOpGasLimit ) { revert InvalidPaymasterData(); } } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.23; import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol"; /** * @title User operation Library * @custom:security-contract [email protected] * @author Splits (https://splits.org) * @notice Forked from * https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/core/UserOperationLib.sol * @dev Light here refers to the subset of userOp params not related to gas, gas price or initCode in the userOp. */ library UserOperationLib { /* -------------------------------------------------------------------------- */ /* CONSTANTS */ /* -------------------------------------------------------------------------- */ uint256 public constant VALID_SIGNATURE = 0; uint256 public constant INVALID_SIGNATURE = 1; uint256 public constant PAYMASTER_VALIDATION_GAS_OFFSET = 20; uint256 public constant PAYMASTER_POSTOP_GAS_OFFSET = 36; uint256 public constant PAYMASTER_DATA_OFFSET = 52; /* -------------------------------------------------------------------------- */ /* FUNCTIONS */ /* -------------------------------------------------------------------------- */ /** * @notice keccak function over calldata. * @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than * letting solidity do it. */ function calldataKeccak(bytes calldata data_) private pure returns (bytes32 ret) { assembly ("memory-safe") { let mem := mload(0x40) let len := data_.length calldatacopy(mem, data_.offset, len) ret := keccak256(mem, len) } } /** * @notice Get sender from user operation data. * @param userOp_ - The user operation data. */ function getSender(PackedUserOperation calldata userOp_) internal pure returns (address) { address data; //read sender from userOp, which is first userOp member (saves 800 gas...) assembly { data := calldataload(userOp_) } return address(uint160(data)); } /** * @notice Pack the light user operation data into bytes for hashing. * @dev Does not include the following properties of the User Op. * - initCode * - accountGasLimits * - preVerificationGas * - gasFees * - paymasterAndData * - signature * @param userOp_ - The user operation data. */ function encodeLight(PackedUserOperation calldata userOp_) internal pure returns (bytes memory ret) { address sender = getSender(userOp_); uint256 nonce = userOp_.nonce; bytes32 hashCallData = calldataKeccak(userOp_.callData); return abi.encode(sender, nonce, hashCallData); } /** * @notice Hash light user operation data. * @param userOp_ - The user operation data. */ function hashLight(PackedUserOperation calldata userOp_) internal pure returns (bytes32) { return keccak256(encodeLight(userOp_)); } /** * @notice Pack the user operation data into bytes for hashing. * @param userOp_ - The user operation data. */ function encode(PackedUserOperation calldata userOp_) internal pure returns (bytes memory ret) { address sender = getSender(userOp_); uint256 nonce = userOp_.nonce; bytes32 hashInitCode = calldataKeccak(userOp_.initCode); bytes32 hashCallData = calldataKeccak(userOp_.callData); bytes32 accountGasLimits = userOp_.accountGasLimits; uint256 preVerificationGas = userOp_.preVerificationGas; bytes32 gasFees = userOp_.gasFees; bytes32 hashPaymasterAndData = calldataKeccak(userOp_.paymasterAndData); // solhint-disable return abi.encode( sender, nonce, hashInitCode, hashCallData, accountGasLimits, preVerificationGas, gasFees, hashPaymasterAndData ); // solhint-enable } /** * Hash the user operation data. * @param userOp_ - The user operation data. */ function hash(PackedUserOperation calldata userOp_) internal pure returns (bytes32) { return keccak256(encode(userOp_)); } function unpackUints(bytes32 packed) internal pure returns (uint256 high128, uint256 low128) { return (uint128(bytes16(packed)), uint128(uint256(packed))); } function unpackPaymasterStaticFields(bytes calldata paymasterAndData) internal pure returns (address paymaster, uint256 validationGasLimit, uint256 postOpGasLimit) { return ( address(bytes20(paymasterAndData[:PAYMASTER_VALIDATION_GAS_OFFSET])), uint128(bytes16(paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET:PAYMASTER_POSTOP_GAS_OFFSET])), uint128(bytes16(paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET:PAYMASTER_DATA_OFFSET])) ); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.23; import { Signer } from "../signers/Signer.sol"; /** * @notice Multi Signer struct. * @dev Each signer is of type `Signer`. * @dev Can allow up to 256 signers. */ struct MultiSigner { /// @dev Number of unique signatures required to validate a message signed by this contract. uint8 threshold; /// @dev number of signers uint8 signerCount; /// @dev signers of type `Signer`; Signer[256] signers; } using MultiSignerLib for MultiSigner global; /** * @title Multi Signer Library * @custom:security-contract [email protected] * @author Splits (https://splits.org) */ library MultiSignerLib { /* -------------------------------------------------------------------------- */ /* STRUCTS */ /* -------------------------------------------------------------------------- */ struct SignatureWrapper { /// @dev The index of the signer that signed, see `MultiSigner.signerAtIndex` uint8 signerIndex; /** * @dev If `MultiSigner.signerAtIndex` is an Ethereum address, this should be `abi.encodePacked(r, s, v)` * If `MultiSigner.signerAtIndex` is a public key, this should be `abi.encode(WebAuthnAuth)`. */ bytes signatureData; } /* -------------------------------------------------------------------------- */ /* ERRORS */ /* -------------------------------------------------------------------------- */ /// @notice Thrown when Signer is invalid. error InvalidSigner(Signer signer); /// @notice Thrown when threshold is greater than number of owners or when zero. error InvalidThreshold(); /// @notice Thrown when number of signers is more than 256. error InvalidNumberOfSigners(); /** * @notice Thrown when trying to remove an empty signer. * @param index Index of the empty signer. */ error SignerNotPresent(uint8 index); /** * @notice Thrown when trying to replace an existing signer. * @param index Index of the existing signer. */ error SignerAlreadyPresent(uint8 index); /** * @notice Thrown when same signer is used for signature verification. * @param index Index of duplicate signer. */ error DuplicateSigner(uint8 index); /* -------------------------------------------------------------------------- */ /* FUNCTIONS */ /* -------------------------------------------------------------------------- */ /// @notice Get signer at index from storage. function getSigner(MultiSigner storage $_, uint8 index_) internal view returns (Signer memory) { return $_.signers[index_]; } /// @notice Get threshold from storage. function getThreshold(MultiSigner storage $_) internal view returns (uint8) { return $_.threshold; } /// @notice Get number of signers from storage. function getSignerCount(MultiSigner storage $_) internal view returns (uint8) { return $_.signerCount; } /** * @notice Adds signer at index in storage. * * @dev Throws error when a signer is already present at index. * @dev Throws error when signer is not EOA or Passkey. * * @param $_ Multi signer storage reference. * @param signer_ Signer to be set. * @param index_ Index to set signer at. */ function addSigner(MultiSigner storage $_, Signer calldata signer_, uint8 index_) internal { if (!$_.signers[index_].isEmptyMem()) revert SignerAlreadyPresent(index_); _addSigner($_, signer_, index_); $_.signerCount = $_.signerCount + 1; } /** * @notice Removes signer at the given `index`. * * @dev Reverts if 'threshold' is equal to signer count. * @dev Reverts if signer is empty at `index`. * * @param $_ Multi signer storage reference. * @param index_ The index of the signer to be removed. * @return signer Signer being removed. */ function removeSigner(MultiSigner storage $_, uint8 index_) internal returns (Signer memory signer) { uint8 signerCount = $_.signerCount; if (signerCount == $_.threshold) revert InvalidThreshold(); signer = $_.signers[index_]; if (signer.isEmptyMem()) revert SignerNotPresent(index_); delete $_.signers[index_]; $_.signerCount = signerCount - 1; } /** * @notice Updates threshold of the signer set. * * @dev Reverts if 'threshold' is greater than signer count. * @dev Reverts if 'threshold' is 0. * * @param $_ Multi signer storage reference. * @param threshold_ The new signer set threshold. */ function updateThreshold(MultiSigner storage $_, uint8 threshold_) internal { if (threshold_ == 0) revert InvalidThreshold(); if ($_.signerCount < threshold_) revert InvalidThreshold(); $_.threshold = threshold_; } /** * @notice Initialize the signers. * * @dev Intended to be called when initializing the signer set. * @dev Reverts if signer is neither an EOA or a passkey. * @dev Reverts if 'threshold' is less than number of signers. * @dev Reverts if 'threshold' is 0. * @dev Reverts if number of signers is more than 256. * * @param signers_ The initial set of signers. * @param threshold_ The number of signers needed for approval. */ function initializeSigners(MultiSigner storage $_, Signer[] calldata signers_, uint8 threshold_) internal { if (signers_.length > type(uint8).max) revert InvalidNumberOfSigners(); uint8 numSigners = uint8(signers_.length); if (numSigners < threshold_ || threshold_ < 1) revert InvalidThreshold(); for (uint8 i; i < numSigners; i++) { _addSigner($_, signers_[i], i); } $_.signerCount = numSigners; $_.threshold = threshold_; } /// @dev reverts if signer is neither an EOA or a Passkey. /// @dev replaces signer at `index`. Should be used with proper checks. function _addSigner(MultiSigner storage $_, Signer calldata signer_, uint8 index_) private { if (signer_.isPasskey()) { /// if passkey store signer as is. $_.signers[index_] = signer_; } else if (signer_.isEOA()) { /// if EOA only store slot1 since slot2 is zero. $_.signers[index_].slot1 = signer_.slot1; } else { revert InvalidSigner(signer_); } } /* -------------------------------------------------------------------------- */ /* VALIDATE SIGNERS */ /* -------------------------------------------------------------------------- */ /** * @notice Validates the list of `signers` and `threshold`. * * @dev Throws error when number of signers is zero or greater than 255. * @dev Throws error if `threshold` is zero or greater than number of signers. * * @param signers_ List of Signer(s). * @param threshold_ minimum number of signers required for approval. */ function validateSigners(Signer[] calldata signers_, uint8 threshold_) internal pure { if (signers_.length > type(uint8).max) revert InvalidNumberOfSigners(); uint8 numberOfSigners = uint8(signers_.length); if (numberOfSigners < threshold_ || threshold_ < 1) revert InvalidThreshold(); for (uint8 i; i < numberOfSigners; i++) { validateSigner(signers_[i]); } } /** * @notice Validates the signer. * * @dev Throws error when signer is neither an EOA or a passkey. */ function validateSigner(Signer calldata signer_) internal pure { if (!signer_.isValid()) revert InvalidSigner(signer_); } /* -------------------------------------------------------------------------- */ /* VALIDATE SIGNATURES */ /* -------------------------------------------------------------------------- */ /** * @notice validates if `hash_` was signed by the signer set present in `$_` * * @param $_ Multi signer storage reference. * @param hash_ blob of data that needs to be verified. * @param signatures_ List of signatureWrapper. */ function isValidSignature( MultiSigner storage $_, bytes32 hash_, SignatureWrapper[] memory signatures_ ) internal view returns (bool isValid) { return isValidSignature($_, hash_, hash_, signatures_); } /** * @notice validates if a pair of hashes was signed by the signer set present in `$_`. * * @param $_ Multi signer storage reference. * @param frontHash_ blob of data that should be signed by all but the last signer. * @param backHash_ blob of data that should be signed by the last signer. * @param signatures_ List of signatureWrapper. */ function isValidSignature( MultiSigner storage $_, bytes32 frontHash_, bytes32 backHash_, SignatureWrapper[] memory signatures_ ) internal view returns (bool isValid) { isValid = true; uint8 threshold = $_.threshold; uint256 alreadySigned; uint256 mask; uint8 signerIndex; uint256 i; for (; i < threshold - 1; i++) { signerIndex = signatures_[i].signerIndex; mask = (1 << signerIndex); if (alreadySigned & mask != 0) revert DuplicateSigner(signerIndex); if ($_.signers[signerIndex].isValidSignature(frontHash_, signatures_[i].signatureData)) { alreadySigned |= mask; } else { isValid = false; } } signerIndex = signatures_[i].signerIndex; mask = (1 << signerIndex); if (alreadySigned & mask != 0) revert DuplicateSigner(signerIndex); if ($_.signers[signerIndex].isValidSignature(backHash_, signatures_[i].signatureData)) { alreadySigned |= mask; } else { isValid = false; } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.23; import { decodeAccountSigner } from "./AccountSigner.sol"; import { decodePasskeySigner } from "./PasskeySigner.sol"; /** * @notice An EOA or Passkey signer. * * @dev For a Signer to be valid it has to be either an EOA or a Passkey signer. * - EOA -> slot2 has to be empty and slot1 has to be a valid address. * - Passkey -> slot2 has to be non empty. */ struct Signer { bytes32 slot1; bytes32 slot2; } using SignerLib for Signer global; /** * @notice Signer library * @custom:security-contract [email protected] * @author Splits (https://splits.org/) */ library SignerLib { /* -------------------------------------------------------------------------- */ /* CONSTANTS */ /* -------------------------------------------------------------------------- */ /// @dev bytes32(0). bytes32 constant ZERO = bytes32(0); /* -------------------------------------------------------------------------- */ /* ERRORS */ /* -------------------------------------------------------------------------- */ /// @notice Thrown when signer is neither EOA or Passkey. error InvalidSigner(); /* -------------------------------------------------------------------------- */ /* FUNCTIONS */ /* -------------------------------------------------------------------------- */ /// @notice checks if slot2 is zero and slot1 is a valid address. function isEOA(Signer calldata signer_) internal pure returns (bool) { uint256 slot1 = uint256(bytes32(signer_.slot1)); return signer_.slot2 == ZERO && slot1 <= type(uint160).max && slot1 > 0; } /// @notice checks if slot2 is zero and slot1 is a valid address. function isEOAMem(Signer memory signer_) internal pure returns (bool) { uint256 slot1 = uint256(bytes32(signer_.slot1)); return signer_.slot2 == ZERO && slot1 <= type(uint160).max && slot1 > 0; } /** * @dev slot2 will always be non zero for a passkey. * ref: https://crypto.stackexchange.com/questions/108238/could-a-ec-public-key-have-zero-coordinate/108242#108242 */ function isPasskey(Signer calldata signer_) internal pure returns (bool) { return signer_.slot2 != ZERO; } /** * @dev slot2 will always be non zero for a passkey. * ref: https://crypto.stackexchange.com/questions/108238/could-a-ec-public-key-have-zero-coordinate/108242#108242 */ function isPasskeyMem(Signer memory signer_) internal pure returns (bool) { return signer_.slot2 != ZERO; } /// @notice Signer is considered valid if it is an EOA or a Passkey. function isValid(Signer calldata signer_) internal pure returns (bool) { return isEOA(signer_) || isPasskey(signer_); } /// @notice Returns true if both slot1 and slot2 are zero. function isEmptyMem(Signer memory signer_) internal pure returns (bool) { return signer_.slot1 == ZERO && signer_.slot2 == ZERO; } /// @notice validates if the signature provided by the signer is valid for the hash. function isValidSignature( Signer memory signer_, bytes32 hash_, bytes memory signature_ ) internal view returns (bool) { if (isPasskeyMem(signer_)) { return decodePasskeySigner(signer_).isValidSignature(hash_, signature_); } else if (isEOAMem(signer_)) { return decodeAccountSigner(signer_).isValidSignature(hash_, signature_); } else { revert InvalidSigner(); } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.23; import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; /** * @title ERC-1271 * @custom:security-contract [email protected] * @author Splits (https://splits.org) * @notice Based on https://github.com/coinbase/smart-wallet/blob/main/src/ERC1271.sol * @notice Abstract ERC-1271 implementation (based on Solady's) with guards to handle the same * signer being used on multiple accounts. */ abstract contract ERC1271 is EIP712 { /* -------------------------------------------------------------------------- */ /* CONSTANTS */ /* -------------------------------------------------------------------------- */ /** * @dev We use `bytes32 hash` rather than `bytes message` * In the EIP-712 context, `bytes message` would be useful for showing users a full message * they are signing in some wallet preview. But in this case, to prevent replay * across accounts, we are always dealing with nested messages, and so the * input should be a EIP-191 or EIP-712 output hash. * E.g. The input hash would be result of * * keccak256("\x19\x01" || someDomainSeparator || hashStruct(someStruct)) * * OR * * keccak256("\x19Ethereum Signed Message:\n" || len(someMessage) || someMessage), */ bytes32 private constant _MESSAGE_TYPEHASH = keccak256("SplitMessage(bytes32 hash)"); /* -------------------------------------------------------------------------- */ /* CONSTRUCTOR */ /* -------------------------------------------------------------------------- */ /** * @dev Initializes the {EIP712} domain separator. */ constructor(string memory name_, string memory version_) EIP712(name_, version_) { } /* -------------------------------------------------------------------------- */ /* PUBLIC FUNCTIONS */ /* -------------------------------------------------------------------------- */ /** * @notice Validates the `signature` against the given `hash`. * * @dev This implementation follows ERC-1271. See https://eips.ethereum.org/EIPS/eip-1271. * @dev IMPORTANT: Signature verification is performed on the hash produced AFTER applying the anti * cross-account-replay layer on the given `hash` (i.e., verification is run on the replay-safe * hash version). * * @param hash_ The original hash. * @param signature_ The signature of the replay-safe hash to validate. * @return result `0x1626ba7e` if validation succeeded, else `0xffffffff`. */ function isValidSignature(bytes32 hash_, bytes calldata signature_) public view virtual returns (bytes4) { if (_isValidSignature(replaySafeHash(hash_), signature_)) { // bytes4(keccak256("isValidSignature(bytes32,bytes)")) return 0x1626ba7e; } return 0xffffffff; } /** * @dev Returns an EIP-712-compliant hash of `hash`, * where the domainSeparator includes address(this) and block.chainId * to protect against the same signature being used for many accounts. * * @param hash_ The original hash. * @return * keccak256(\x19\x01 || this.domainSeparator || * hashStruct(SplitWalletMessage({ * hash: `hash` * })) * ) */ function replaySafeHash(bytes32 hash_) public view virtual returns (bytes32) { return _hashTypedDataV4(keccak256(abi.encode(_MESSAGE_TYPEHASH, hash_))); } /* -------------------------------------------------------------------------- */ /* INTERNAL FUNCTIONS */ /* -------------------------------------------------------------------------- */ /** * @notice Validates the `signature` against the given `hash`. * * @dev MUST be defined by the implementation. * * @param hash_ The hash whose signature has been performed on. * @param signature_ The signature associated with `hash`. * @return `true` is the signature is valid, else `false`. */ function _isValidSignature(bytes32 hash_, bytes calldata signature_) internal view virtual returns (bool); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.23; import { Receiver } from "solady/accounts/Receiver.sol"; /** * @title Fallback Manager - Fallback call handler for a smart contract. * @custom:security-contract [email protected] * @author Splits (https://splits.org) * @dev By defaults supports all ERC721 and ERC1155 token safety callbacks. */ abstract contract FallbackManager is Receiver { /* -------------------------------------------------------------------------- */ /* CONSTANTS */ /* -------------------------------------------------------------------------- */ /** * @dev Slot for the `FallbackManager` struct in storage. * Computed from * keccak256(abi.encode(uint256(keccak256("splits.storage.fallbackManager")) - 1)) & ~bytes32(uint256(0xff)) * Follows ERC-7201 (see https://eips.ethereum.org/EIPS/eip-7201). */ bytes32 internal constant _FALLBACK_MANAGER_STORAGE_SLOT = 0xb944faae3883660fcc8ae340f6a7654d66dcad30db4f2f608a0b893e0f339a00; /* -------------------------------------------------------------------------- */ /* STRUCTS */ /* -------------------------------------------------------------------------- */ /// @notice Fallback Manager storage structure /// @custom:storage-location erc7201:splits.storage.fallbackManager struct FallbackManagerStorage { mapping(bytes4 => address) fallbackHandler; } /* -------------------------------------------------------------------------- */ /* EVENTS */ /* -------------------------------------------------------------------------- */ /// @notice Event emitted when a new Fallback Handler is registered. event UpdatedFallbackHandler(bytes4 indexed sig, address indexed handler); /// @notice Event emitted when this contract receives ETH. event ReceiveEth(address indexed sender, uint256 amount); /// @notice Event emitted when a call is made to a fallback handler. event FallbackHandlerCalled( address indexed sender, address indexed handler, bytes data, bool success, bytes result ); /* -------------------------------------------------------------------------- */ /* ERRORS */ /* -------------------------------------------------------------------------- */ /// @notice Thrown when a function is not supported by the FallbackHandler. error FunctionNotSupported(bytes4 sig); /* -------------------------------------------------------------------------- */ /* EXTERNAL/PUBLIC FUNCTIONS */ /* -------------------------------------------------------------------------- */ /** * @dev Fallback function to handle unsupported function calls. * It checks if a handler is set for the given function signature and * if so, forwards the call to the handler. */ fallback() external payable override receiverFallback { address handler = _getFallbackManagerStorage().fallbackHandler[msg.sig]; if (handler == address(0)) { revert FunctionNotSupported(msg.sig); } bytes calldata data = msg.data; (bool success, bytes memory result) = handler.call(data); emit FallbackHandlerCalled(msg.sender, handler, data, success, result); if (!success) { assembly ("memory-safe") { revert(add(result, 32), mload(result)) } } assembly ("memory-safe") { return(add(result, 0x20), mload(result)) } } /// @notice Receive function that logs the amount of ETH received. receive() external payable override { emit ReceiveEth(msg.sender, msg.value); } /** * @notice Allows setting a handler for a given function signature. * * @dev Following signatures is handled automatically by this contract and does not need any handler. * - 0x150b7a02: `onERC721Received(address,address,uint256,bytes)`. * - 0xf23a6e61: `onERC1155Received(address,address,uint256,uint256,bytes)`. * - 0xbc197c81: `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`. * * @param sig_ The function signature for which the handler is being set. * @param handler_ The address of the handler contract. */ function updateFallbackHandler(bytes4 sig_, address handler_) external { _authorize(); _getFallbackManagerStorage().fallbackHandler[sig_] = handler_; emit UpdatedFallbackHandler(sig_, handler_); } /** * @notice Returns the fallback handler associated with the provided `sig`. * @param sig_ bytes4 signature. * @return handler address of contract that receives the call for `sig`. */ function getFallbackHandler(bytes4 sig_) public view returns (address) { return _getFallbackManagerStorage().fallbackHandler[sig_]; } /* -------------------------------------------------------------------------- */ /* INTERNAL/PRIVATE FUNCTIONS */ /* -------------------------------------------------------------------------- */ function _authorize() internal view virtual; function _getFallbackManagerStorage() internal pure returns (FallbackManagerStorage storage $) { assembly ("memory-safe") { $.slot := _FALLBACK_MANAGER_STORAGE_SLOT } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.23; import { Caller } from "./Caller.sol"; /** * @title Module Manager * @custom:security-contract [email protected] * @author Splits (https://splits.org) * @notice Manages modules of a smart contract. Gives each module the ability to execute `Calls` from this account. * @dev Account owners should be careful when adding a module. */ abstract contract ModuleManager is Caller { /* -------------------------------------------------------------------------- */ /* CONSTANTS */ /* -------------------------------------------------------------------------- */ /** * @dev Slot for the `ModuleManager` struct in storage. * Computed from * keccak256(abi.encode(uint256(keccak256("splits.storage.moduleManager")) - 1)) & ~bytes32(uint256(0xff)) * Follows ERC-7201 (see https://eips.ethereum.org/EIPS/eip-7201). */ bytes32 internal constant _MODULE_MANAGER_STORAGE_SLOT = 0xe103b19f601cd46db3208af0dd24bb7e0acd21ca997b18cc4246bfe5258d3800; /* -------------------------------------------------------------------------- */ /* STRUCTS */ /* -------------------------------------------------------------------------- */ /// @notice Module Manager storage structure. /// @custom:storage-location erc7201:splits.storage.moduleManager struct ModuleManagerStorage { mapping(address => bool) isModule; } /* -------------------------------------------------------------------------- */ /* EVENTS */ /* -------------------------------------------------------------------------- */ /// @notice Event emitted when an module is enabled. event EnabledModule(address indexed module); /// @notice Event emitted when an module is disabled. event DisabledModule(address indexed module); /// @notice Event emitted when an module executes a call. event ExecutedTxFromModule(address indexed module, Call call); /* -------------------------------------------------------------------------- */ /* ERRORS */ /* -------------------------------------------------------------------------- */ /// @notice Thrown when caller is not an module. error OnlyModule(); /* -------------------------------------------------------------------------- */ /* MODIFIERS */ /* -------------------------------------------------------------------------- */ modifier onlyModule() { if (!_getModuleManagerStorage().isModule[msg.sender]) revert OnlyModule(); _; } /* -------------------------------------------------------------------------- */ /* EXTERNAL/PUBLIC FUNCTIONS */ /* -------------------------------------------------------------------------- */ /** * @notice Adds `module` to the allowlist. * * @dev access is controlled by `_authorize()`. * * @param module_ address of module. */ function enableModule(address module_) external { _authorize(); _enableModule(module_); } /** * @notice Adds `module` to the allowlist and makes a call to the `setupContract` passing `data_`. * * @dev access is controlled by `_authorize()`. * * @param module_ address of module. * @param setupContract_ address of contract to call to setup module. * @param data_ data passed to the setupContract. */ function setupAndEnableModule(address module_, address setupContract_, bytes calldata data_) external { _authorize(); _enableModule(module_); _call(setupContract_, 0, data_); } /** * @notice Removes module from the allowlist. * * @dev access is controlled by `_authorize()`. * * @param module_ address of module. */ function disableModule(address module_) external { _authorize(); _disableModule(module_); } /** * @notice Removes `module` from the allowlist and makes a call to the `teardownContract_` passing `data_`. * * @dev access is controlled by `_authorize()`. * * @param module_ address of module. * @param teardownContract_ address of contract to call to teardown module. * @param data_ data passed to the teardown contract. */ function teardownAndDisableModule(address module_, address teardownContract_, bytes calldata data_) external { _authorize(); _disableModule(module_); _call(teardownContract_, 0, data_); } /** * @notice Executes a single call from the account. * * @dev Can only be called by a module present in the allowlist. * @dev Emits an event to capture the call executed. * * @param call_ Call to execute from the account. */ function executeFromModule(Call calldata call_) external onlyModule { _call(call_); emit ExecutedTxFromModule(msg.sender, call_); } /** * @notice Executes calls from the account. * * @dev Can only be called by a module present in the allowlist. * @dev Emits an event to capture the call executed. * * @param calls_ Calls to execute from this account. */ function executeFromModule(Call[] calldata calls_) external onlyModule { uint256 numCalls = calls_.length; for (uint256 i; i < numCalls; i++) { _call(calls_[i]); emit ExecutedTxFromModule(msg.sender, calls_[i]); } } /** * @notice Returns true if the provided `module` is enabled otherwise false. * * @param module_ address of module. */ function isModuleEnabled(address module_) external view returns (bool) { return _getModuleManagerStorage().isModule[module_]; } /* -------------------------------------------------------------------------- */ /* INTERNAL/PRIVATE FUNCTIONS */ /* -------------------------------------------------------------------------- */ function _enableModule(address module_) internal { _getModuleManagerStorage().isModule[module_] = true; emit EnabledModule(module_); } function _disableModule(address module_) internal { _getModuleManagerStorage().isModule[module_] = false; emit DisabledModule(module_); } function _authorize() internal view virtual; function _getModuleManagerStorage() internal pure returns (ModuleManagerStorage storage $) { assembly ("memory-safe") { $.slot := _MODULE_MANAGER_STORAGE_SLOT } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.23; import { MultiSigner } from "../signers/MultiSigner.sol"; import { Signer } from "../signers/Signer.sol"; /** * @title Multi Signer Auth * @custom:security-contract [email protected] * @author Splits (https://splits.org) * @notice Auth contract allowing multiple signers, each identified as `Signer` with a specified threshold. * @dev Based on Coinbase's Smart Wallet Multi Ownable (https://github.com/coinbase/smart-wallet) */ abstract contract MultiSignerAuth { /* -------------------------------------------------------------------------- */ /* CONSTANTS */ /* -------------------------------------------------------------------------- */ /** * @dev Slot for the `MultiSignerStorage` struct in storage. * Computed from * keccak256(abi.encode(uint256(keccak256("splits.storage.multiSignerAuth")) - 1)) & ~bytes32(uint256(0xff)) * Follows ERC-7201 (see https://eips.ethereum.org/EIPS/eip-7201). */ bytes32 private constant _MUTLI_SIGNER_AUTH_STORAGE_SLOT = 0x3e5431599761dc1a6f375d94085bdcd73bc8fa7c6b3d455d31679f3080214700; /* -------------------------------------------------------------------------- */ /* STRUCT */ /* -------------------------------------------------------------------------- */ /// @custom:storage-location erc7201:splits.storage.multiSignerAuth struct MultiSignerAuthStorage { MultiSigner signers; } /* -------------------------------------------------------------------------- */ /* EVENTS */ /* -------------------------------------------------------------------------- */ /** * @notice Emitted when MultiSigner is initialized. * @param signers Initial set of signers. * @param threshold Initial threshold for the signer set. */ event InitializedSigners(Signer[] signers, uint8 threshold); /** * @notice Emitted when a new signer is registered. * @param index The index of the signer added. * @param signer The signer added. */ event AddSigner(uint256 indexed index, Signer signer); /** * @notice Emitted when a signer is removed. * @param index The index of the signer removed. * @param signer The signer removed. */ event RemoveSigner(uint256 indexed index, Signer signer); /** * @notice Emitted when threshold is updated. * @param threshold The new threshold for the signer set. */ event UpdateThreshold(uint8 threshold); /* -------------------------------------------------------------------------- */ /* MODIFIERS */ /* -------------------------------------------------------------------------- */ modifier onlyAuthorized() { _authorize(); _; } /* -------------------------------------------------------------------------- */ /* PUBLIC VIEW FUNCTIONS */ /* -------------------------------------------------------------------------- */ /// @notice Returns the Signer at the given `index`. function getSigner(uint8 index_) public view returns (Signer memory) { return _getMultiSignerStorage().getSigner(index_); } /// @notice Returns the current number of signers. function getSignerCount() public view returns (uint8) { return _getMultiSignerStorage().getSignerCount(); } /// @notice Returns the threshold. function getThreshold() public view returns (uint8) { return _getMultiSignerStorage().getThreshold(); } /* -------------------------------------------------------------------------- */ /* EXTERNAL FUNCTIONS */ /* -------------------------------------------------------------------------- */ /** * @notice Adds a `signer` at the `index`. * * @dev Throws error when a signer is already present at index. * @dev Reverts if Signer is neither EOA or Passkey. * * @param signer_ The Signer to register. * @param index_ The index to register the signer. */ function addSigner(Signer calldata signer_, uint8 index_) external onlyAuthorized { _getMultiSignerStorage().addSigner(signer_, index_); emit AddSigner(index_, signer_); } /** * @notice Removes signer at the given `index`. * * @dev Reverts if 'threshold' is equal to signer count. * @dev Reverts if signer is empty at `index`. * * @param index_ The index of the signer to be removed. */ function removeSigner(uint8 index_) external onlyAuthorized { Signer memory signer = _getMultiSignerStorage().removeSigner(index_); emit RemoveSigner(index_, signer); } /** * @notice Updates threshold of the signer set. * * @dev Reverts if 'threshold' is greater than signer count. * @dev Reverts if 'threshold' is 0. * * @param threshold_ The new signer set threshold. */ function updateThreshold(uint8 threshold_) external onlyAuthorized { _getMultiSignerStorage().updateThreshold(threshold_); emit UpdateThreshold(threshold_); } /* -------------------------------------------------------------------------- */ /* INTERNAL FUNCTIONS */ /* -------------------------------------------------------------------------- */ function _authorize() internal virtual; /// @notice Helper function to get storage reference to the `MultiSignerStorage` struct. function _getMultiSignerStorage() internal view returns (MultiSigner storage) { MultiSignerAuthStorage storage $; assembly ("memory-safe") { $.slot := _MUTLI_SIGNER_AUTH_STORAGE_SLOT } return $.signers; } /** * @notice Initialize the signers of this contract. * * @dev Intended to be called when contract is first deployed and never again. * @dev Reverts if signer is neither an EOA or a passkey. * @dev Reverts if 'threshold' is less than number of signers. * @dev Reverts if 'threshold' is 0. * @dev Reverts if number of signers is more than 256. * * @param signers_ The initial set of signers. * @param threshold_ The number of signers needed for approval. */ function _initializeMultiSignerAuth(Signer[] calldata signers_, uint8 threshold_) internal { _getMultiSignerStorage().initializeSigners(signers_, threshold_); emit InitializedSigners(signers_, threshold_); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.20; /** * @dev These functions deal with verification of Merkle Tree proofs. * * The tree and the proofs can be generated using our * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. * You will find a quickstart guide in the readme. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the Merkle tree could be reinterpreted as a leaf value. * OpenZeppelin's JavaScript library generates Merkle trees that are safe * against this attack out of the box. */ library MerkleProof { /** *@dev The multiproof provided is not valid. */ error MerkleProofInvalidMultiproof(); /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Calldata version of {verify} */ function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProofCalldata(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Calldata version of {processProof} */ function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function multiProofVerify( bytes32[] memory proof, bool[] memory proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProof(proof, proofFlags, leaves) == root; } /** * @dev Calldata version of {multiProofVerify} * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function multiProofVerifyCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProofCalldata(proof, proofFlags, leaves) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). */ function processMultiProof( bytes32[] memory proof, bool[] memory proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. if (leavesLen + proofLen != totalHashes + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { if (proofPos != proofLen) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Calldata version of {processMultiProof}. * * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details. */ function processMultiProofCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the Merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. if (leavesLen + proofLen != totalHashes + 1) { revert MerkleProofInvalidMultiproof(); } // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { if (proofPos != proofLen) { revert MerkleProofInvalidMultiproof(); } unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Sorts the pair (a, b) and hashes the result. */ function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) { return a < b ? _efficientHash(a, b) : _efficientHash(b, a); } /** * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory. */ function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { /// @solidity memory-safe-assembly assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.5; import "./PackedUserOperation.sol"; interface IAccount { /** * Validate user's signature and nonce * the entryPoint will make the call to the recipient only if this validation call returns successfully. * signature failure should be reported by returning SIG_VALIDATION_FAILED (1). * This allows making a "simulation call" without a valid signature * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure. * * @dev Must validate caller is the entryPoint. * Must validate the signature and nonce * @param userOp - The operation that is about to be executed. * @param userOpHash - Hash of the user's request data. can be used as the basis for signature. * @param missingAccountFunds - Missing funds on the account's deposit in the entrypoint. * This is the minimum amount to transfer to the sender(entryPoint) to be * able to make the call. The excess is left as a deposit in the entrypoint * for future calls. Can be withdrawn anytime using "entryPoint.withdrawTo()". * In case there is a paymaster in the request (or the current deposit is high * enough), this value will be zero. * @return validationData - Packaged ValidationData structure. use `_packValidationData` and * `_unpackValidationData` to encode and decode. * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, * otherwise, an address of an "authorizer" contract. * <6-byte> validUntil - Last timestamp this operation is valid. 0 for "indefinite" * <6-byte> validAfter - First timestamp this operation is valid * If an account doesn't use time-range, it is enough to * return SIG_VALIDATION_FAILED value (1) for signature failure. * Note that the validation code cannot use block.timestamp (or block.number) directly. */ function validateUserOp( PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds ) external returns (uint256 validationData); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.5; /** * User Operation struct * @param sender - The sender account of this request. * @param nonce - Unique value the sender uses to verify it is not a replay. * @param initCode - If set, the account contract will be created by this constructor/ * @param callData - The method call to execute on this account. * @param accountGasLimits - Packed gas limits for validateUserOp and gas limit passed to the callData method call. * @param preVerificationGas - Gas not calculated by the handleOps method, but added to the gas paid. * Covers batch overhead. * @param gasFees - packed gas fields maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters. * @param paymasterAndData - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra data * The paymaster will pay for the transaction instead of the sender. * @param signature - Sender-verified signature over the entire request, the EntryPoint address and the chain ID. */ struct PackedUserOperation { address sender; uint256 nonce; bytes initCode; bytes callData; bytes32 accountGasLimits; uint256 preVerificationGas; bytes32 gasFees; bytes paymasterAndData; bytes signature; }
// 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(); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice UUPS proxy mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/UUPSUpgradeable.sol) /// @author Modified from OpenZeppelin /// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol) /// /// Note: /// - This implementation is intended to be used with ERC1967 proxies. /// See: `LibClone.deployERC1967` and related functions. /// - This implementation is NOT compatible with legacy OpenZeppelin proxies /// which do not store the implementation at `_ERC1967_IMPLEMENTATION_SLOT`. abstract contract UUPSUpgradeable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The upgrade failed. error UpgradeFailed(); /// @dev The call is from an unauthorized call context. error UnauthorizedCallContext(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* IMMUTABLES */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev For checking if the context is a delegate call. uint256 private immutable __self = uint256(uint160(address(this))); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Emitted when the proxy's implementation is upgraded. event Upgraded(address indexed implementation); /// @dev `keccak256(bytes("Upgraded(address)"))`. uint256 private constant _UPGRADED_EVENT_SIGNATURE = 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ERC-1967 storage slot for the implementation in the proxy. /// `uint256(keccak256("eip1967.proxy.implementation")) - 1`. bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* UUPS OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Please override this function to check if `msg.sender` is authorized /// to upgrade the proxy to `newImplementation`, reverting if not. /// ``` /// function _authorizeUpgrade(address) internal override onlyOwner {} /// ``` function _authorizeUpgrade(address newImplementation) internal virtual; /// @dev Returns the storage slot used by the implementation, /// as specified in [ERC1822](https://eips.ethereum.org/EIPS/eip-1822). /// /// Note: The `notDelegated` modifier prevents accidental upgrades to /// an implementation that is a proxy contract. function proxiableUUID() public view virtual notDelegated returns (bytes32) { // This function must always return `_ERC1967_IMPLEMENTATION_SLOT` to comply with ERC1967. return _ERC1967_IMPLEMENTATION_SLOT; } /// @dev Upgrades the proxy's implementation to `newImplementation`. /// Emits a {Upgraded} event. /// /// Note: Passing in empty `data` skips the delegatecall to `newImplementation`. function upgradeToAndCall(address newImplementation, bytes calldata data) public payable virtual onlyProxy { _authorizeUpgrade(newImplementation); /// @solidity memory-safe-assembly assembly { newImplementation := shr(96, shl(96, newImplementation)) // Clears upper 96 bits. mstore(0x01, 0x52d1902d) // `proxiableUUID()`. let s := _ERC1967_IMPLEMENTATION_SLOT // Check if `newImplementation` implements `proxiableUUID` correctly. if iszero(eq(mload(staticcall(gas(), newImplementation, 0x1d, 0x04, 0x01, 0x20)), s)) { mstore(0x01, 0x55299b49) // `UpgradeFailed()`. revert(0x1d, 0x04) } // Emit the {Upgraded} event. log2(codesize(), 0x00, _UPGRADED_EVENT_SIGNATURE, newImplementation) sstore(s, newImplementation) // Updates the implementation. // Perform a delegatecall to `newImplementation` if `data` is non-empty. if data.length { // Forwards the `data` to `newImplementation` via delegatecall. let m := mload(0x40) calldatacopy(m, data.offset, data.length) if iszero(delegatecall(gas(), newImplementation, m, data.length, codesize(), 0x00)) { // Bubble up the revert if the call reverts. returndatacopy(m, 0x00, returndatasize()) revert(m, returndatasize()) } } } } /// @dev Requires that the execution is performed through a proxy. modifier onlyProxy() { uint256 s = __self; /// @solidity memory-safe-assembly assembly { // To enable use cases with an immutable default implementation in the bytecode, // (see: ERC6551Proxy), we don't require that the proxy address must match the // value stored in the implementation slot, which may not be initialized. if eq(s, address()) { mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`. revert(0x1c, 0x04) } } _; } /// @dev Requires that the execution is NOT performed via delegatecall. /// This is the opposite of `onlyProxy`. modifier notDelegated() { uint256 s = __self; /// @solidity memory-safe-assembly assembly { if iszero(eq(s, address())) { mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`. revert(0x1c, 0x04) } } _; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.23; import { Signer } from "./Signer.sol"; import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol"; /** * @notice A signer backed by an EOA or ERC-1271 smart account. */ type AccountSigner is address; /// @notice converts Signer to AccountSigner. function decodeAccountSigner(Signer memory signer_) pure returns (AccountSigner) { return AccountSigner.wrap(address(uint160(uint256(signer_.slot1)))); } using AccountSignerLib for AccountSigner global; /** * @notice Library for verifying AccountSigner signatures. * @custom:security-contract [email protected] * @author Splits (https://splits.org) */ library AccountSignerLib { /* -------------------------------------------------------------------------- */ /* FUNCTIONS */ /* -------------------------------------------------------------------------- */ /** * @notice Verifies if the `signer` has signed the provided `messageHash`. * * @param signer_ Account signer. * @param messageHash_ Message hash that should be signed by the signer. * @param signature_ abi.encode(r,s,v) signature. * @return isValid true when signer has signed the messageHash otherwise false. */ function isValidSignature( AccountSigner signer_, bytes32 messageHash_, bytes memory signature_ ) internal view returns (bool) { return SignatureCheckerLib.isValidSignatureNow( AccountSigner.unwrap(signer_), SignatureCheckerLib.toEthSignedMessageHash(messageHash_), signature_ ); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.23; import { Signer } from "./Signer.sol"; import { WebAuthn } from "src/library/WebAuthn.sol"; /** * @notice A signer backed by a passkey. */ struct PasskeySigner { uint256 x; uint256 y; } /// @notice converts Signer to PasskeySigner. function decodePasskeySigner(Signer memory signer_) pure returns (PasskeySigner memory) { return PasskeySigner(uint256(signer_.slot1), uint256(signer_.slot2)); } using PasskeySignerLib for PasskeySigner global; /** * @notice Library for verifying PasskeySigner signatures. * @custom:security-contract [email protected] * @author Splits (https://splits.org) */ library PasskeySignerLib { /* -------------------------------------------------------------------------- */ /* FUNCTIONS */ /* -------------------------------------------------------------------------- */ /** * @notice Verifies if the `signer` has signed the provided `messageHash`. * * @param signer_ Passkey signer. * @param messageHash_ Message hash that should be signed by the signer. * @param signature_ abi.encode(WebAuthn.WebAuthnAuth) signature. * @return isValid true when signer has signed the messageHash otherwise false. */ function isValidSignature( PasskeySigner memory signer_, bytes32 messageHash_, bytes memory signature_ ) internal view returns (bool) { WebAuthn.WebAuthnAuth memory auth = abi.decode(signature_, (WebAuthn.WebAuthnAuth)); return WebAuthn.verify({ challenge: abi.encode(messageHash_), requireUV: false, webAuthnAuth: auth, x: signer_.x, y: signer_.y }); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol) pragma solidity ^0.8.20; import {MessageHashUtils} from "./MessageHashUtils.sol"; import {ShortStrings, ShortString} from "../ShortStrings.sol"; import {IERC5267} from "../../interfaces/IERC5267.sol"; /** * @dev https://eips.ethereum.org/EIPS/eip-712[EIP-712] is a standard for hashing and signing of typed structured data. * * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`. * * This contract implements the EIP-712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA * ({_hashTypedDataV4}). * * The implementation of the domain separator was designed to be as efficient as possible while still properly updating * the chain id to protect against replay attacks on an eventual fork of the chain. * * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. * * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the * separator from the immutable values, which is cheaper than accessing a cached version in cold storage. * * @custom:oz-upgrades-unsafe-allow state-variable-immutable */ abstract contract EIP712 is IERC5267 { using ShortStrings for *; bytes32 private constant TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to // invalidate the cached domain separator if the chain id changes. bytes32 private immutable _cachedDomainSeparator; uint256 private immutable _cachedChainId; address private immutable _cachedThis; bytes32 private immutable _hashedName; bytes32 private immutable _hashedVersion; ShortString private immutable _name; ShortString private immutable _version; string private _nameFallback; string private _versionFallback; /** * @dev Initializes the domain separator and parameter caches. * * The meaning of `name` and `version` is specified in * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP-712]: * * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. * - `version`: the current major version of the signing domain. * * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart * contract upgrade]. */ constructor(string memory name, string memory version) { _name = name.toShortStringWithFallback(_nameFallback); _version = version.toShortStringWithFallback(_versionFallback); _hashedName = keccak256(bytes(name)); _hashedVersion = keccak256(bytes(version)); _cachedChainId = block.chainid; _cachedDomainSeparator = _buildDomainSeparator(); _cachedThis = address(this); } /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4() internal view returns (bytes32) { if (address(this) == _cachedThis && block.chainid == _cachedChainId) { return _cachedDomainSeparator; } else { return _buildDomainSeparator(); } } function _buildDomainSeparator() private view returns (bytes32) { return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this))); } /** * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this * function returns the hash of the fully encoded EIP712 message for this domain. * * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: * * ```solidity * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( * keccak256("Mail(address to,string contents)"), * mailTo, * keccak256(bytes(mailContents)) * ))); * address signer = ECDSA.recover(digest, signature); * ``` */ function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash); } /** * @dev See {IERC-5267}. */ function eip712Domain() public view virtual returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ) { return ( hex"0f", // 01111 _EIP712Name(), _EIP712Version(), block.chainid, address(this), bytes32(0), new uint256[](0) ); } /** * @dev The name parameter for the EIP712 domain. * * NOTE: By default this function reads _name which is an immutable value. * It only reads from storage if necessary (in case the value is too large to fit in a ShortString). */ // solhint-disable-next-line func-name-mixedcase function _EIP712Name() internal view returns (string memory) { return _name.toStringWithFallback(_nameFallback); } /** * @dev The version parameter for the EIP712 domain. * * NOTE: By default this function reads _version which is an immutable value. * It only reads from storage if necessary (in case the value is too large to fit in a ShortString). */ // solhint-disable-next-line func-name-mixedcase function _EIP712Version() internal view returns (string memory) { return _version.toStringWithFallback(_versionFallback); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Receiver mixin for ETH and safe-transferred ERC721 and ERC1155 tokens. /// @author Solady (https://github.com/Vectorized/solady/blob/main/src/accounts/Receiver.sol) /// /// @dev Note: /// - Handles all ERC721 and ERC1155 token safety callbacks. /// - Collapses function table gas overhead and code size. /// - Utilizes fallback so unknown calldata will pass on. abstract contract Receiver { /// @dev For receiving ETH. receive() external payable virtual {} /// @dev Fallback function with the `receiverFallback` modifier. fallback() external payable virtual receiverFallback {} /// @dev Modifier for the fallback function to handle token callbacks. modifier receiverFallback() virtual { /// @solidity memory-safe-assembly assembly { let s := shr(224, calldataload(0)) // 0x150b7a02: `onERC721Received(address,address,uint256,bytes)`. // 0xf23a6e61: `onERC1155Received(address,address,uint256,uint256,bytes)`. // 0xbc197c81: `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`. if or(eq(s, 0x150b7a02), or(eq(s, 0xf23a6e61), eq(s, 0xbc197c81))) { mstore(0x20, s) // Store `msg.sig`. return(0x3c, 0x20) // Return `msg.sig`. } } _; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.23; /** * @title Caller contract * @custom:security-contract [email protected] * @author Splits (https://splits.org) * @notice Contract that provides basic functionalities to make external calls from a smart contract. */ contract Caller { /* -------------------------------------------------------------------------- */ /* STRUCTS */ /* -------------------------------------------------------------------------- */ /// @notice Represents a call to execute. struct Call { /// @dev The address to call. address target; /// @dev The value to send when making the call. uint256 value; /// @dev The data of the call. bytes data; } /* -------------------------------------------------------------------------- */ /* INTERNAL/PRIVATE FUNCTIONS */ /* -------------------------------------------------------------------------- */ function _call(address target_, uint256 value_, bytes calldata data_) internal { (bool success, bytes memory result) = target_.call{ value: value_ }(data_); if (!success) { assembly ("memory-safe") { revert(add(result, 32), mload(result)) } } } function _call(Call calldata call_) internal { _call(call_.target, call_.value, call_.data); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Signature verification helper that supports both ECDSA signatures from EOAs /// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol) /// /// @dev Note: /// - The signature checking functions use the ecrecover precompile (0x1). /// - The `bytes memory signature` variants use the identity precompile (0x4) /// to copy memory internally. /// - Unlike ECDSA signatures, contract signatures are revocable. /// - 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 use signatures as unique identifiers: /// - 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. /// This implementation does NOT check if a signature is non-malleable. library SignatureCheckerLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SIGNATURE CHECKING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether `signature` is valid for `signer` and `hash`. /// If `signer` is a smart contract, the signature is validated with ERC1271. /// Otherwise, the signature is validated with `ECDSA.recover`. function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits of `signer` in case they are dirty. for { signer := shr(96, shl(96, signer)) } signer {} { let m := mload(0x40) mstore(0x00, hash) mstore(0x40, mload(add(signature, 0x20))) // `r`. if eq(mload(signature), 64) { let vs := mload(add(signature, 0x40)) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x60, shr(1, shl(1, vs))) // `s`. let t := staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { isValid := 1 mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } } if eq(mload(signature), 65) { mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. mstore(0x60, mload(add(signature, 0x40))) // `s`. let t := staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { isValid := 1 mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. // Copy the `signature` over. let n := add(0x20, mload(signature)) pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n)) // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. add(returndatasize(), 0x44), // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) break } } } /// @dev Returns whether `signature` is valid for `signer` and `hash`. /// If `signer` is a smart contract, the signature is validated with ERC1271. /// Otherwise, the signature is validated with `ECDSA.recover`. function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits of `signer` in case they are dirty. for { signer := shr(96, shl(96, signer)) } signer {} { let m := mload(0x40) mstore(0x00, hash) if eq(signature.length, 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`. let t := staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { isValid := 1 mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } } if eq(signature.length, 65) { mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`. let t := staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { isValid := 1 mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } } mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. mstore(add(m, 0x44), signature.length) // Copy the `signature` over. calldatacopy(add(m, 0x64), signature.offset, signature.length) // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. add(signature.length, 0x64), // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) break } } } /// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`. /// If `signer` is a smart contract, the signature is validated with ERC1271. /// Otherwise, the signature is validated with `ECDSA.recover`. function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits of `signer` in case they are dirty. for { signer := shr(96, shl(96, signer)) } signer {} { let m := mload(0x40) mstore(0x00, hash) mstore(0x20, add(shr(255, vs), 27)) // `v`. mstore(0x40, r) // `r`. mstore(0x60, shr(1, shl(1, vs))) // `s`. let t := staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { isValid := 1 mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. mstore(add(m, 0x44), 65) // Length of the signature. mstore(add(m, 0x64), r) // `r`. mstore(add(m, 0x84), mload(0x60)) // `s`. mstore8(add(m, 0xa4), mload(0x20)) // `v`. // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. 0xa5, // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } } } /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`. /// If `signer` is a smart contract, the signature is validated with ERC1271. /// Otherwise, the signature is validated with `ECDSA.recover`. function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits of `signer` in case they are dirty. for { signer := shr(96, shl(96, signer)) } signer {} { let m := mload(0x40) mstore(0x00, hash) mstore(0x20, and(v, 0xff)) // `v`. mstore(0x40, r) // `r`. mstore(0x60, s) // `s`. let t := staticcall( gas(), // Amount of gas left for the transaction. 1, // Address of `ecrecover`. 0x00, // Start of input. 0x80, // Size of input. 0x01, // Start of output. 0x20 // Size of output. ) // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { isValid := 1 mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. mstore(add(m, 0x44), 65) // Length of the signature. mstore(add(m, 0x64), r) // `r`. mstore(add(m, 0x84), s) // `s`. mstore8(add(m, 0xa4), v) // `v`. // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. 0xa5, // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) mstore(0x60, 0) // Restore the zero slot. mstore(0x40, m) // Restore the free memory pointer. break } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC1271 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract. function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. // Copy the `signature` over. let n := add(0x20, mload(signature)) pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n)) // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. add(returndatasize(), 0x44), // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) } } /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract. function isValidERC1271SignatureNowCalldata( address signer, bytes32 hash, bytes calldata signature ) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. mstore(add(m, 0x44), signature.length) // Copy the `signature` over. calldatacopy(add(m, 0x64), signature.offset, signature.length) // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. add(signature.length, 0x64), // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) } } /// @dev Returns whether the signature (`r`, `vs`) is valid for `hash` /// for an ERC1271 `signer` contract. function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. mstore(add(m, 0x44), 65) // Length of the signature. mstore(add(m, 0x64), r) // `r`. mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`. mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`. // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. 0xa5, // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) } } /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash` /// for an ERC1271 `signer` contract. function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) let f := shl(224, 0x1626ba7e) mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. mstore(add(m, 0x04), hash) let d := add(m, 0x24) mstore(d, 0x40) // The offset of the `signature` in the calldata. mstore(add(m, 0x44), 65) // Length of the signature. mstore(add(m, 0x64), r) // `r`. mstore(add(m, 0x84), s) // `s`. mstore8(add(m, 0xa4), v) // `v`. // forgefmt: disable-next-item isValid := and( // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). eq(mload(d), f), // Whether the staticcall does not revert. // This must be placed at the end of the `and` clause, // as the arguments are evaluated from right to left. staticcall( gas(), // Remaining gas. signer, // The `signer` address. m, // Offset of calldata in memory. 0xa5, // Length of calldata in memory. d, // Offset of returndata. 0x20 // Length of returndata to write. ) ) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* 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://eth.wiki/json-rpc/API#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://eth.wiki/json-rpc/API#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. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* 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 } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { FCL_ecdsa } from "FreshCryptoLib/FCL_ecdsa.sol"; import { FCL_Elliptic_ZZ } from "FreshCryptoLib/FCL_elliptic.sol"; import { Base64 } from "openzeppelin-contracts/contracts/utils/Base64.sol"; import { LibString } from "solady/utils/LibString.sol"; /// @title WebAuthn /// /// @notice A library for verifying WebAuthn Authentication Assertions, built off the work /// of Daimo and Coinbase. /// /// @dev Attempts to use the RIP-7212 precompile for signature verification. /// If precompile verification fails, it falls back to FreshCryptoLib. /// /// @author Coinbase (https://github.com/base-org/webauthn-sol) /// @author Daimo (https://github.com/daimo-eth/p256-verifier/blob/master/src/WebAuthn.sol) library WebAuthn { using LibString for string; struct WebAuthnAuth { /// @dev The WebAuthn authenticator data. /// See https://www.w3.org/TR/webauthn-2/#dom-authenticatorassertionresponse-authenticatordata. bytes authenticatorData; /// @dev The WebAuthn client data JSON. /// See https://www.w3.org/TR/webauthn-2/#dom-authenticatorresponse-clientdatajson. string clientDataJSON; /// @dev The index at which "challenge":"..." occurs in `clientDataJSON`. uint256 challengeIndex; /// @dev The index at which "type":"..." occurs in `clientDataJSON`. uint256 typeIndex; /// @dev The r value of secp256r1 signature uint256 r; /// @dev The s value of secp256r1 signature uint256 s; } /// @dev Bit 0 of the authenticator data struct, corresponding to the "User Present" bit. /// See https://www.w3.org/TR/webauthn-2/#flags. bytes1 private constant _AUTH_DATA_FLAGS_UP = 0x01; /// @dev Bit 2 of the authenticator data struct, corresponding to the "User Verified" bit. /// See https://www.w3.org/TR/webauthn-2/#flags. bytes1 private constant _AUTH_DATA_FLAGS_UV = 0x04; /// @dev Secp256r1 curve order / 2 used as guard to prevent signature malleability issue. uint256 private constant _P256_N_DIV_2 = FCL_Elliptic_ZZ.n / 2; /// @dev The precompiled contract address to use for signature verification in the “secp256r1” elliptic curve. /// See https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md. address private constant _VERIFIER = address(0x100); /// @dev The expected type (hash) in the client data JSON when verifying assertion signatures. /// See https://www.w3.org/TR/webauthn-2/#dom-collectedclientdata-type // solhint-disable-next-line bytes32 private constant _EXPECTED_TYPE_HASH = keccak256('"type":"webauthn.get"'); /// /// @notice Verifies a Webauthn Authentication Assertion as described /// in https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion. /// /// @dev We do not verify all the steps as described in the specification, only ones relevant to our context. /// Please carefully read through this list before usage. /// /// Specifically, we do verify the following: /// - Verify that authenticatorData (which comes from the authenticator, such as iCloud Keychain) indicates /// a well-formed assertion with the user present bit set. If `requireUV` is set, checks that the /// authenticator /// enforced user verification. User verification should be required if, and only if, /// options.userVerification /// is set to required in the request. /// - Verifies that the client JSON is of type "webauthn.get", i.e. the client was responding to a request /// to /// assert authentication. /// - Verifies that the client JSON contains the requested challenge. /// - Verifies that (r, s) constitute a valid signature over both the authenicatorData and client JSON, for /// public /// key (x, y). /// /// We make some assumptions about the particular use case of this verifier, so we do NOT verify the following: /// - Does NOT verify that the origin in the `clientDataJSON` matches the Relying Party's origin: tt is /// considered /// the authenticator's responsibility to ensure that the user is interacting with the correct RP. This is /// enforced by most high quality authenticators properly, particularly the iCloud Keychain and Google /// Password /// Manager were tested. /// - Does NOT verify That `topOrigin` in `clientDataJSON` is well-formed: We assume it would never be /// present, i.e. /// the credentials are never used in a cross-origin/iframe context. The website/app set up should /// disallow /// cross-origin usage of the credentials. This is the default behaviour for created credentials in common /// settings. /// - Does NOT verify that the `rpIdHash` in `authenticatorData` is the SHA-256 hash of the RP ID expected /// by the Relying /// Party: this means that we rely on the authenticator to properly enforce credentials to be used only by /// the correct RP. /// This is generally enforced with features like Apple App Site Association and Google Asset Links. To /// protect from /// edge cases in which a previously-linked RP ID is removed from the authorised RP IDs, we recommend that /// messages /// signed by the authenticator include some expiry mechanism. /// - Does NOT verify the credential backup state: this assumes the credential backup state is NOT used as /// part of Relying /// Party business logic or policy. /// - Does NOT verify the values of the client extension outputs: this assumes that the Relying Party does /// not use client /// extension outputs. /// - Does NOT verify the signature counter: signature counters are intended to enable risk scoring for the /// Relying Party. /// This assumes risk scoring is not used as part of Relying Party business logic or policy. /// - Does NOT verify the attestation object: this assumes that response.attestationObject is NOT present in /// the response, /// i.e. the RP does not intend to verify an attestation. /// /// @param challenge The challenge that was provided by the relying party. /// @param requireUV A boolean indicating whether user verification is required. /// @param webAuthnAuth The `WebAuthnAuth` struct. /// @param x The x coordinate of the public key. /// @param y The y coordinate of the public key. /// /// @return `true` if the authentication assertion passed validation, else `false`. function verify( bytes memory challenge, bool requireUV, WebAuthnAuth memory webAuthnAuth, uint256 x, uint256 y ) internal view returns (bool) { bool deferredResult = true; if (webAuthnAuth.s > _P256_N_DIV_2) { // guard against signature malleability deferredResult = false; } // 11. Verify that the value of C.type is the string webauthn.get. // bytes("type":"webauthn.get").length = 21 if ( keccak256(bytes(webAuthnAuth.clientDataJSON.slice(webAuthnAuth.typeIndex, webAuthnAuth.typeIndex + 21))) != _EXPECTED_TYPE_HASH ) { deferredResult = false; } // 12. Verify that the value of C.challenge equals the base64url encoding of options.challenge. // solhint-disable-next-line bytes memory expectedChallenge = bytes(string.concat('"challenge":"', Base64.encodeURL(challenge), '"')); string memory actualChallenge = webAuthnAuth.clientDataJSON.slice( webAuthnAuth.challengeIndex, webAuthnAuth.challengeIndex + expectedChallenge.length ); if (keccak256(bytes(actualChallenge)) != keccak256(expectedChallenge)) { deferredResult = false; } // Skip 13., 14., 15. // 16. Verify that the UP bit of the flags in authData is set. if (webAuthnAuth.authenticatorData[32] & _AUTH_DATA_FLAGS_UP != _AUTH_DATA_FLAGS_UP) { deferredResult = false; } // 17. If user verification is required for this assertion, verify that the User Verified bit of the flags in // authData is set. if (requireUV && (webAuthnAuth.authenticatorData[32] & _AUTH_DATA_FLAGS_UV) != _AUTH_DATA_FLAGS_UV) { deferredResult = false; } // skip 18. // 19. Let hash be the result of computing a hash over the cData using SHA-256. bytes32 clientDataJSONHash = sha256(bytes(webAuthnAuth.clientDataJSON)); // 20. Using credentialPublicKey, verify that sig is a valid signature over the binary concatenation of authData // and hash. bytes32 messageHash = sha256(abi.encodePacked(webAuthnAuth.authenticatorData, clientDataJSONHash)); bytes memory args = abi.encode(messageHash, webAuthnAuth.r, webAuthnAuth.s, x, y); // try the RIP-7212 precompile address (bool success, bytes memory ret) = _VERIFIER.staticcall(args); // staticcall will not revert if address has no code // check return length // note that even if precompile exists, ret.length is 0 when verification returns false // so an invalid signature will be checked twice: once by the precompile and once by FCL. // Ideally this signature failure is simulated offchain and no one actually pay this gas. bool valid = ret.length > 0; if (success && valid) return abi.decode(ret, (uint256)) == 1 && deferredResult; return FCL_ecdsa.ecdsa_verify(messageHash, webAuthnAuth.r, webAuthnAuth.s, x, y) && deferredResult; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol) pragma solidity ^0.8.20; import {Strings} from "../Strings.sol"; /** * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing. * * The library provides methods for generating a hash of a message that conforms to the * https://eips.ethereum.org/EIPS/eip-191[ERC-191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712] * specifications. */ library MessageHashUtils { /** * @dev Returns the keccak256 digest of an ERC-191 signed data with version * `0x45` (`personal_sign` messages). * * The digest is calculated by prefixing a bytes32 `messageHash` with * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with * keccak256, although any bytes32 value can be safely used because the final digest will * be re-hashed. * * See {ECDSA-recover}. */ function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) { /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20) } } /** * @dev Returns the keccak256 digest of an ERC-191 signed data with version * `0x45` (`personal_sign` messages). * * The digest is calculated by prefixing an arbitrary `message` with * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * * See {ECDSA-recover}. */ function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) { return keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message)); } /** * @dev Returns the keccak256 digest of an ERC-191 signed data with version * `0x00` (data with intended validator). * * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended * `validator` address. Then hashing the result. * * See {ECDSA-recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked(hex"19_00", validator, data)); } /** * @dev Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version `0x01`). * * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with * `\x19\x01` and hashing the result. It corresponds to the hash signed by the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. * * See {ECDSA-recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, hex"19_01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) digest := keccak256(ptr, 0x42) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ShortStrings.sol) pragma solidity ^0.8.20; import {StorageSlot} from "./StorageSlot.sol"; // | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | // | length | 0x BB | type ShortString is bytes32; /** * @dev This library provides functions to convert short memory strings * into a `ShortString` type that can be used as an immutable variable. * * Strings of arbitrary length can be optimized using this library if * they are short enough (up to 31 bytes) by packing them with their * length (1 byte) in a single EVM word (32 bytes). Additionally, a * fallback mechanism can be used for every other case. * * Usage example: * * ```solidity * contract Named { * using ShortStrings for *; * * ShortString private immutable _name; * string private _nameFallback; * * constructor(string memory contractName) { * _name = contractName.toShortStringWithFallback(_nameFallback); * } * * function name() external view returns (string memory) { * return _name.toStringWithFallback(_nameFallback); * } * } * ``` */ library ShortStrings { // Used as an identifier for strings longer than 31 bytes. bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF; error StringTooLong(string str); error InvalidShortString(); /** * @dev Encode a string of at most 31 chars into a `ShortString`. * * This will trigger a `StringTooLong` error is the input string is too long. */ function toShortString(string memory str) internal pure returns (ShortString) { bytes memory bstr = bytes(str); if (bstr.length > 31) { revert StringTooLong(str); } return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length)); } /** * @dev Decode a `ShortString` back to a "normal" string. */ function toString(ShortString sstr) internal pure returns (string memory) { uint256 len = byteLength(sstr); // using `new string(len)` would work locally but is not memory safe. string memory str = new string(32); /// @solidity memory-safe-assembly assembly { mstore(str, len) mstore(add(str, 0x20), sstr) } return str; } /** * @dev Return the length of a `ShortString`. */ function byteLength(ShortString sstr) internal pure returns (uint256) { uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF; if (result > 31) { revert InvalidShortString(); } return result; } /** * @dev Encode a string into a `ShortString`, or write it to storage if it is too long. */ function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) { if (bytes(value).length < 32) { return toShortString(value); } else { StorageSlot.getStringSlot(store).value = value; return ShortString.wrap(FALLBACK_SENTINEL); } } /** * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}. */ function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) { if (ShortString.unwrap(value) != FALLBACK_SENTINEL) { return toString(value); } else { return store; } } /** * @dev Return the length of a string that was encoded to `ShortString` or written to storage using * {setWithFallback}. * * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of * actual characters as the UTF-8 encoding of a single character can span over multiple bytes. */ function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) { if (ShortString.unwrap(value) != FALLBACK_SENTINEL) { return byteLength(value); } else { return bytes(store).length; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol) pragma solidity ^0.8.20; interface IERC5267 { /** * @dev MAY be emitted to signal that the domain could have changed. */ event EIP712DomainChanged(); /** * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712 * signature. */ function eip712Domain() external view returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ); }
//********************************************************************************************/ // ___ _ ___ _ _ _ _ // | __| _ ___ __| |_ / __|_ _ _ _ _ __| |_ ___ | | (_) |__ // | _| '_/ -_|_-< ' \ | (__| '_| || | '_ \ _/ _ \ | |__| | '_ \ // |_||_| \___/__/_||_| \___|_| \_, | .__/\__\___/ |____|_|_.__/ // |__/|_| ///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project ///* License: This software is licensed under MIT License ///* This Code may be reused including license and copyright notice. ///* See LICENSE file at the root folder of the project. ///* FILE: FCL_ecdsa.sol ///* ///* ///* DESCRIPTION: ecdsa verification implementation ///* //**************************************************************************************/ //* WARNING: this code SHALL not be used for non prime order curves for security reasons. // Code is optimized for a=-3 only curves with prime order, constant like -1, -2 shall be replaced // if ever used for other curve than sec256R1 // SPDX-License-Identifier: MIT pragma solidity >=0.8.19 <0.9.0; import { FCL_Elliptic_ZZ } from "./FCL_elliptic.sol"; library FCL_ecdsa { // Set parameters for curve sec256r1.public //curve order (number of points) uint256 constant n = FCL_Elliptic_ZZ.n; /** * @dev ECDSA verification, given , signature, and public key. */ /** * @dev ECDSA verification, given , signature, and public key, no calldata version */ function ecdsa_verify(bytes32 message, uint256 r, uint256 s, uint256 Qx, uint256 Qy) internal view returns (bool) { if (r == 0 || r >= FCL_Elliptic_ZZ.n || s == 0 || s >= FCL_Elliptic_ZZ.n) { return false; } if (!FCL_Elliptic_ZZ.ecAff_isOnCurve(Qx, Qy)) { return false; } uint256 sInv = FCL_Elliptic_ZZ.FCL_nModInv(s); uint256 scalar_u = mulmod(uint256(message), sInv, FCL_Elliptic_ZZ.n); uint256 scalar_v = mulmod(r, sInv, FCL_Elliptic_ZZ.n); uint256 x1; x1 = FCL_Elliptic_ZZ.ecZZ_mulmuladd_S_asm(Qx, Qy, scalar_u, scalar_v); x1 = addmod(x1, n - r, n); return x1 == 0; } function ec_recover_r1(uint256 h, uint256 v, uint256 r, uint256 s) internal view returns (address) { if (r == 0 || r >= FCL_Elliptic_ZZ.n || s == 0 || s >= FCL_Elliptic_ZZ.n) { return address(0); } uint256 y = FCL_Elliptic_ZZ.ec_Decompress(r, v - 27); uint256 rinv = FCL_Elliptic_ZZ.FCL_nModInv(r); uint256 u1 = mulmod(FCL_Elliptic_ZZ.n - addmod(0, h, FCL_Elliptic_ZZ.n), rinv, FCL_Elliptic_ZZ.n); //-hr^-1 uint256 u2 = mulmod(s, rinv, FCL_Elliptic_ZZ.n); //sr^-1 uint256 Qx; uint256 Qy; (Qx, Qy) = FCL_Elliptic_ZZ.ecZZ_mulmuladd(r, y, u1, u2); return address(uint160(uint256(keccak256(abi.encodePacked(Qx, Qy))))); } function ecdsa_precomputed_verify( bytes32 message, uint256 r, uint256 s, address Shamir8 ) internal view returns (bool) { if (r == 0 || r >= n || s == 0 || s >= n) { return false; } /* Q is pushed via the contract at address Shamir8 assumed to be correct if (!isOnCurve(Q[0], Q[1])) { return false; }*/ uint256 sInv = FCL_Elliptic_ZZ.FCL_nModInv(s); uint256 X; //Shamir 8 dimensions X = FCL_Elliptic_ZZ.ecZZ_mulmuladd_S8_extcode(mulmod(uint256(message), sInv, n), mulmod(r, sInv, n), Shamir8); X = addmod(X, n - r, n); return X == 0; } //end ecdsa_precomputed_verify() function ecdsa_precomputed_verify( bytes32 message, uint256[2] calldata rs, address Shamir8 ) internal view returns (bool) { uint256 r = rs[0]; uint256 s = rs[1]; if (r == 0 || r >= n || s == 0 || s >= n) { return false; } /* Q is pushed via the contract at address Shamir8 assumed to be correct if (!isOnCurve(Q[0], Q[1])) { return false; }*/ uint256 sInv = FCL_Elliptic_ZZ.FCL_nModInv(s); uint256 X; //Shamir 8 dimensions X = FCL_Elliptic_ZZ.ecZZ_mulmuladd_S8_extcode(mulmod(uint256(message), sInv, n), mulmod(r, sInv, n), Shamir8); X = addmod(X, n - r, n); return X == 0; } //end ecdsa_precomputed_verify() }
//********************************************************************************************/ // ___ _ ___ _ _ _ _ // | __| _ ___ __| |_ / __|_ _ _ _ _ __| |_ ___ | | (_) |__ // | _| '_/ -_|_-< ' \ | (__| '_| || | '_ \ _/ _ \ | |__| | '_ \ // |_||_| \___/__/_||_| \___|_| \_, | .__/\__\___/ |____|_|_.__/ // |__/|_| ///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project ///* License: This software is licensed under MIT License ///* This Code may be reused including license and copyright notice. ///* See LICENSE file at the root folder of the project. ///* FILE: FCL_elliptic.sol ///* ///* ///* DESCRIPTION: modified XYZZ system coordinates for EVM elliptic point multiplication ///* optimization ///* //**************************************************************************************/ //* WARNING: this code SHALL not be used for non prime order curves for security reasons. // Code is optimized for a=-3 only curves with prime order, constant like -1, -2 shall be replaced // if ever used for other curve than sec256R1 // SPDX-License-Identifier: MIT pragma solidity >=0.8.19 <0.9.0; library FCL_Elliptic_ZZ { // Set parameters for curve sec256r1. // address of the ModExp precompiled contract (Arbitrary-precision exponentiation under modulo) address constant MODEXP_PRECOMPILE = 0x0000000000000000000000000000000000000005; //curve prime field modulus uint256 constant p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; //short weierstrass first coefficient uint256 constant a = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC; //short weierstrass second coefficient uint256 constant b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B; //generating point affine coordinates uint256 constant gx = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296; uint256 constant gy = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5; //curve order (number of points) uint256 constant n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551; /* -2 mod p constant, used to speed up inversion and doubling (avoid negation)*/ uint256 constant minus_2 = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD; /* -2 mod n constant, used to speed up inversion*/ uint256 constant minus_2modn = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F; uint256 constant minus_1 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; //P+1 div 4 uint256 constant pp1div4=0x3fffffffc0000000400000000000000000000000400000000000000000000000; //arbitrary constant to express no quadratic residuosity uint256 constant _NOTSQUARE=0xFFFFFFFF00000002000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; uint256 constant _NOTONCURVE=0xFFFFFFFF00000003000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; /** * /* inversion mod n via a^(n-2), use of precompiled using little Fermat theorem */ function FCL_nModInv(uint256 u) internal view returns (uint256 result) { assembly { let pointer := mload(0x40) // Define length of base, exponent and modulus. 0x20 == 32 bytes mstore(pointer, 0x20) mstore(add(pointer, 0x20), 0x20) mstore(add(pointer, 0x40), 0x20) // Define variables base, exponent and modulus mstore(add(pointer, 0x60), u) mstore(add(pointer, 0x80), minus_2modn) mstore(add(pointer, 0xa0), n) // Call the precompiled contract 0x05 = ModExp if iszero(staticcall(not(0), 0x05, pointer, 0xc0, pointer, 0x20)) { revert(0, 0) } result := mload(pointer) } } /** * /* @dev inversion mod nusing little Fermat theorem via a^(n-2), use of precompiled */ function FCL_pModInv(uint256 u) internal view returns (uint256 result) { assembly { let pointer := mload(0x40) // Define length of base, exponent and modulus. 0x20 == 32 bytes mstore(pointer, 0x20) mstore(add(pointer, 0x20), 0x20) mstore(add(pointer, 0x40), 0x20) // Define variables base, exponent and modulus mstore(add(pointer, 0x60), u) mstore(add(pointer, 0x80), minus_2) mstore(add(pointer, 0xa0), p) // Call the precompiled contract 0x05 = ModExp if iszero(staticcall(not(0), 0x05, pointer, 0xc0, pointer, 0x20)) { revert(0, 0) } result := mload(pointer) } } //Coron projective shuffling, take as input alpha as blinding factor function ecZZ_Coronize(uint256 alpha, uint256 x, uint256 y, uint256 zz, uint256 zzz) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) { uint256 alpha2=mulmod(alpha,alpha,p); x3=mulmod(alpha2, x,p); //alpha^-2.x y3=mulmod(mulmod(alpha, alpha2,p), y,p); zz3=mulmod(zz,alpha2,p);//alpha^2 zz zzz3=mulmod(zzz,mulmod(alpha, alpha2,p),p);//alpha^3 zzz return (x3, y3, zz3, zzz3); } function ecZZ_Add(uint256 x1, uint256 y1, uint256 zz1, uint256 zzz1, uint256 x2, uint256 y2, uint256 zz2, uint256 zzz2) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) { uint256 u1=mulmod(x1,zz2,p); // U1 = X1*ZZ2 uint256 u2=mulmod(x2, zz1,p); // U2 = X2*ZZ1 u2=addmod(u2, p-u1, p);// P = U2-U1 x1=mulmod(u2, u2, p);//PP x2=mulmod(x1, u2, p);//PPP zz3=mulmod(x1, mulmod(zz1, zz2, p),p);//ZZ3 = ZZ1*ZZ2*PP zzz3=mulmod(zzz1, mulmod(zzz2, x2, p),p);//ZZZ3 = ZZZ1*ZZZ2*PPP zz1=mulmod(y1, zzz2,p); // S1 = Y1*ZZZ2 zz2=mulmod(y2, zzz1, p); // S2 = Y2*ZZZ1 zz2=addmod(zz2, p-zz1, p);//R = S2-S1 zzz1=mulmod(u1, x1,p); //Q = U1*PP x3= addmod(addmod(mulmod(zz2, zz2, p), p-x2,p), mulmod(minus_2, zzz1,p),p); //X3 = R2-PPP-2*Q y3=addmod( mulmod(zz2, addmod(zzz1, p-x3, p),p), p-mulmod(zz1, x2, p),p);//R*(Q-X3)-S1*PPP return (x3, y3, zz3, zzz3); } /// @notice Calculate one modular square root of a given integer. Assume that p=3 mod 4. /// @dev Uses the ModExp precompiled contract at address 0x05 for fast computation using little Fermat theorem /// @param self The integer of which to find the modular inverse /// @return result The modular inverse of the input integer. If the modular inverse doesn't exist, it revert the tx function SqrtMod(uint256 self) internal view returns (uint256 result){ assembly ("memory-safe") { // load the free memory pointer value let pointer := mload(0x40) // Define length of base (Bsize) mstore(pointer, 0x20) // Define the exponent size (Esize) mstore(add(pointer, 0x20), 0x20) // Define the modulus size (Msize) mstore(add(pointer, 0x40), 0x20) // Define variables base (B) mstore(add(pointer, 0x60), self) // Define the exponent (E) mstore(add(pointer, 0x80), pp1div4) // We save the point of the last argument, it will be override by the result // of the precompile call in order to avoid paying for the memory expansion properly let _result := add(pointer, 0xa0) // Define the modulus (M) mstore(_result, p) // Call the precompiled ModExp (0x05) https://www.evm.codes/precompiled#0x05 if iszero( staticcall( not(0), // amount of gas to send MODEXP_PRECOMPILE, // target pointer, // argsOffset 0xc0, // argsSize (6 * 32 bytes) _result, // retOffset (we override M to avoid paying for the memory expansion) 0x20 // retSize (32 bytes) ) ) { revert(0, 0) } result := mload(_result) // result :=addmod(result,0,p) } if(mulmod(result,result,p)!=self){ result=_NOTSQUARE; } return result; } /** * /* @dev Convert from affine rep to XYZZ rep */ function ecAff_SetZZ(uint256 x0, uint256 y0) internal pure returns (uint256[4] memory P) { unchecked { P[2] = 1; //ZZ P[3] = 1; //ZZZ P[0] = x0; P[1] = y0; } } function ec_Decompress(uint256 x, uint256 parity) internal view returns(uint256 y){ uint256 y2=mulmod(x,mulmod(x,x,p),p);//x3 y2=addmod(b,addmod(y2,mulmod(x,a,p),p),p);//x3+ax+b y=SqrtMod(y2); if(y==_NOTSQUARE){ return _NOTONCURVE; } if((y&1)!=(parity&1)){ y=p-y; } } /** * /* @dev Convert from XYZZ rep to affine rep */ /* https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz-3.html#addition-add-2008-s*/ function ecZZ_SetAff(uint256 x, uint256 y, uint256 zz, uint256 zzz) internal view returns (uint256 x1, uint256 y1) { uint256 zzzInv = FCL_pModInv(zzz); //1/zzz y1 = mulmod(y, zzzInv, p); //Y/zzz uint256 _b = mulmod(zz, zzzInv, p); //1/z zzzInv = mulmod(_b, _b, p); //1/zz x1 = mulmod(x, zzzInv, p); //X/zz } /** * /* @dev Sutherland2008 doubling */ /* The "dbl-2008-s-1" doubling formulas */ function ecZZ_Dbl(uint256 x, uint256 y, uint256 zz, uint256 zzz) internal pure returns (uint256 P0, uint256 P1, uint256 P2, uint256 P3) { unchecked { assembly { P0 := mulmod(2, y, p) //U = 2*Y1 P2 := mulmod(P0, P0, p) // V=U^2 P3 := mulmod(x, P2, p) // S = X1*V P1 := mulmod(P0, P2, p) // W=UV P2 := mulmod(P2, zz, p) //zz3=V*ZZ1 zz := mulmod(3, mulmod(addmod(x, sub(p, zz), p), addmod(x, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1) P0 := addmod(mulmod(zz, zz, p), mulmod(minus_2, P3, p), p) //X3=M^2-2S x := mulmod(zz, addmod(P3, sub(p, P0), p), p) //M(S-X3) P3 := mulmod(P1, zzz, p) //zzz3=W*zzz1 P1 := addmod(x, sub(p, mulmod(P1, y, p)), p) //Y3= M(S-X3)-W*Y1 } } return (P0, P1, P2, P3); } /** * @dev Sutherland2008 add a ZZ point with a normalized point and greedy formulae * warning: assume that P1(x1,y1)!=P2(x2,y2), true in multiplication loop with prime order (cofactor 1) */ function ecZZ_AddN(uint256 x1, uint256 y1, uint256 zz1, uint256 zzz1, uint256 x2, uint256 y2) internal pure returns (uint256 P0, uint256 P1, uint256 P2, uint256 P3) { unchecked { if (y1 == 0) { return (x2, y2, 1, 1); } assembly { y1 := sub(p, y1) y2 := addmod(mulmod(y2, zzz1, p), y1, p) x2 := addmod(mulmod(x2, zz1, p), sub(p, x1), p) P0 := mulmod(x2, x2, p) //PP = P^2 P1 := mulmod(P0, x2, p) //PPP = P*PP P2 := mulmod(zz1, P0, p) ////ZZ3 = ZZ1*PP P3 := mulmod(zzz1, P1, p) ////ZZZ3 = ZZZ1*PPP zz1 := mulmod(x1, P0, p) //Q = X1*PP P0 := addmod(addmod(mulmod(y2, y2, p), sub(p, P1), p), mulmod(minus_2, zz1, p), p) //R^2-PPP-2*Q P1 := addmod(mulmod(addmod(zz1, sub(p, P0), p), y2, p), mulmod(y1, P1, p), p) //R*(Q-X3) } //end assembly } //end unchecked return (P0, P1, P2, P3); } /** * @dev Return the zero curve in XYZZ coordinates. */ function ecZZ_SetZero() internal pure returns (uint256 x, uint256 y, uint256 zz, uint256 zzz) { return (0, 0, 0, 0); } /** * @dev Check if point is the neutral of the curve */ // uint256 x0, uint256 y0, uint256 zz0, uint256 zzz0 function ecZZ_IsZero(uint256, uint256 y0, uint256, uint256) internal pure returns (bool) { return y0 == 0; } /** * @dev Return the zero curve in affine coordinates. Compatible with the double formulae (no special case) */ function ecAff_SetZero() internal pure returns (uint256 x, uint256 y) { return (0, 0); } /** * @dev Check if the curve is the zero curve in affine rep. */ // uint256 x, uint256 y) function ecAff_IsZero(uint256, uint256 y) internal pure returns (bool flag) { return (y == 0); } /** * @dev Check if a point in affine coordinates is on the curve (reject Neutral that is indeed on the curve). */ function ecAff_isOnCurve(uint256 x, uint256 y) internal pure returns (bool) { if (x >= p || y >= p || ((x == 0) && (y == 0))) { return false; } unchecked { uint256 LHS = mulmod(y, y, p); // y^2 uint256 RHS = addmod(mulmod(mulmod(x, x, p), x, p), mulmod(x, a, p), p); // x^3+ax RHS = addmod(RHS, b, p); // x^3 + a*x + b return LHS == RHS; } } /** * @dev Add two elliptic curve points in affine coordinates. Deal with P=Q */ function ecAff_add(uint256 x0, uint256 y0, uint256 x1, uint256 y1) internal view returns (uint256, uint256) { uint256 zz0; uint256 zzz0; if (ecAff_IsZero(x0, y0)) return (x1, y1); if (ecAff_IsZero(x1, y1)) return (x0, y0); if((x0==x1)&&(y0==y1)) { (x0, y0, zz0, zzz0) = ecZZ_Dbl(x0, y0,1,1); } else{ (x0, y0, zz0, zzz0) = ecZZ_AddN(x0, y0, 1, 1, x1, y1); } return ecZZ_SetAff(x0, y0, zz0, zzz0); } /** * @dev Computation of uG+vQ using Strauss-Shamir's trick, G basepoint, Q public key * Returns only x for ECDSA use * */ function ecZZ_mulmuladd_S_asm( uint256 Q0, uint256 Q1, //affine rep for input point Q uint256 scalar_u, uint256 scalar_v ) internal view returns (uint256 X) { uint256 zz; uint256 zzz; uint256 Y; uint256 index = 255; uint256 H0; uint256 H1; unchecked { if (scalar_u == 0 && scalar_v == 0) return 0; (H0, H1) = ecAff_add(gx, gy, Q0, Q1); if((H0==0)&&(H1==0))//handling Q=-G { scalar_u=addmod(scalar_u, n-scalar_v, n); scalar_v=0; if (scalar_u == 0 && scalar_v == 0) return 0; } assembly { for { let T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) } eq(T4, 0) { index := sub(index, 1) T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) } {} zz := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) if eq(zz, 1) { X := gx Y := gy } if eq(zz, 2) { X := Q0 Y := Q1 } if eq(zz, 3) { X := H0 Y := H1 } index := sub(index, 1) zz := 1 zzz := 1 for {} gt(minus_1, index) { index := sub(index, 1) } { // inlined EcZZ_Dbl let T1 := mulmod(2, Y, p) //U = 2*Y1, y free let T2 := mulmod(T1, T1, p) // V=U^2 let T3 := mulmod(X, T2, p) // S = X1*V T1 := mulmod(T1, T2, p) // W=UV let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1) zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1 zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S T2 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S) Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd { //value of dibit T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) if iszero(T4) { Y := sub(p, Y) //restore the -Y inversion continue } // if T4!=0 if eq(T4, 1) { T1 := gx T2 := gy } if eq(T4, 2) { T1 := Q0 T2 := Q1 } if eq(T4, 3) { T1 := H0 T2 := H1 } if iszero(zz) { X := T1 Y := T2 zz := 1 zzz := 1 continue } // inlined EcZZ_AddN //T3:=sub(p, Y) //T3:=Y let y2 := addmod(mulmod(T2, zzz, p), Y, p) //R T2 := addmod(mulmod(T1, zz, p), sub(p, X), p) //P //special extremely rare case accumulator where EcAdd is replaced by EcDbl, no need to optimize this //todo : construct edge vector case if iszero(y2) { if iszero(T2) { T1 := mulmod(minus_2, Y, p) //U = 2*Y1, y free T2 := mulmod(T1, T1, p) // V=U^2 T3 := mulmod(X, T2, p) // S = X1*V T1 := mulmod(T1, T2, p) // W=UV y2 := mulmod(addmod(X, zz, p), addmod(X, sub(p, zz), p), p) //(X-ZZ)(X+ZZ) T4 := mulmod(3, y2, p) //M=3*(X-ZZ)(X+ZZ) zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1 zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3) Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1 continue } } T4 := mulmod(T2, T2, p) //PP let TT1 := mulmod(T4, T2, p) //PPP, this one could be spared, but adding this register spare gas zz := mulmod(zz, T4, p) zzz := mulmod(zzz, TT1, p) //zz3=V*ZZ1 let TT2 := mulmod(X, T4, p) T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, TT1), p), mulmod(minus_2, TT2, p), p) Y := addmod(mulmod(addmod(TT2, sub(p, T4), p), y2, p), mulmod(Y, TT1, p), p) X := T4 } } //end loop let T := mload(0x40) mstore(add(T, 0x60), zz) //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz); //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile: // Define length of base, exponent and modulus. 0x20 == 32 bytes mstore(T, 0x20) mstore(add(T, 0x20), 0x20) mstore(add(T, 0x40), 0x20) // Define variables base, exponent and modulus //mstore(add(pointer, 0x60), u) mstore(add(T, 0x80), minus_2) mstore(add(T, 0xa0), p) // Call the precompiled contract 0x05 = ModExp if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) } //Y:=mulmod(Y,zzz,p)//Y/zzz //zz :=mulmod(zz, mload(T),p) //1/z //zz:= mulmod(zz,zz,p) //1/zz X := mulmod(X, mload(T), p) //X/zz } //end assembly } //end unchecked return X; } /** * @dev Computation of uG+vQ using Strauss-Shamir's trick, G basepoint, Q public key * Returns affine representation of point (normalized) * */ function ecZZ_mulmuladd( uint256 Q0, uint256 Q1, //affine rep for input point Q uint256 scalar_u, uint256 scalar_v ) internal view returns (uint256 X, uint256 Y) { uint256 zz; uint256 zzz; uint256 index = 255; uint256[6] memory T; uint256[2] memory H; unchecked { if (scalar_u == 0 && scalar_v == 0) return (0,0); (H[0], H[1]) = ecAff_add(gx, gy, Q0, Q1); //will not work if Q=P, obvious forbidden private key assembly { for { let T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) } eq(T4, 0) { index := sub(index, 1) T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) } {} zz := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) if eq(zz, 1) { X := gx Y := gy } if eq(zz, 2) { X := Q0 Y := Q1 } if eq(zz, 3) { Y := mload(add(H,32)) X := mload(H) } index := sub(index, 1) zz := 1 zzz := 1 for {} gt(minus_1, index) { index := sub(index, 1) } { // inlined EcZZ_Dbl let T1 := mulmod(2, Y, p) //U = 2*Y1, y free let T2 := mulmod(T1, T1, p) // V=U^2 let T3 := mulmod(X, T2, p) // S = X1*V T1 := mulmod(T1, T2, p) // W=UV let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1) zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1 zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S T2 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S) Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd { //value of dibit T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) if iszero(T4) { Y := sub(p, Y) //restore the -Y inversion continue } // if T4!=0 if eq(T4, 1) { T1 := gx T2 := gy } if eq(T4, 2) { T1 := Q0 T2 := Q1 } if eq(T4, 3) { T1 := mload(H) T2 := mload(add(H,32)) } if iszero(zz) { X := T1 Y := T2 zz := 1 zzz := 1 continue } // inlined EcZZ_AddN //T3:=sub(p, Y) //T3:=Y let y2 := addmod(mulmod(T2, zzz, p), Y, p) //R T2 := addmod(mulmod(T1, zz, p), sub(p, X), p) //P //special extremely rare case accumulator where EcAdd is replaced by EcDbl, no need to optimize this //todo : construct edge vector case if iszero(y2) { if iszero(T2) { T1 := mulmod(minus_2, Y, p) //U = 2*Y1, y free T2 := mulmod(T1, T1, p) // V=U^2 T3 := mulmod(X, T2, p) // S = X1*V T1 := mulmod(T1, T2, p) // W=UV y2 := mulmod(addmod(X, zz, p), addmod(X, sub(p, zz), p), p) //(X-ZZ)(X+ZZ) T4 := mulmod(3, y2, p) //M=3*(X-ZZ)(X+ZZ) zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1 zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3) Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1 continue } } T4 := mulmod(T2, T2, p) //PP let TT1 := mulmod(T4, T2, p) //PPP, this one could be spared, but adding this register spare gas zz := mulmod(zz, T4, p) zzz := mulmod(zzz, TT1, p) //zz3=V*ZZ1 let TT2 := mulmod(X, T4, p) T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, TT1), p), mulmod(minus_2, TT2, p), p) Y := addmod(mulmod(addmod(TT2, sub(p, T4), p), y2, p), mulmod(Y, TT1, p), p) X := T4 } } //end loop mstore(add(T, 0x60), zzz) //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz); //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile: // Define length of base, exponent and modulus. 0x20 == 32 bytes mstore(T, 0x20) mstore(add(T, 0x20), 0x20) mstore(add(T, 0x40), 0x20) // Define variables base, exponent and modulus //mstore(add(pointer, 0x60), u) mstore(add(T, 0x80), minus_2) mstore(add(T, 0xa0), p) // Call the precompiled contract 0x05 = ModExp if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) } Y:=mulmod(Y,mload(T),p)//Y/zzz zz :=mulmod(zz, mload(T),p) //1/z zz:= mulmod(zz,zz,p) //1/zz X := mulmod(X, zz, p) //X/zz } //end assembly } //end unchecked return (X,Y); } //8 dimensions Shamir's trick, using precomputations stored in Shamir8, stored as Bytecode of an external //contract at given address dataPointer //(thx to Lakhdar https://github.com/Kelvyne for EVM storage explanations and tricks) // the external tool to generate tables from public key is in the /sage directory function ecZZ_mulmuladd_S8_extcode(uint256 scalar_u, uint256 scalar_v, address dataPointer) internal view returns (uint256 X /*, uint Y*/ ) { unchecked { uint256 zz; // third and coordinates of the point uint256[6] memory T; zz = 256; //start index while (T[0] == 0) { zz = zz - 1; //tbd case of msb octobit is null T[0] = 64 * ( 128 * ((scalar_v >> zz) & 1) + 64 * ((scalar_v >> (zz - 64)) & 1) + 32 * ((scalar_v >> (zz - 128)) & 1) + 16 * ((scalar_v >> (zz - 192)) & 1) + 8 * ((scalar_u >> zz) & 1) + 4 * ((scalar_u >> (zz - 64)) & 1) + 2 * ((scalar_u >> (zz - 128)) & 1) + ((scalar_u >> (zz - 192)) & 1) ); } assembly { extcodecopy(dataPointer, T, mload(T), 64) let index := sub(zz, 1) X := mload(T) let Y := mload(add(T, 32)) let zzz := 1 zz := 1 //loop over 1/4 of scalars thx to Shamir's trick over 8 points for {} gt(index, 191) { index := add(index, 191) } { //inline Double { let TT1 := mulmod(2, Y, p) //U = 2*Y1, y free let T2 := mulmod(TT1, TT1, p) // V=U^2 let T3 := mulmod(X, T2, p) // S = X1*V let T1 := mulmod(TT1, T2, p) // W=UV let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1) zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1 zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S //T2:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3) let T5 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S) //Y:= addmod(T2, sub(p, mulmod(T1, Y ,p)),p )//Y3= M(S-X3)-W*Y1 Y := addmod(mulmod(T1, Y, p), T5, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd /* compute element to access in precomputed table */ } { let T4 := add(shl(13, and(shr(index, scalar_v), 1)), shl(9, and(shr(index, scalar_u), 1))) let index2 := sub(index, 64) let T3 := add(T4, add(shl(12, and(shr(index2, scalar_v), 1)), shl(8, and(shr(index2, scalar_u), 1)))) let index3 := sub(index2, 64) let T2 := add(T3, add(shl(11, and(shr(index3, scalar_v), 1)), shl(7, and(shr(index3, scalar_u), 1)))) index := sub(index3, 64) let T1 := add(T2, add(shl(10, and(shr(index, scalar_v), 1)), shl(6, and(shr(index, scalar_u), 1)))) //tbd: check validity of formulae with (0,1) to remove conditional jump if iszero(T1) { Y := sub(p, Y) continue } extcodecopy(dataPointer, T, T1, 64) } { /* Access to precomputed table using extcodecopy hack */ // inlined EcZZ_AddN if iszero(zz) { X := mload(T) Y := mload(add(T, 32)) zz := 1 zzz := 1 continue } let y2 := addmod(mulmod(mload(add(T, 32)), zzz, p), Y, p) let T2 := addmod(mulmod(mload(T), zz, p), sub(p, X), p) //special case ecAdd(P,P)=EcDbl if iszero(y2) { if iszero(T2) { let T1 := mulmod(minus_2, Y, p) //U = 2*Y1, y free T2 := mulmod(T1, T1, p) // V=U^2 let T3 := mulmod(X, T2, p) // S = X1*V T1 := mulmod(T1, T2, p) // W=UV y2 := mulmod(addmod(X, zz, p), addmod(X, sub(p, zz), p), p) //(X-ZZ)(X+ZZ) let T4 := mulmod(3, y2, p) //M=3*(X-ZZ)(X+ZZ) zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1 zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3) Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1 continue } } let T4 := mulmod(T2, T2, p) let T1 := mulmod(T4, T2, p) // zz := mulmod(zz, T4, p) //zzz3=V*ZZ1 zzz := mulmod(zzz, T1, p) // W=UV/ let zz1 := mulmod(X, T4, p) X := addmod(addmod(mulmod(y2, y2, p), sub(p, T1), p), mulmod(minus_2, zz1, p), p) Y := addmod(mulmod(addmod(zz1, sub(p, X), p), y2, p), mulmod(Y, T1, p), p) } } //end loop mstore(add(T, 0x60), zz) //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz); //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile: // Define length of base, exponent and modulus. 0x20 == 32 bytes mstore(T, 0x20) mstore(add(T, 0x20), 0x20) mstore(add(T, 0x40), 0x20) // Define variables base, exponent and modulus //mstore(add(pointer, 0x60), u) mstore(add(T, 0x80), minus_2) mstore(add(T, 0xa0), p) // Call the precompiled contract 0x05 = ModExp if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) } zz := mload(T) X := mulmod(X, zz, p) //X/zz } } //end unchecked } // improving the extcodecopy trick : append array at end of contract function ecZZ_mulmuladd_S8_hackmem(uint256 scalar_u, uint256 scalar_v, uint256 dataPointer) internal view returns (uint256 X /*, uint Y*/ ) { uint256 zz; // third and coordinates of the point uint256[6] memory T; zz = 256; //start index unchecked { while (T[0] == 0) { zz = zz - 1; //tbd case of msb octobit is null T[0] = 64 * ( 128 * ((scalar_v >> zz) & 1) + 64 * ((scalar_v >> (zz - 64)) & 1) + 32 * ((scalar_v >> (zz - 128)) & 1) + 16 * ((scalar_v >> (zz - 192)) & 1) + 8 * ((scalar_u >> zz) & 1) + 4 * ((scalar_u >> (zz - 64)) & 1) + 2 * ((scalar_u >> (zz - 128)) & 1) + ((scalar_u >> (zz - 192)) & 1) ); } assembly { codecopy(T, add(mload(T), dataPointer), 64) X := mload(T) let Y := mload(add(T, 32)) let zzz := 1 zz := 1 //loop over 1/4 of scalars thx to Shamir's trick over 8 points for { let index := 254 } gt(index, 191) { index := add(index, 191) } { let T1 := mulmod(2, Y, p) //U = 2*Y1, y free let T2 := mulmod(T1, T1, p) // V=U^2 let T3 := mulmod(X, T2, p) // S = X1*V T1 := mulmod(T1, T2, p) // W=UV let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1) zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1 zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S //T2:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3) T2 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S) //Y:= addmod(T2, sub(p, mulmod(T1, Y ,p)),p )//Y3= M(S-X3)-W*Y1 Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd /* compute element to access in precomputed table */ T4 := add(shl(13, and(shr(index, scalar_v), 1)), shl(9, and(shr(index, scalar_u), 1))) index := sub(index, 64) T4 := add(T4, add(shl(12, and(shr(index, scalar_v), 1)), shl(8, and(shr(index, scalar_u), 1)))) index := sub(index, 64) T4 := add(T4, add(shl(11, and(shr(index, scalar_v), 1)), shl(7, and(shr(index, scalar_u), 1)))) index := sub(index, 64) T4 := add(T4, add(shl(10, and(shr(index, scalar_v), 1)), shl(6, and(shr(index, scalar_u), 1)))) //index:=add(index,192), restore index, interleaved with loop //tbd: check validity of formulae with (0,1) to remove conditional jump if iszero(T4) { Y := sub(p, Y) continue } { /* Access to precomputed table using extcodecopy hack */ codecopy(T, add(T4, dataPointer), 64) // inlined EcZZ_AddN let y2 := addmod(mulmod(mload(add(T, 32)), zzz, p), Y, p) T2 := addmod(mulmod(mload(T), zz, p), sub(p, X), p) T4 := mulmod(T2, T2, p) T1 := mulmod(T4, T2, p) T2 := mulmod(zz, T4, p) // W=UV zzz := mulmod(zzz, T1, p) //zz3=V*ZZ1 let zz1 := mulmod(X, T4, p) T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, T1), p), mulmod(minus_2, zz1, p), p) Y := addmod(mulmod(addmod(zz1, sub(p, T4), p), y2, p), mulmod(Y, T1, p), p) zz := T2 X := T4 } } //end loop mstore(add(T, 0x60), zz) //(X,Y)=ecZZ_SetAff(X,Y,zz, zzz); //T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile: // Define length of base, exponent and modulus. 0x20 == 32 bytes mstore(T, 0x20) mstore(add(T, 0x20), 0x20) mstore(add(T, 0x40), 0x20) // Define variables base, exponent and modulus //mstore(add(pointer, 0x60), u) mstore(add(T, 0x80), minus_2) mstore(add(T, 0xa0), p) // Call the precompiled contract 0x05 = ModExp if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) } zz := mload(T) X := mulmod(X, zz, p) //X/zz } } //end unchecked } /** * @dev ECDSA verification using a precomputed table of multiples of P and Q stored in contract at address Shamir8 * generation of contract bytecode for precomputations is done using sagemath code * (see sage directory, WebAuthn_precompute.sage) */ /** * @dev ECDSA verification using a precomputed table of multiples of P and Q appended at end of contract at address endcontract * generation of contract bytecode for precomputations is done using sagemath code * (see sage directory, WebAuthn_precompute.sage) */ function ecdsa_precomputed_hackmem(bytes32 message, uint256[2] calldata rs, uint256 endcontract) internal view returns (bool) { uint256 r = rs[0]; uint256 s = rs[1]; if (r == 0 || r >= n || s == 0 || s >= n) { return false; } /* Q is pushed via bytecode assumed to be correct if (!isOnCurve(Q[0], Q[1])) { return false; }*/ uint256 sInv = FCL_nModInv(s); uint256 X; //Shamir 8 dimensions X = ecZZ_mulmuladd_S8_hackmem(mulmod(uint256(message), sInv, n), mulmod(r, sInv, n), endcontract); assembly { X := addmod(X, sub(n, r), n) } return X == 0; } //end ecdsa_precomputed_verify() } //EOF
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.2) (utils/Base64.sol) pragma solidity ^0.8.20; /** * @dev Provides a set of functions to operate with Base64 strings. */ library Base64 { /** * @dev Base64 Encoding/Decoding Table * See sections 4 and 5 of https://datatracker.ietf.org/doc/html/rfc4648 */ string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; string internal constant _TABLE_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; /** * @dev Converts a `bytes` to its Bytes64 `string` representation. */ function encode(bytes memory data) internal pure returns (string memory) { return _encode(data, _TABLE, true); } /** * @dev Converts a `bytes` to its Bytes64Url `string` representation. */ function encodeURL(bytes memory data) internal pure returns (string memory) { return _encode(data, _TABLE_URL, false); } /** * @dev Internal table-agnostic conversion */ function _encode(bytes memory data, string memory table, bool withPadding) private pure returns (string memory) { /** * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol */ if (data.length == 0) return ""; // If padding is enabled, the final length should be `bytes` data length divided by 3 rounded up and then // multiplied by 4 so that it leaves room for padding the last chunk // - `data.length + 2` -> Round up // - `/ 3` -> Number of 3-bytes chunks // - `4 *` -> 4 characters for each chunk // If padding is disabled, the final length should be `bytes` data length multiplied by 4/3 rounded up as // opposed to when padding is required to fill the last chunk. // - `4 *` -> 4 characters for each chunk // - `data.length + 2` -> Round up // - `/ 3` -> Number of 3-bytes chunks uint256 resultLength = withPadding ? 4 * ((data.length + 2) / 3) : (4 * data.length + 2) / 3; string memory result = new string(resultLength); /// @solidity memory-safe-assembly assembly { // Prepare the lookup table (skip the first "length" byte) let tablePtr := add(table, 1) // Prepare result pointer, jump over length let resultPtr := add(result, 0x20) let dataPtr := data let endPtr := add(data, mload(data)) // In some cases, the last iteration will read bytes after the end of the data. We cache the value, and // set it to zero to make sure no dirty bytes are read in that section. let afterPtr := add(endPtr, 0x20) let afterCache := mload(afterPtr) mstore(afterPtr, 0x00) // Run over the input, 3 bytes at a time for { } lt(dataPtr, endPtr) { } { // Advance 3 bytes dataPtr := add(dataPtr, 3) let input := mload(dataPtr) // To write each character, shift the 3 byte (24 bits) chunk // 4 times in blocks of 6 bits for each character (18, 12, 6, 0) // and apply logical AND with 0x3F to bitmask the least significant 6 bits. // Use this as an index into the lookup table, mload an entire word // so the desired character is in the least significant byte, and // mstore8 this least significant byte into the result and continue. mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F)))) resultPtr := add(resultPtr, 1) // Advance } // Reset the value that was cached mstore(afterPtr, afterCache) if withPadding { // When data `bytes` is not exactly 3 bytes long // it is padded with `=` characters at the end switch mod(mload(data), 3) case 1 { mstore8(sub(resultPtr, 1), 0x3d) mstore8(sub(resultPtr, 2), 0x3d) } case 2 { mstore8(sub(resultPtr, 1), 0x3d) } } } return result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for converting numbers into strings and other string operations. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol) /// /// Note: /// For performance and bytecode compactness, most of the string operations are restricted to /// byte strings (7-bit ASCII), except where otherwise specified. /// Usage of byte string operations on charsets with runes spanning two or more bytes /// can lead to undefined behavior. library LibString { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The length of the output is too small to contain all the hex digits. error HexLengthInsufficient(); /// @dev The length of the string is more than 32 bytes. error TooBigForSmallString(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The constant returned when the `search` is not found in the string. uint256 internal constant NOT_FOUND = type(uint256).max; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* DECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the base 10 decimal representation of `value`. function toString(uint256 value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // The maximum value of a uint256 contains 78 digits (1 byte per digit), but // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned. // We will need 1 word for the trailing zeros padding, 1 word for the length, // and 3 words for a maximum of 78 digits. str := add(mload(0x40), 0x80) // Update the free memory pointer to allocate. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end of the memory to calculate the length later. let end := str let w := not(0) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 {} { str := add(str, w) // `sub(str, 1)`. // Write the character to the pointer. // The ASCII index of the '0' character is 48. mstore8(str, add(48, mod(temp, 10))) // Keep dividing `temp` until zero. temp := div(temp, 10) if iszero(temp) { break } } let length := sub(end, str) // Move the pointer 32 bytes leftwards to make room for the length. str := sub(str, 0x20) // Store the length. mstore(str, length) } } /// @dev Returns the base 10 decimal representation of `value`. function toString(int256 value) internal pure returns (string memory str) { if (value >= 0) { return toString(uint256(value)); } unchecked { str = toString(uint256(-value)); } /// @solidity memory-safe-assembly assembly { // We still have some spare memory space on the left, // as we have allocated 3 words (96 bytes) for up to 78 digits. let length := mload(str) // Load the string length. mstore(str, 0x2d) // Store the '-' character. str := sub(str, 1) // Move back the string pointer by a byte. mstore(str, add(length, 1)) // Update the string length. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HEXADECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2 + 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) { str = toHexStringNoPrefix(value, length); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length. // We add 0x20 to the total and round down to a multiple of 0x20. // (0x20 + 0x20 + 0x02 + 0x20) = 0x62. str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f))) // Allocate the memory. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let start := sub(str, add(length, length)) let w := not(1) // Tsk. let temp := value // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for {} 1 {} { str := add(str, w) // `sub(str, 2)`. mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(xor(str, start)) { break } } if temp { mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`. revert(0x1c, 0x04) } // Compute the string's length. let strLength := sub(end, str) // Move the pointer and write the length. str := sub(str, 0x20) mstore(str, strLength) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2 + 2` bytes. function toHexString(uint256 value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x". /// The output excludes leading "0" from the `toHexString` output. /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`. function toMinimalHexString(uint256 value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present. let strLength := add(mload(str), 2) // Compute the length. mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero. str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero. mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero. } } /// @dev Returns the hexadecimal representation of `value`. /// The output excludes leading "0" from the `toHexStringNoPrefix` output. /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`. function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present. let strLength := mload(str) // Get the length. str := add(str, o) // Move the pointer, accounting for leading zero. mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2` bytes. function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x40 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0. str := add(mload(0x40), 0x80) // Allocate the memory. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let w := not(1) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 {} { str := add(str, w) // `sub(str, 2)`. mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(temp) { break } } // Compute the string's length. let strLength := sub(end, str) // Move the pointer and write the length. str := sub(str, 0x20) mstore(str, strLength) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte, /// and the alphabets are capitalized conditionally according to /// https://eips.ethereum.org/EIPS/eip-55 function toHexStringChecksummed(address value) internal pure returns (string memory str) { str = toHexString(value); /// @solidity memory-safe-assembly assembly { let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` let o := add(str, 0x22) let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... ` let t := shl(240, 136) // `0b10001000 << 240` for { let i := 0 } 1 {} { mstore(add(i, i), mul(t, byte(i, hashed))) i := add(i, 1) if eq(i, 20) { break } } mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))) o := add(o, 0x20) mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. function toHexString(address value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(address value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { str := mload(0x40) // Allocate the memory. // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x28 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80. mstore(0x40, add(str, 0x80)) // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) str := add(str, 2) mstore(str, 40) let o := add(str, 0x20) mstore(add(o, 40), 0) value := shl(96, value) // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let i := 0 } 1 {} { let p := add(o, add(i, i)) let temp := byte(i, value) mstore8(add(p, 1), mload(and(temp, 15))) mstore8(p, mload(shr(4, temp))) i := add(i, 1) if eq(i, 20) { break } } } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexString(bytes memory raw) internal pure returns (string memory str) { str = toHexStringNoPrefix(raw); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { let length := mload(raw) str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. mstore(str, add(length, length)) // Store the length of the output. // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let o := add(str, 0x20) let end := add(raw, length) for {} iszero(eq(raw, end)) {} { raw := add(raw, 1) mstore8(add(o, 1), mload(and(mload(raw), 15))) mstore8(o, mload(and(shr(4, mload(raw)), 15))) o := add(o, 2) } mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate the memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RUNE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the number of UTF characters in the string. function runeCount(string memory s) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { if mload(s) { mstore(0x00, div(not(0), 255)) mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506) let o := add(s, 0x20) let end := add(o, mload(s)) for { result := 1 } 1 { result := add(result, 1) } { o := add(o, byte(0, mload(shr(250, mload(o))))) if iszero(lt(o, end)) { break } } } } } /// @dev Returns if this string is a 7-bit ASCII string. /// (i.e. all characters codes are in [0..127]) function is7BitASCII(string memory s) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let mask := shl(7, div(not(0), 255)) result := 1 let n := mload(s) if n { let o := add(s, 0x20) let end := add(o, n) let last := mload(end) mstore(end, 0) for {} 1 {} { if and(mask, mload(o)) { result := 0 break } o := add(o, 0x20) if iszero(lt(o, end)) { break } } mstore(end, last) } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* BYTE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // For performance and bytecode compactness, byte string operations are restricted // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets. // Usage of byte string operations on charsets with runes spanning two or more bytes // can lead to undefined behavior. /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`. function replace(string memory subject, string memory search, string memory replacement) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) let searchLength := mload(search) let replacementLength := mload(replacement) subject := add(subject, 0x20) search := add(search, 0x20) replacement := add(replacement, 0x20) result := add(mload(0x40), 0x20) let subjectEnd := add(subject, subjectLength) if iszero(gt(searchLength, subjectLength)) { let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1) let h := 0 if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) } let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(search) for {} 1 {} { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { mstore(result, t) result := add(result, 1) subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Copy the `replacement` one word at a time. for { let o := 0 } 1 {} { mstore(add(result, o), mload(add(replacement, o))) o := add(o, 0x20) if iszero(lt(o, replacementLength)) { break } } result := add(result, replacementLength) subject := add(subject, searchLength) if searchLength { if iszero(lt(subject, subjectSearchEnd)) { break } continue } } mstore(result, t) result := add(result, 1) subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } } } let resultRemainder := result result := add(mload(0x40), 0x20) let k := add(sub(resultRemainder, result), sub(subjectEnd, subject)) // Copy the rest of the string one word at a time. for {} lt(subject, subjectEnd) {} { mstore(resultRemainder, mload(subject)) resultRemainder := add(resultRemainder, 0x20) subject := add(subject, 0x20) } result := sub(result, 0x20) let last := add(add(result, 0x20), k) // Zeroize the slot after the string. mstore(last, 0) mstore(0x40, add(last, 0x20)) // Allocate the memory. mstore(result, k) // Store the length. } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for { let subjectLength := mload(subject) } 1 {} { if iszero(mload(search)) { if iszero(gt(from, subjectLength)) { result := from break } result := subjectLength break } let searchLength := mload(search) let subjectStart := add(subject, 0x20) result := not(0) // Initialize to `NOT_FOUND`. subject := add(subjectStart, from) let end := add(sub(add(subjectStart, subjectLength), searchLength), 1) let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(add(search, 0x20)) if iszero(and(lt(subject, end), lt(from, subjectLength))) { break } if iszero(lt(searchLength, 0x20)) { for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if iszero(shr(m, xor(mload(subject), s))) { if eq(keccak256(subject, searchLength), h) { result := sub(subject, subjectStart) break } } subject := add(subject, 1) if iszero(lt(subject, end)) { break } } break } for {} 1 {} { if iszero(shr(m, xor(mload(subject), s))) { result := sub(subject, subjectStart) break } subject := add(subject, 1) if iszero(lt(subject, end)) { break } } break } } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = indexOf(subject, search, 0); } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from right to left, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf(string memory subject, string memory search, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { result := not(0) // Initialize to `NOT_FOUND`. let searchLength := mload(search) if gt(searchLength, mload(subject)) { break } let w := result let fromMax := sub(mload(subject), searchLength) if iszero(gt(fromMax, from)) { from := fromMax } let end := add(add(subject, 0x20), w) subject := add(add(subject, 0x20), from) if iszero(gt(subject, end)) { break } // As this function is not too often used, // we shall simply use keccak256 for smaller bytecode size. for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if eq(keccak256(subject, searchLength), h) { result := sub(subject, add(end, 1)) break } subject := add(subject, w) // `sub(subject, 1)`. if iszero(gt(subject, end)) { break } } break } } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from right to left. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = lastIndexOf(subject, search, uint256(int256(-1))); } /// @dev Returns true if `search` is found in `subject`, false otherwise. function contains(string memory subject, string memory search) internal pure returns (bool) { return indexOf(subject, search) != NOT_FOUND; } /// @dev Returns whether `subject` starts with `search`. function startsWith(string memory subject, string memory search) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let searchLength := mload(search) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( iszero(gt(searchLength, mload(subject))), eq( keccak256(add(subject, 0x20), searchLength), keccak256(add(search, 0x20), searchLength) ) ) } } /// @dev Returns whether `subject` ends with `search`. function endsWith(string memory subject, string memory search) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let searchLength := mload(search) let subjectLength := mload(subject) // Whether `search` is not longer than `subject`. let withinRange := iszero(gt(searchLength, subjectLength)) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( withinRange, eq( keccak256( // `subject + 0x20 + max(subjectLength - searchLength, 0)`. add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))), searchLength ), keccak256(add(search, 0x20), searchLength) ) ) } } /// @dev Returns `subject` repeated `times`. function repeat(string memory subject, uint256 times) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) if iszero(or(iszero(times), iszero(subjectLength))) { subject := add(subject, 0x20) result := mload(0x40) let output := add(result, 0x20) for {} 1 {} { // Copy the `subject` one word at a time. for { let o := 0 } 1 {} { mstore(add(output, o), mload(add(subject, o))) o := add(o, 0x20) if iszero(lt(o, subjectLength)) { break } } output := add(output, subjectLength) times := sub(times, 1) if iszero(times) { break } } mstore(output, 0) // Zeroize the slot after the string. let resultLength := sub(output, add(result, 0x20)) mstore(result, resultLength) // Store the length. // Allocate the memory. mstore(0x40, add(result, add(resultLength, 0x20))) } } } /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive). /// `start` and `end` are byte offsets. function slice(string memory subject, uint256 start, uint256 end) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) if iszero(gt(subjectLength, end)) { end := subjectLength } if iszero(gt(subjectLength, start)) { start := subjectLength } if lt(start, end) { result := mload(0x40) let resultLength := sub(end, start) mstore(result, resultLength) subject := add(subject, start) let w := not(0x1f) // Copy the `subject` one word at a time, backwards. for { let o := and(add(resultLength, 0x1f), w) } 1 {} { mstore(add(result, o), mload(add(subject, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(result, 0x20), resultLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(result, and(add(resultLength, 0x3f), w))) } } } /// @dev Returns a copy of `subject` sliced from `start` to the end of the string. /// `start` is a byte offset. function slice(string memory subject, uint256 start) internal pure returns (string memory result) { result = slice(subject, start, uint256(int256(-1))); } /// @dev Returns all the indices of `search` in `subject`. /// The indices are byte offsets. function indicesOf(string memory subject, string memory search) internal pure returns (uint256[] memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) let searchLength := mload(search) if iszero(gt(searchLength, subjectLength)) { subject := add(subject, 0x20) search := add(search, 0x20) result := add(mload(0x40), 0x20) let subjectStart := subject let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1) let h := 0 if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) } let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(search) for {} 1 {} { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Append to `result`. mstore(result, sub(subject, subjectStart)) result := add(result, 0x20) // Advance `subject` by `searchLength`. subject := add(subject, searchLength) if searchLength { if iszero(lt(subject, subjectSearchEnd)) { break } continue } } subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } } let resultEnd := result // Assign `result` to the free memory pointer. result := mload(0x40) // Store the length of `result`. mstore(result, shr(5, sub(resultEnd, add(result, 0x20)))) // Allocate memory for result. // We allocate one more word, so this array can be recycled for {split}. mstore(0x40, add(resultEnd, 0x20)) } } } /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string. function split(string memory subject, string memory delimiter) internal pure returns (string[] memory result) { uint256[] memory indices = indicesOf(subject, delimiter); /// @solidity memory-safe-assembly assembly { let w := not(0x1f) let indexPtr := add(indices, 0x20) let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1))) mstore(add(indicesEnd, w), mload(subject)) mstore(indices, add(mload(indices), 1)) let prevIndex := 0 for {} 1 {} { let index := mload(indexPtr) mstore(indexPtr, 0x60) if iszero(eq(index, prevIndex)) { let element := mload(0x40) let elementLength := sub(index, prevIndex) mstore(element, elementLength) // Copy the `subject` one word at a time, backwards. for { let o := and(add(elementLength, 0x1f), w) } 1 {} { mstore(add(element, o), mload(add(add(subject, prevIndex), o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(element, 0x20), elementLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(element, and(add(elementLength, 0x3f), w))) // Store the `element` into the array. mstore(indexPtr, element) } prevIndex := add(index, mload(delimiter)) indexPtr := add(indexPtr, 0x20) if iszero(lt(indexPtr, indicesEnd)) { break } } result := indices if iszero(mload(delimiter)) { result := add(indices, 0x20) mstore(result, sub(mload(indices), 2)) } } } /// @dev Returns a concatenated string of `a` and `b`. /// Cheaper than `string.concat()` and does not de-align the free memory pointer. function concat(string memory a, string memory b) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let w := not(0x1f) result := mload(0x40) let aLength := mload(a) // Copy `a` one word at a time, backwards. for { let o := and(add(aLength, 0x20), w) } 1 {} { mstore(add(result, o), mload(add(a, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let bLength := mload(b) let output := add(result, aLength) // Copy `b` one word at a time, backwards. for { let o := and(add(bLength, 0x20), w) } 1 {} { mstore(add(output, o), mload(add(b, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let totalLength := add(aLength, bLength) let last := add(add(result, 0x20), totalLength) // Zeroize the slot after the string. mstore(last, 0) // Stores the length. mstore(result, totalLength) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 0x1f), w)) } } /// @dev Returns a copy of the string in either lowercase or UPPERCASE. /// WARNING! This function is only compatible with 7-bit ASCII strings. function toCase(string memory subject, bool toUpper) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let length := mload(subject) if length { result := add(mload(0x40), 0x20) subject := add(subject, 1) let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff) let w := not(0) for { let o := length } 1 {} { o := add(o, w) let b := and(0xff, mload(add(subject, o))) mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20))) if iszero(o) { break } } result := mload(0x40) mstore(result, length) // Store the length. let last := add(add(result, 0x20), length) mstore(last, 0) // Zeroize the slot after the string. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } } /// @dev Returns a string from a small bytes32 string. /// `s` must be null-terminated, or behavior will be undefined. function fromSmallString(bytes32 s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let n := 0 for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'. mstore(result, n) let o := add(result, 0x20) mstore(o, s) mstore(add(o, n), 0) mstore(0x40, add(result, 0x40)) } } /// @dev Returns the small string, with all bytes after the first null byte zeroized. function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'. mstore(0x00, s) mstore(result, 0x00) result := mload(0x00) } } /// @dev Returns the string as a normalized null-terminated small string. function toSmallString(string memory s) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { result := mload(s) if iszero(lt(result, 33)) { mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`. revert(0x1c, 0x04) } result := shl(shl(3, sub(32, result)), mload(add(s, result))) } } /// @dev Returns a lowercased copy of the string. /// WARNING! This function is only compatible with 7-bit ASCII strings. function lower(string memory subject) internal pure returns (string memory result) { result = toCase(subject, false); } /// @dev Returns an UPPERCASED copy of the string. /// WARNING! This function is only compatible with 7-bit ASCII strings. function upper(string memory subject) internal pure returns (string memory result) { result = toCase(subject, true); } /// @dev Escapes the string to be used within HTML tags. function escapeHTML(string memory s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let end := add(s, mload(s)) result := add(mload(0x40), 0x20) // Store the bytes of the packed offsets and strides into the scratch space. // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6. mstore(0x1f, 0x900094) mstore(0x08, 0xc0000000a6ab) // Store ""&'<>" into the scratch space. mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b)) for {} iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) // Not in `["\"","'","&","<",">"]`. if iszero(and(shl(c, 1), 0x500000c400000000)) { mstore8(result, c) result := add(result, 1) continue } let t := shr(248, mload(c)) mstore(result, mload(and(t, 0x1f))) result := add(result, shr(5, t)) } let last := result mstore(last, 0) // Zeroize the slot after the string. result := mload(0x40) mstore(result, sub(last, add(result, 0x20))) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } /// @dev Escapes the string to be used within double-quotes in a JSON. /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes. function escapeJSON(string memory s, bool addDoubleQuotes) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let end := add(s, mload(s)) result := add(mload(0x40), 0x20) if addDoubleQuotes { mstore8(result, 34) result := add(1, result) } // Store "\\u0000" in scratch space. // Store "0123456789abcdef" in scratch space. // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`. // into the scratch space. mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672) // Bitmask for detecting `["\"","\\"]`. let e := or(shl(0x22, 1), shl(0x5c, 1)) for {} iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) if iszero(lt(c, 0x20)) { if iszero(and(shl(c, 1), e)) { // Not in `["\"","\\"]`. mstore8(result, c) result := add(result, 1) continue } mstore8(result, 0x5c) // "\\". mstore8(add(result, 1), c) result := add(result, 2) continue } if iszero(and(shl(c, 1), 0x3700)) { // Not in `["\b","\t","\n","\f","\d"]`. mstore8(0x1d, mload(shr(4, c))) // Hex value. mstore8(0x1e, mload(and(c, 15))) // Hex value. mstore(result, mload(0x19)) // "\\u00XX". result := add(result, 6) continue } mstore8(result, 0x5c) // "\\". mstore8(add(result, 1), mload(add(c, 8))) result := add(result, 2) } if addDoubleQuotes { mstore8(result, 34) result := add(1, result) } let last := result mstore(last, 0) // Zeroize the slot after the string. result := mload(0x40) mstore(result, sub(last, add(result, 0x20))) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } /// @dev Escapes the string to be used within double-quotes in a JSON. function escapeJSON(string memory s) internal pure returns (string memory result) { result = escapeJSON(s, false); } /// @dev Returns whether `a` equals `b`. function eq(string memory a, string memory b) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b))) } } /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string. function eqs(string memory a, bytes32 b) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { // These should be evaluated on compile time, as far as possible. let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`. let x := not(or(m, or(b, add(m, and(b, m))))) let r := shl(7, iszero(iszero(shr(128, x)))) r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x)))))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(r, shl(3, lt(0xff, shr(r, x)))) // forgefmt: disable-next-item result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))), xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20))))) } } /// @dev Packs a single string with its length into a single word. /// Returns `bytes32(0)` if the length is zero or greater than 31. function packOne(string memory a) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { // We don't need to zero right pad the string, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes. mload(add(a, 0x1f)), // `length != 0 && length < 32`. Abuses underflow. // Assumes that the length is valid and within the block gas limit. lt(sub(mload(a), 1), 0x1f) ) } } /// @dev Unpacks a string packed using {packOne}. /// Returns the empty string if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packOne}, the output behavior is undefined. function unpackOne(bytes32 packed) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { // Grab the free memory pointer. result := mload(0x40) // Allocate 2 words (1 for the length, 1 for the bytes). mstore(0x40, add(result, 0x40)) // Zeroize the length slot. mstore(result, 0) // Store the length and bytes. mstore(add(result, 0x1f), packed) // Right pad with zeroes. mstore(add(add(result, 0x20), mload(result)), 0) } } /// @dev Packs two strings with their lengths into a single word. /// Returns `bytes32(0)` if combined length is zero or greater than 30. function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let aLength := mload(a) // We don't need to zero right pad the strings, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes of `a` and `b`. or( shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))), mload(sub(add(b, 0x1e), aLength)) ), // `totalLength != 0 && totalLength < 31`. Abuses underflow. // Assumes that the lengths are valid and within the block gas limit. lt(sub(add(aLength, mload(b)), 1), 0x1e) ) } } /// @dev Unpacks strings packed using {packTwo}. /// Returns the empty strings if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packTwo}, the output behavior is undefined. function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) { /// @solidity memory-safe-assembly assembly { // Grab the free memory pointer. resultA := mload(0x40) resultB := add(resultA, 0x40) // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words. mstore(0x40, add(resultB, 0x40)) // Zeroize the length slots. mstore(resultA, 0) mstore(resultB, 0) // Store the lengths and bytes. mstore(add(resultA, 0x1f), packed) mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA)))) // Right pad with zeroes. mstore(add(add(resultA, 0x20), mload(resultA)), 0) mstore(add(add(resultB, 0x20), mload(resultB)), 0) } } /// @dev Directly returns `a` without copying. function directReturn(string memory a) internal pure { assembly { // Assumes that the string does not start from the scratch space. let retStart := sub(a, 0x20) let retSize := add(mload(a), 0x40) // Right pad with zeroes. Just in case the string is produced // by a method that doesn't zero right pad. mstore(add(retStart, retSize), 0) // Store the return offset. mstore(retStart, 0x20) // End the transaction, returning the string. return(retStart, retSize) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol) pragma solidity ^0.8.20; import {Math} from "./math/Math.sol"; import {SignedMath} from "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; uint8 private constant ADDRESS_LENGTH = 20; /** * @dev The `value` string doesn't fit in the specified `length`. */ error StringsInsufficientHexLength(uint256 value, uint256 length); /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), HEX_DIGITS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toStringSigned(int256 value) internal pure returns (string memory) { return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { uint256 localValue = value; bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = HEX_DIGITS[localValue & 0xf]; localValue >>= 4; } if (localValue != 0) { revert StringsInsufficientHexLength(value, length); } return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal * representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.20; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC-1967 implementation slot: * ```solidity * contract ERC1967 { * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(newImplementation.code.length > 0); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } struct StringSlot { string value; } struct BytesSlot { bytes value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `StringSlot` with member `value` located at `slot`. */ function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `StringSlot` representation of the string storage pointer `store`. */ function getStringSlot(string storage store) internal pure returns (StringSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot } } /** * @dev Returns an `BytesSlot` with member `value` located at `slot`. */ function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`. */ function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; import {Panic} from "../Panic.sol"; import {SafeCast} from "./SafeCast.sol"; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an success flag (no overflow). */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow). */ function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow). */ function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a success flag (no division by zero). */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero). */ function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. Panic.panic(Panic.DIVISION_BY_ZERO); } // The following calculation ensures accurate ceiling division without overflow. // Since a is non-zero, (a - 1) / b will not overflow. // The largest possible result occurs when (a - 1) / b is type(uint256).max, // but the largest value we can obtain is type(uint256).max - 1, which happens // when a = type(uint256).max and b = 1. unchecked { return a == 0 ? 0 : (a - 1) / b + 1; } } /** * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2²⁵⁶ + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0. if (denominator <= prod1) { Panic.panic(denominator == 0 ? Panic.DIVISION_BY_ZERO : Panic.UNDER_OVERFLOW); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv ≡ 1 mod 2⁴. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2⁸ inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶ inverse *= 2 - denominator * inverse; // inverse mod 2³² inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴ inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸ inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶ // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @dev Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0); } /** * @dev Calculate the modular multiplicative inverse of a number in Z/nZ. * * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, expect 0. * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible. * * If the input value is not inversible, 0 is returned. * * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Ferma's little theorem and get the * inverse using `Math.modExp(a, n - 2, n)`. */ function invMod(uint256 a, uint256 n) internal pure returns (uint256) { unchecked { if (n == 0) return 0; // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version) // Used to compute integers x and y such that: ax + ny = gcd(a, n). // When the gcd is 1, then the inverse of a modulo n exists and it's x. // ax + ny = 1 // ax = 1 + (-y)n // ax ≡ 1 (mod n) # x is the inverse of a modulo n // If the remainder is 0 the gcd is n right away. uint256 remainder = a % n; uint256 gcd = n; // Therefore the initial coefficients are: // ax + ny = gcd(a, n) = n // 0a + 1n = n int256 x = 0; int256 y = 1; while (remainder != 0) { uint256 quotient = gcd / remainder; (gcd, remainder) = ( // The old remainder is the next gcd to try. remainder, // Compute the next remainder. // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd // where gcd is at most n (capped to type(uint256).max) gcd - remainder * quotient ); (x, y) = ( // Increment the coefficient of a. y, // Decrement the coefficient of n. // Can overflow, but the result is casted to uint256 so that the // next value of y is "wrapped around" to a value between 0 and n - 1. x - y * int256(quotient) ); } if (gcd != 1) return 0; // No inverse exists. return x < 0 ? (n - uint256(-x)) : uint256(x); // Wrap the result if it's negative. } } /** * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m) * * Requirements: * - modulus can't be zero * - underlying staticcall to precompile must succeed * * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make * sure the chain you're using it on supports the precompiled contract for modular exponentiation * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, * the underlying function will succeed given the lack of a revert, but the result may be incorrectly * interpreted as 0. */ function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) { (bool success, uint256 result) = tryModExp(b, e, m); if (!success) { Panic.panic(Panic.DIVISION_BY_ZERO); } return result; } /** * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m). * It includes a success flag indicating if the operation succeeded. Operation will be marked has failed if trying * to operate modulo 0 or if the underlying precompile reverted. * * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack * of a revert, but the result may be incorrectly interpreted as 0. */ function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) { if (m == 0) return (false, 0); /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) // | Offset | Content | Content (Hex) | // |-----------|------------|--------------------------------------------------------------------| // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 | // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 | // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 | // | 0x60:0x7f | value of b | 0x<.............................................................b> | // | 0x80:0x9f | value of e | 0x<.............................................................e> | // | 0xa0:0xbf | value of m | 0x<.............................................................m> | mstore(ptr, 0x20) mstore(add(ptr, 0x20), 0x20) mstore(add(ptr, 0x40), 0x20) mstore(add(ptr, 0x60), b) mstore(add(ptr, 0x80), e) mstore(add(ptr, 0xa0), m) // Given the result < m, it's guaranteed to fit in 32 bytes, // so we can use the memory scratch space located at offset 0. success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20) result := mload(0x00) } } /** * @dev Variant of {modExp} that supports inputs of arbitrary length. */ function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) { (bool success, bytes memory result) = tryModExp(b, e, m); if (!success) { Panic.panic(Panic.DIVISION_BY_ZERO); } return result; } /** * @dev Variant of {tryModExp} that supports inputs of arbitrary length. */ function tryModExp( bytes memory b, bytes memory e, bytes memory m ) internal view returns (bool success, bytes memory result) { if (_zeroBytes(m)) return (false, new bytes(0)); uint256 mLen = m.length; // Encode call args in result and move the free memory pointer result = abi.encodePacked(b.length, e.length, mLen, b, e, m); /// @solidity memory-safe-assembly assembly { let dataPtr := add(result, 0x20) // Write result on top of args to avoid allocating extra memory. success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen) // Overwrite the length. // result.length > returndatasize() is guaranteed because returndatasize() == m.length mstore(result, mLen) // Set the memory pointer after the returned data. mstore(0x40, add(dataPtr, mLen)) } } /** * @dev Returns whether the provided byte array is zero. */ function _zeroBytes(bytes memory byteArray) private pure returns (bool) { for (uint256 i = 0; i < byteArray.length; ++i) { if (byteArray[i] != 0) { return false; } } return true; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * This method is based on Newton's method for computing square roots; the algorithm is restricted to only * using integer operations. */ function sqrt(uint256 a) internal pure returns (uint256) { unchecked { // Take care of easy edge cases when a == 0 or a == 1 if (a <= 1) { return a; } // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between // the current value as `ε_n = | x_n - sqrt(a) |`. // // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is // bigger than any uint256. // // By noticing that // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)` // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar // to the msb function. uint256 aa = a; uint256 xn = 1; if (aa >= (1 << 128)) { aa >>= 128; xn <<= 64; } if (aa >= (1 << 64)) { aa >>= 64; xn <<= 32; } if (aa >= (1 << 32)) { aa >>= 32; xn <<= 16; } if (aa >= (1 << 16)) { aa >>= 16; xn <<= 8; } if (aa >= (1 << 8)) { aa >>= 8; xn <<= 4; } if (aa >= (1 << 4)) { aa >>= 4; xn <<= 2; } if (aa >= (1 << 2)) { xn <<= 1; } // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1). // // We can refine our estimation by noticing that the the middle of that interval minimizes the error. // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2). // This is going to be our x_0 (and ε_0) xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2) // From here, Newton's method give us: // x_{n+1} = (x_n + a / x_n) / 2 // // One should note that: // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a // = ((x_n² + a) / (2 * x_n))² - a // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²) // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²) // = (x_n² - a)² / (2 * x_n)² // = ((x_n² - a) / (2 * x_n))² // ≥ 0 // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n // // This gives us the proof of quadratic convergence of the sequence: // ε_{n+1} = | x_{n+1} - sqrt(a) | // = | (x_n + a / x_n) / 2 - sqrt(a) | // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) | // = | (x_n - sqrt(a))² / (2 * x_n) | // = | ε_n² / (2 * x_n) | // = ε_n² / | (2 * x_n) | // // For the first iteration, we have a special case where x_0 is known: // ε_1 = ε_0² / | (2 * x_0) | // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2))) // ≤ 2**(2*e-4) / (3 * 2**(e-1)) // ≤ 2**(e-3) / 3 // ≤ 2**(e-3-log2(3)) // ≤ 2**(e-4.5) // // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n: // ε_{n+1} = ε_n² / | (2 * x_n) | // ≤ (2**(e-k))² / (2 * 2**(e-1)) // ≤ 2**(2*e-2*k) / 2**e // ≤ 2**(e-2*k) xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5 xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9 xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18 xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36 xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72 // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either // sqrt(a) or sqrt(a) + 1. return xn - SafeCast.toUint(xn > a / xn); } } /** * @dev Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; uint256 exp; unchecked { exp = 128 * SafeCast.toUint(value > (1 << 128) - 1); value >>= exp; result += exp; exp = 64 * SafeCast.toUint(value > (1 << 64) - 1); value >>= exp; result += exp; exp = 32 * SafeCast.toUint(value > (1 << 32) - 1); value >>= exp; result += exp; exp = 16 * SafeCast.toUint(value > (1 << 16) - 1); value >>= exp; result += exp; exp = 8 * SafeCast.toUint(value > (1 << 8) - 1); value >>= exp; result += exp; exp = 4 * SafeCast.toUint(value > (1 << 4) - 1); value >>= exp; result += exp; exp = 2 * SafeCast.toUint(value > (1 << 2) - 1); value >>= exp; result += exp; result += SafeCast.toUint(value > 1); } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; uint256 isGt; unchecked { isGt = SafeCast.toUint(value > (1 << 128) - 1); value >>= isGt * 128; result += isGt * 16; isGt = SafeCast.toUint(value > (1 << 64) - 1); value >>= isGt * 64; result += isGt * 8; isGt = SafeCast.toUint(value > (1 << 32) - 1); value >>= isGt * 32; result += isGt * 4; isGt = SafeCast.toUint(value > (1 << 16) - 1); value >>= isGt * 16; result += isGt * 2; result += SafeCast.toUint(value > (1 << 8) - 1); } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.20; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson. // Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift, // taking advantage of the most significant (or "sign" bit) in two's complement representation. // This opcode adds new most significant bits set to the value of the previous most significant bit. As a result, // the mask will either be `bytes(0)` (if n is positive) or `~bytes32(0)` (if n is negative). int256 mask = n >> 255; // A `bytes(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it. return uint256((n + mask) ^ mask); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @dev Helper library for emitting standardized panic codes. * * ```solidity * contract Example { * using Panic for uint256; * * // Use any of the declared internal constants * function foo() { Panic.GENERIC.panic(); } * * // Alternatively * function foo() { Panic.panic(Panic.GENERIC); } * } * ``` * * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil]. */ // slither-disable-next-line unused-state library Panic { /// @dev generic / unspecified error uint256 internal constant GENERIC = 0x00; /// @dev used by the assert() builtin uint256 internal constant ASSERT = 0x01; /// @dev arithmetic underflow or overflow uint256 internal constant UNDER_OVERFLOW = 0x11; /// @dev division or modulo by zero uint256 internal constant DIVISION_BY_ZERO = 0x12; /// @dev enum conversion error uint256 internal constant ENUM_CONVERSION_ERROR = 0x21; /// @dev invalid encoding in storage uint256 internal constant STORAGE_ENCODING_ERROR = 0x22; /// @dev empty array pop uint256 internal constant EMPTY_ARRAY_POP = 0x31; /// @dev array out of bounds access uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32; /// @dev resource error (too large allocation or too large array) uint256 internal constant RESOURCE_ERROR = 0x41; /// @dev calling invalid internal function uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51; /// @dev Reverts with a panic code. Recommended to use with /// the internal constants with predefined codes. function panic(uint256 code) internal pure { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x4e487b71) mstore(0x20, code) revert(0x1c, 0x24) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.20; /** * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeCast { /** * @dev Value doesn't fit in an uint of `bits` size. */ error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value); /** * @dev An int value doesn't fit in an uint of `bits` size. */ error SafeCastOverflowedIntToUint(int256 value); /** * @dev Value doesn't fit in an int of `bits` size. */ error SafeCastOverflowedIntDowncast(uint8 bits, int256 value); /** * @dev An uint value doesn't fit in an int of `bits` size. */ error SafeCastOverflowedUintToInt(uint256 value); /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits */ function toUint248(uint256 value) internal pure returns (uint248) { if (value > type(uint248).max) { revert SafeCastOverflowedUintDowncast(248, value); } return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits */ function toUint240(uint256 value) internal pure returns (uint240) { if (value > type(uint240).max) { revert SafeCastOverflowedUintDowncast(240, value); } return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits */ function toUint232(uint256 value) internal pure returns (uint232) { if (value > type(uint232).max) { revert SafeCastOverflowedUintDowncast(232, value); } return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits */ function toUint224(uint256 value) internal pure returns (uint224) { if (value > type(uint224).max) { revert SafeCastOverflowedUintDowncast(224, value); } return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits */ function toUint216(uint256 value) internal pure returns (uint216) { if (value > type(uint216).max) { revert SafeCastOverflowedUintDowncast(216, value); } return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits */ function toUint208(uint256 value) internal pure returns (uint208) { if (value > type(uint208).max) { revert SafeCastOverflowedUintDowncast(208, value); } return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits */ function toUint200(uint256 value) internal pure returns (uint200) { if (value > type(uint200).max) { revert SafeCastOverflowedUintDowncast(200, value); } return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits */ function toUint192(uint256 value) internal pure returns (uint192) { if (value > type(uint192).max) { revert SafeCastOverflowedUintDowncast(192, value); } return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits */ function toUint184(uint256 value) internal pure returns (uint184) { if (value > type(uint184).max) { revert SafeCastOverflowedUintDowncast(184, value); } return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits */ function toUint176(uint256 value) internal pure returns (uint176) { if (value > type(uint176).max) { revert SafeCastOverflowedUintDowncast(176, value); } return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits */ function toUint168(uint256 value) internal pure returns (uint168) { if (value > type(uint168).max) { revert SafeCastOverflowedUintDowncast(168, value); } return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits */ function toUint160(uint256 value) internal pure returns (uint160) { if (value > type(uint160).max) { revert SafeCastOverflowedUintDowncast(160, value); } return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits */ function toUint152(uint256 value) internal pure returns (uint152) { if (value > type(uint152).max) { revert SafeCastOverflowedUintDowncast(152, value); } return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits */ function toUint144(uint256 value) internal pure returns (uint144) { if (value > type(uint144).max) { revert SafeCastOverflowedUintDowncast(144, value); } return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits */ function toUint136(uint256 value) internal pure returns (uint136) { if (value > type(uint136).max) { revert SafeCastOverflowedUintDowncast(136, value); } return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { if (value > type(uint128).max) { revert SafeCastOverflowedUintDowncast(128, value); } return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits */ function toUint120(uint256 value) internal pure returns (uint120) { if (value > type(uint120).max) { revert SafeCastOverflowedUintDowncast(120, value); } return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits */ function toUint112(uint256 value) internal pure returns (uint112) { if (value > type(uint112).max) { revert SafeCastOverflowedUintDowncast(112, value); } return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits */ function toUint104(uint256 value) internal pure returns (uint104) { if (value > type(uint104).max) { revert SafeCastOverflowedUintDowncast(104, value); } return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits */ function toUint96(uint256 value) internal pure returns (uint96) { if (value > type(uint96).max) { revert SafeCastOverflowedUintDowncast(96, value); } return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits */ function toUint88(uint256 value) internal pure returns (uint88) { if (value > type(uint88).max) { revert SafeCastOverflowedUintDowncast(88, value); } return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits */ function toUint80(uint256 value) internal pure returns (uint80) { if (value > type(uint80).max) { revert SafeCastOverflowedUintDowncast(80, value); } return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits */ function toUint72(uint256 value) internal pure returns (uint72) { if (value > type(uint72).max) { revert SafeCastOverflowedUintDowncast(72, value); } return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { if (value > type(uint64).max) { revert SafeCastOverflowedUintDowncast(64, value); } return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits */ function toUint56(uint256 value) internal pure returns (uint56) { if (value > type(uint56).max) { revert SafeCastOverflowedUintDowncast(56, value); } return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits */ function toUint48(uint256 value) internal pure returns (uint48) { if (value > type(uint48).max) { revert SafeCastOverflowedUintDowncast(48, value); } return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits */ function toUint40(uint256 value) internal pure returns (uint40) { if (value > type(uint40).max) { revert SafeCastOverflowedUintDowncast(40, value); } return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { if (value > type(uint32).max) { revert SafeCastOverflowedUintDowncast(32, value); } return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits */ function toUint24(uint256 value) internal pure returns (uint24) { if (value > type(uint24).max) { revert SafeCastOverflowedUintDowncast(24, value); } return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { if (value > type(uint16).max) { revert SafeCastOverflowedUintDowncast(16, value); } return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits */ function toUint8(uint256 value) internal pure returns (uint8) { if (value > type(uint8).max) { revert SafeCastOverflowedUintDowncast(8, value); } return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { if (value < 0) { revert SafeCastOverflowedIntToUint(value); } return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(248, value); } } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(240, value); } } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(232, value); } } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(224, value); } } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(216, value); } } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(208, value); } } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(200, value); } } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(192, value); } } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(184, value); } } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(176, value); } } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(168, value); } } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(160, value); } } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(152, value); } } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(144, value); } } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(136, value); } } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(128, value); } } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(120, value); } } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(112, value); } } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(104, value); } } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(96, value); } } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(88, value); } } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(80, value); } } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(72, value); } } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(64, value); } } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(56, value); } } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(48, value); } } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(40, value); } } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(32, value); } } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(24, value); } } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(16, value); } } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(8, value); } } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive if (value > uint256(type(int256).max)) { revert SafeCastOverflowedUintToInt(value); } return int256(value); } /** * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump. */ function toUint(bool b) internal pure returns (uint256 u) { /// @solidity memory-safe-assembly assembly { u := iszero(iszero(b)) } } }
{ "remappings": [ "@prb/test/=node_modules/@prb/test/src/", "forge-std/=node_modules/forge-std/src/", "@openzeppelin/=node_modules/@openzeppelin/contracts/", "@account-abstraction/=node_modules/accountabstraction/contracts/", "solady/=node_modules/solady/src/", "@web-authn/=node_modules/web-authn/src/", "openzeppelin-contracts/=node_modules/@openzeppelin/contracts/", "FreshCryptoLib/=node_modules/FreshCryptoLib/solidity/src/", "src/=src/", "account-abstraction/=node_modules/account-abstraction/contracts/", "web-authn/=node_modules/web-authn/" ], "optimizer": { "enabled": true, "runs": 5000000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "shanghai", "viaIR": true, "libraries": {} }
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"DuplicateSigner","type":"error"},{"inputs":[],"name":"FailedContractCreation","type":"error"},{"inputs":[{"internalType":"bytes4","name":"sig","type":"bytes4"}],"name":"FunctionNotSupported","type":"error"},{"inputs":[],"name":"InvalidGasLimits","type":"error"},{"inputs":[],"name":"InvalidNumberOfSigners","type":"error"},{"inputs":[],"name":"InvalidPaymasterData","type":"error"},{"inputs":[],"name":"InvalidShortString","type":"error"},{"inputs":[],"name":"InvalidSignatureType","type":"error"},{"inputs":[{"components":[{"internalType":"bytes32","name":"slot1","type":"bytes32"},{"internalType":"bytes32","name":"slot2","type":"bytes32"}],"internalType":"struct Signer","name":"signer","type":"tuple"}],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidThreshold","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"OnlyEntryPoint","type":"error"},{"inputs":[],"name":"OnlyFactory","type":"error"},{"inputs":[],"name":"OnlyModule","type":"error"},{"inputs":[],"name":"OnlySelf","type":"error"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"SignerAlreadyPresent","type":"error"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"SignerNotPresent","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnauthorizedCallContext","type":"error"},{"inputs":[],"name":"UpgradeFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"components":[{"internalType":"bytes32","name":"slot1","type":"bytes32"},{"internalType":"bytes32","name":"slot2","type":"bytes32"}],"indexed":false,"internalType":"struct Signer","name":"signer","type":"tuple"}],"name":"AddSigner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"}],"name":"DisabledModule","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"}],"name":"EnabledModule","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"indexed":false,"internalType":"struct Caller.Call","name":"call","type":"tuple"}],"name":"ExecutedTxFromModule","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"handler","type":"address"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"bytes","name":"result","type":"bytes"}],"name":"FallbackHandlerCalled","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"bytes32","name":"slot1","type":"bytes32"},{"internalType":"bytes32","name":"slot2","type":"bytes32"}],"indexed":false,"internalType":"struct Signer[]","name":"signers","type":"tuple[]"},{"indexed":false,"internalType":"uint8","name":"threshold","type":"uint8"}],"name":"InitializedSigners","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReceiveEth","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"components":[{"internalType":"bytes32","name":"slot1","type":"bytes32"},{"internalType":"bytes32","name":"slot2","type":"bytes32"}],"indexed":false,"internalType":"struct Signer","name":"signer","type":"tuple"}],"name":"RemoveSigner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"threshold","type":"uint8"}],"name":"UpdateThreshold","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"handler","type":"address"}],"name":"UpdatedFallbackHandler","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"slot1","type":"bytes32"},{"internalType":"bytes32","name":"slot2","type":"bytes32"}],"internalType":"struct Signer","name":"signer_","type":"tuple"},{"internalType":"uint8","name":"index_","type":"uint8"}],"name":"addSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"initCode_","type":"bytes"}],"name":"deployCreate","outputs":[{"internalType":"address","name":"newContract","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"module_","type":"address"}],"name":"disableModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"module_","type":"address"}],"name":"enableModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"entryPoint","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Caller.Call","name":"call_","type":"tuple"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Caller.Call[]","name":"calls_","type":"tuple[]"}],"name":"executeBatch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Caller.Call","name":"call_","type":"tuple"}],"name":"executeFromModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Caller.Call[]","name":"calls_","type":"tuple[]"}],"name":"executeFromModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"sig_","type":"bytes4"}],"name":"getFallbackHandler","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getImplementation","outputs":[{"internalType":"address","name":"implementation","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index_","type":"uint8"}],"name":"getSigner","outputs":[{"components":[{"internalType":"bytes32","name":"slot1","type":"bytes32"},{"internalType":"bytes32","name":"slot2","type":"bytes32"}],"internalType":"struct Signer","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSignerCount","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getThreshold","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"components":[{"internalType":"bytes32","name":"slot1","type":"bytes32"},{"internalType":"bytes32","name":"slot2","type":"bytes32"}],"internalType":"struct Signer[]","name":"signers_","type":"tuple[]"},{"internalType":"uint8","name":"threshold_","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"module_","type":"address"}],"name":"isModuleEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash_","type":"bytes32"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index_","type":"uint8"}],"name":"removeSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash_","type":"bytes32"}],"name":"replaySafeHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"module_","type":"address"},{"internalType":"address","name":"setupContract_","type":"address"},{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"setupAndEnableModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"module_","type":"address"},{"internalType":"address","name":"teardownContract_","type":"address"},{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"teardownAndDisableModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"sig_","type":"bytes4"},{"internalType":"address","name":"handler_","type":"address"}],"name":"updateFallbackHandler","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"threshold_","type":"uint8"}],"name":"updateThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes32","name":"accountGasLimits","type":"bytes32"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"bytes32","name":"gasFees","type":"bytes32"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct PackedUserOperation","name":"userOp_","type":"tuple"},{"internalType":"bytes32","name":"userOpHash_","type":"bytes32"},{"internalType":"uint256","name":"missingAccountFunds_","type":"uint256"}],"name":"validateUserOp","outputs":[{"internalType":"uint256","name":"validationData","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
OP | 54.25% | $1.78 | 167,687.0069 | $298,628.71 | |
OP | 0.25% | $3,294.9 | 0.4237 | $1,396.09 | |
OP | <0.01% | $3,292.09 | 0.00329107 | $10.83 | |
ETH | Ether (ETH) | 24.34% | $3,297 | 40.6324 | $133,964.93 |
ETH | 6.84% | $34.16 | 1,101.937 | $37,642.17 | |
ETH | 6.57% | $13.38 | 2,703.0412 | $36,166.69 | |
ETH | 3.73% | $1.69 | 12,146.1203 | $20,526.94 | |
ETH | 1.18% | $3,297 | 1.9722 | $6,502.21 | |
ETH | 1.09% | $0.9969 | 6,032.1683 | $6,013.47 | |
ETH | 0.36% | $0.203834 | 9,713.826 | $1,980.01 | |
ETH | 0.10% | $0.997418 | 556.3628 | $554.93 | |
ETH | <0.01% | $1.07 | 25.2752 | $26.92 | |
BASE | 1.14% | $3,292.44 | 1.9067 | $6,277.79 | |
BASE | 0.14% | $0.999149 | 752.2329 | $751.59 | |
BASE | <0.01% | $3,288.25 | 0.00188606 | $6.2 |
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.