Source Code
Overview
SOPH Balance
More Info
ContractCreator
Multichain Info
N/A
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 13 internal transactions
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
877687 | 13 days ago | 0 SOPH | ||||
877687 | 13 days ago | 0 SOPH | ||||
877687 | 13 days ago | 0 SOPH | ||||
877687 | 13 days ago | 0 SOPH | ||||
877687 | 13 days ago | 0 SOPH | ||||
877687 | 13 days ago | 0 SOPH | ||||
877687 | 13 days ago | 0 SOPH | ||||
877687 | 13 days ago | 0 SOPH | ||||
877687 | 13 days ago | 0 SOPH | ||||
877687 | 13 days ago | 0 SOPH | ||||
877687 | 13 days ago | 0 SOPH | ||||
877687 | 13 days ago | 0 SOPH | ||||
877687 | 13 days ago | Contract Creation | 0 SOPH |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x48A9C682...C2c7cD1f5 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
AccountProxy
Compiler Version
v0.8.28-1.0.1
ZkSolc Version
v1.5.11
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol"; import { EfficientProxy } from "./EfficientProxy.sol"; import { BeaconProxy } from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; /// @title AccountProxy /// @author Matter Labs /// @custom:security-contact [email protected] /// @notice This contract is modification of OpenZeppelin `BeaconProxy` with optimisation for /// cheap delegate calls on ZKsync. contract AccountProxy is BeaconProxy, EfficientProxy { constructor(address beacon) BeaconProxy(beacon, bytes("")) {} function _delegate(address implementation) internal override(EfficientProxy, Proxy) { EfficientProxy._delegate(implementation); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/IBootloaderUtilities.sol"; import "./libraries/TransactionHelper.sol"; import "./libraries/RLPEncoder.sol"; import "./libraries/EfficientCall.sol"; /** * @author Matter Labs * @notice A contract that provides some utility methods for the bootloader * that is very hard to write in Yul. */ contract BootloaderUtilities is IBootloaderUtilities { using TransactionHelper for *; /// @notice Calculates the canonical transaction hash and the recommended transaction hash. /// @param _transaction The transaction. /// @return txHash and signedTxHash of the transaction, i.e. the transaction hash to be used in the explorer and commits to all /// the fields of the transaction and the recommended hash to be signed for this transaction. /// @dev txHash must be unique for all transactions. function getTransactionHashes( Transaction calldata _transaction ) external view override returns (bytes32 txHash, bytes32 signedTxHash) { signedTxHash = _transaction.encodeHash(); if (_transaction.txType == EIP_712_TX_TYPE) { txHash = keccak256(bytes.concat(signedTxHash, EfficientCall.keccak(_transaction.signature))); } else if (_transaction.txType == LEGACY_TX_TYPE) { txHash = encodeLegacyTransactionHash(_transaction); } else if (_transaction.txType == EIP_1559_TX_TYPE) { txHash = encodeEIP1559TransactionHash(_transaction); } else if (_transaction.txType == EIP_2930_TX_TYPE) { txHash = encodeEIP2930TransactionHash(_transaction); } else { revert("Unsupported tx type"); } } /// @notice Calculates the hash for a legacy transaction. /// @param _transaction The legacy transaction. /// @return txHash The hash of the transaction. function encodeLegacyTransactionHash(Transaction calldata _transaction) internal view returns (bytes32 txHash) { // Hash of legacy transactions are encoded as one of the: // - RLP(nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0) // - RLP(nonce, gasPrice, gasLimit, to, value, data) // // In this RLP encoding, only the first one above list appears, so we encode each element // inside list and then concatenate the length of all elements with them. bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); // Encode `gasPrice` and `gasLimit` together to prevent "stack too deep error". bytes memory encodedGasParam; { bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); encodedGasParam = bytes.concat(encodedGasPrice, encodedGasLimit); } bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. bytes memory encodedDataLength; { // Safe cast, because the length of the transaction data can't be so large. uint64 txDataLen = uint64(_transaction.data.length); if (txDataLen != 1) { // If the length is not equal to one, then only using the length can it be encoded definitely. encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); } else if (_transaction.data[0] >= 0x80) { // If input is a byte in [0x80, 0xff] range, RLP encoding will concatenates 0x81 with the byte. encodedDataLength = hex"81"; } // Otherwise the length is not encoded at all. } bytes memory rEncoded; { uint256 rInt = uint256(bytes32(_transaction.signature[0:32])); rEncoded = RLPEncoder.encodeUint256(rInt); } bytes memory sEncoded; { uint256 sInt = uint256(bytes32(_transaction.signature[32:64])); sEncoded = RLPEncoder.encodeUint256(sInt); } bytes memory vEncoded; { uint256 vInt = uint256(uint8(_transaction.signature[64])); require(vInt == 27 || vInt == 28, "Invalid v value"); // If the `chainId` is specified in the transaction, then the `v` value is encoded as // `35 + y + 2 * chainId == vInt + 8 + 2 * chainId`, where y - parity bit (see EIP-155). if (_transaction.reserved[0] != 0) { vInt += 8 + block.chainid * 2; } vEncoded = RLPEncoder.encodeUint256(vInt); } bytes memory encodedListLength; unchecked { uint256 listLength = encodedNonce.length + encodedGasParam.length + encodedTo.length + encodedValue.length + encodedDataLength.length + _transaction.data.length + rEncoded.length + sEncoded.length + vEncoded.length; // Safe cast, because the length of the list can't be so large. encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); } return keccak256( bytes.concat( encodedListLength, encodedNonce, encodedGasParam, encodedTo, encodedValue, encodedDataLength, _transaction.data, vEncoded, rEncoded, sEncoded ) ); } /// @notice Calculates the hash for an EIP2930 transaction. /// @param _transaction The EIP2930 transaction. /// @return txHash The hash of the transaction. function encodeEIP2930TransactionHash(Transaction calldata _transaction) internal view returns (bytes32) { // Encode all fixed-length params to avoid "stack too deep error" bytes memory encodedFixedLengthParams; { bytes memory encodedChainId = RLPEncoder.encodeUint256(block.chainid); bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); encodedFixedLengthParams = bytes.concat( encodedChainId, encodedNonce, encodedGasPrice, encodedGasLimit, encodedTo, encodedValue ); } // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. bytes memory encodedDataLength; { // Safe cast, because the length of the transaction data can't be so large. uint64 txDataLen = uint64(_transaction.data.length); if (txDataLen != 1) { // If the length is not equal to one, then only using the length can it be encoded definitely. encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); } else if (_transaction.data[0] >= 0x80) { // If input is a byte in [0x80, 0xff] range, RLP encoding will concatenates 0x81 with the byte. encodedDataLength = hex"81"; } // Otherwise the length is not encoded at all. } // On zkSync, access lists are always zero length (at least for now). bytes memory encodedAccessListLength = RLPEncoder.encodeListLen(0); bytes memory rEncoded; { uint256 rInt = uint256(bytes32(_transaction.signature[0:32])); rEncoded = RLPEncoder.encodeUint256(rInt); } bytes memory sEncoded; { uint256 sInt = uint256(bytes32(_transaction.signature[32:64])); sEncoded = RLPEncoder.encodeUint256(sInt); } bytes memory vEncoded; { uint256 vInt = uint256(uint8(_transaction.signature[64])); require(vInt == 27 || vInt == 28, "Invalid v value"); vEncoded = RLPEncoder.encodeUint256(vInt - 27); } bytes memory encodedListLength; unchecked { uint256 listLength = encodedFixedLengthParams.length + encodedDataLength.length + _transaction.data.length + encodedAccessListLength.length + rEncoded.length + sEncoded.length + vEncoded.length; // Safe cast, because the length of the list can't be so large. encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); } return keccak256( bytes.concat( "\x01", encodedListLength, encodedFixedLengthParams, encodedDataLength, _transaction.data, encodedAccessListLength, vEncoded, rEncoded, sEncoded ) ); } /// @notice Calculates the hash for an EIP1559 transaction. /// @param _transaction The legacy transaction. /// @return txHash The hash of the transaction. function encodeEIP1559TransactionHash(Transaction calldata _transaction) internal view returns (bytes32) { // The formula for hash of EIP1559 transaction in the original proposal: // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md // Encode all fixed-length params to avoid "stack too deep error" bytes memory encodedFixedLengthParams; { bytes memory encodedChainId = RLPEncoder.encodeUint256(block.chainid); bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); bytes memory encodedMaxPriorityFeePerGas = RLPEncoder.encodeUint256(_transaction.maxPriorityFeePerGas); bytes memory encodedMaxFeePerGas = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); encodedFixedLengthParams = bytes.concat( encodedChainId, encodedNonce, encodedMaxPriorityFeePerGas, encodedMaxFeePerGas, encodedGasLimit, encodedTo, encodedValue ); } // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. bytes memory encodedDataLength; { // Safe cast, because the length of the transaction data can't be so large. uint64 txDataLen = uint64(_transaction.data.length); if (txDataLen != 1) { // If the length is not equal to one, then only using the length can it be encoded definitely. encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); } else if (_transaction.data[0] >= 0x80) { // If input is a byte in [0x80, 0xff] range, RLP encoding will concatenates 0x81 with the byte. encodedDataLength = hex"81"; } // Otherwise the length is not encoded at all. } // On zkSync, access lists are always zero length (at least for now). bytes memory encodedAccessListLength = RLPEncoder.encodeListLen(0); bytes memory rEncoded; { uint256 rInt = uint256(bytes32(_transaction.signature[0:32])); rEncoded = RLPEncoder.encodeUint256(rInt); } bytes memory sEncoded; { uint256 sInt = uint256(bytes32(_transaction.signature[32:64])); sEncoded = RLPEncoder.encodeUint256(sInt); } bytes memory vEncoded; { uint256 vInt = uint256(uint8(_transaction.signature[64])); require(vInt == 27 || vInt == 28, "Invalid v value"); vEncoded = RLPEncoder.encodeUint256(vInt - 27); } bytes memory encodedListLength; unchecked { uint256 listLength = encodedFixedLengthParams.length + encodedDataLength.length + _transaction.data.length + encodedAccessListLength.length + rEncoded.length + sEncoded.length + vEncoded.length; // Safe cast, because the length of the list can't be so large. encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); } return keccak256( bytes.concat( "\x02", encodedListLength, encodedFixedLengthParams, encodedDataLength, _transaction.data, encodedAccessListLength, vEncoded, rEncoded, sEncoded ) ); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/IAccountCodeStorage.sol"; import "./interfaces/INonceHolder.sol"; import "./interfaces/IContractDeployer.sol"; import "./interfaces/IKnownCodesStorage.sol"; import "./interfaces/IImmutableSimulator.sol"; import "./interfaces/IEthToken.sol"; import "./interfaces/IL1Messenger.sol"; import "./interfaces/ISystemContext.sol"; import "./interfaces/IBytecodeCompressor.sol"; import "./BootloaderUtilities.sol"; /// @dev All the system contracts introduced by zkSync have their addresses /// started from 2^15 in order to avoid collision with Ethereum precompiles. uint160 constant SYSTEM_CONTRACTS_OFFSET = 0x8000; // 2^15 /// @dev All the system contracts must be located in the kernel space, /// i.e. their addresses must be below 2^16. uint160 constant MAX_SYSTEM_CONTRACT_ADDRESS = 0xffff; // 2^16 - 1 address constant ECRECOVER_SYSTEM_CONTRACT = address(0x01); address constant SHA256_SYSTEM_CONTRACT = address(0x02); /// @dev The current maximum deployed precompile address. /// Note: currently only two precompiles are deployed: /// 0x01 - ecrecover /// 0x02 - sha256 /// Important! So the constant should be updated if more precompiles are deployed. uint256 constant CURRENT_MAX_PRECOMPILE_ADDRESS = uint256(uint160(SHA256_SYSTEM_CONTRACT)); address payable constant BOOTLOADER_FORMAL_ADDRESS = payable(address(SYSTEM_CONTRACTS_OFFSET + 0x01)); IAccountCodeStorage constant ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT = IAccountCodeStorage( address(SYSTEM_CONTRACTS_OFFSET + 0x02) ); INonceHolder constant NONCE_HOLDER_SYSTEM_CONTRACT = INonceHolder(address(SYSTEM_CONTRACTS_OFFSET + 0x03)); IKnownCodesStorage constant KNOWN_CODE_STORAGE_CONTRACT = IKnownCodesStorage(address(SYSTEM_CONTRACTS_OFFSET + 0x04)); IImmutableSimulator constant IMMUTABLE_SIMULATOR_SYSTEM_CONTRACT = IImmutableSimulator( address(SYSTEM_CONTRACTS_OFFSET + 0x05) ); IContractDeployer constant DEPLOYER_SYSTEM_CONTRACT = IContractDeployer(address(SYSTEM_CONTRACTS_OFFSET + 0x06)); // A contract that is allowed to deploy any codehash // on any address. To be used only during an upgrade. address constant FORCE_DEPLOYER = address(SYSTEM_CONTRACTS_OFFSET + 0x07); IL1Messenger constant L1_MESSENGER_CONTRACT = IL1Messenger(address(SYSTEM_CONTRACTS_OFFSET + 0x08)); address constant MSG_VALUE_SYSTEM_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x09); IEthToken constant ETH_TOKEN_SYSTEM_CONTRACT = IEthToken(address(SYSTEM_CONTRACTS_OFFSET + 0x0a)); address constant KECCAK256_SYSTEM_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x10); ISystemContext constant SYSTEM_CONTEXT_CONTRACT = ISystemContext(payable(address(SYSTEM_CONTRACTS_OFFSET + 0x0b))); BootloaderUtilities constant BOOTLOADER_UTILITIES = BootloaderUtilities(address(SYSTEM_CONTRACTS_OFFSET + 0x0c)); address constant EVENT_WRITER_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x0d); IBytecodeCompressor constant BYTECODE_COMPRESSOR_CONTRACT = IBytecodeCompressor( address(SYSTEM_CONTRACTS_OFFSET + 0x0e) ); /// @dev If the bitwise AND of the extraAbi[2] param when calling the MSG_VALUE_SIMULATOR /// is non-zero, the call will be assumed to be a system one. uint256 constant MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT = 1; /// @dev The maximal msg.value that context can have uint256 constant MAX_MSG_VALUE = 2 ** 128 - 1; /// @dev Prefix used during derivation of account addresses using CREATE2 /// @dev keccak256("zksyncCreate2") bytes32 constant CREATE2_PREFIX = 0x2020dba91b30cc0006188af794c2fb30dd8520db7e2c088b7fc7c103c00ca494; /// @dev Prefix used during derivation of account addresses using CREATE /// @dev keccak256("zksyncCreate") bytes32 constant CREATE_PREFIX = 0x63bae3a9951d38e8a3fbb7b70909afc1200610fc5bc55ade242f815974674f23;
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../libraries/TransactionHelper.sol"; bytes4 constant ACCOUNT_VALIDATION_SUCCESS_MAGIC = IAccount.validateTransaction.selector; interface IAccount { /// @notice Called by the bootloader to validate that an account agrees to process the transaction /// (and potentially pay for it). /// @param _txHash The hash of the transaction to be used in the explorer /// @param _suggestedSignedHash The hash of the transaction is signed by EOAs /// @param _transaction The transaction itself /// @return magic The magic value that should be equal to the signature of this function /// if the user agrees to proceed with the transaction. /// @dev The developer should strive to preserve as many steps as possible both for valid /// and invalid transactions as this very method is also used during the gas fee estimation /// (without some of the necessary data, e.g. signature). function validateTransaction( bytes32 _txHash, bytes32 _suggestedSignedHash, Transaction calldata _transaction ) external payable returns (bytes4 magic); function executeTransaction( bytes32 _txHash, bytes32 _suggestedSignedHash, Transaction calldata _transaction ) external payable; // There is no point in providing possible signed hash in the `executeTransactionFromOutside` method, // since it typically should not be trusted. function executeTransactionFromOutside(Transaction calldata _transaction) external payable; function payForTransaction( bytes32 _txHash, bytes32 _suggestedSignedHash, Transaction calldata _transaction ) external payable; function prepareForPaymaster( bytes32 _txHash, bytes32 _possibleSignedHash, Transaction calldata _transaction ) external payable; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IAccountCodeStorage { function storeAccountConstructingCodeHash(address _address, bytes32 _hash) external; function storeAccountConstructedCodeHash(address _address, bytes32 _hash) external; function markAccountCodeHashAsConstructed(address _address) external; function getRawCodeHash(address _address) external view returns (bytes32 codeHash); function getCodeHash(uint256 _input) external view returns (bytes32 codeHash); function getCodeSize(uint256 _input) external view returns (uint256 codeSize); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../libraries/TransactionHelper.sol"; interface IBootloaderUtilities { function getTransactionHashes( Transaction calldata _transaction ) external view returns (bytes32 txHash, bytes32 signedTxHash); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IBytecodeCompressor { function publishCompressedBytecode( bytes calldata _bytecode, bytes calldata _rawCompressedData ) external payable returns (bytes32 bytecodeHash); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IContractDeployer { /// @notice Defines the version of the account abstraction protocol /// that a contract claims to follow. /// - `None` means that the account is just a contract and it should never be interacted /// with as a custom account /// - `Version1` means that the account follows the first version of the account abstraction protocol enum AccountAbstractionVersion { None, Version1 } /// @notice Defines the nonce ordering used by the account /// - `Sequential` means that it is expected that the nonces are monotonic and increment by 1 /// at a time (the same as EOAs). /// - `Arbitrary` means that the nonces for the accounts can be arbitrary. The operator /// should serve the transactions from such an account on a first-come-first-serve basis. /// @dev This ordering is more of a suggestion to the operator on how the AA expects its transactions /// to be processed and is not considered as a system invariant. enum AccountNonceOrdering { Sequential, Arbitrary } struct AccountInfo { AccountAbstractionVersion supportedAAVersion; AccountNonceOrdering nonceOrdering; } event ContractDeployed( address indexed deployerAddress, bytes32 indexed bytecodeHash, address indexed contractAddress ); event AccountNonceOrderingUpdated(address indexed accountAddress, AccountNonceOrdering nonceOrdering); event AccountVersionUpdated(address indexed accountAddress, AccountAbstractionVersion aaVersion); function getNewAddressCreate2( address _sender, bytes32 _bytecodeHash, bytes32 _salt, bytes calldata _input ) external view returns (address newAddress); function getNewAddressCreate(address _sender, uint256 _senderNonce) external pure returns (address newAddress); function create2( bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input ) external payable returns (address newAddress); function create2Account( bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input, AccountAbstractionVersion _aaVersion ) external payable returns (address newAddress); /// @dev While the `_salt` parameter is not used anywhere here, /// it is still needed for consistency between `create` and /// `create2` functions (required by the compiler). function create( bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input ) external payable returns (address newAddress); /// @dev While `_salt` is never used here, we leave it here as a parameter /// for the consistency with the `create` function. function createAccount( bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input, AccountAbstractionVersion _aaVersion ) external payable returns (address newAddress); /// @notice Returns the information about a certain AA. function getAccountInfo(address _address) external view returns (AccountInfo memory info); /// @notice Can be called by an account to update its account version function updateAccountVersion(AccountAbstractionVersion _version) external; /// @notice Can be called by an account to update its nonce ordering function updateNonceOrdering(AccountNonceOrdering _nonceOrdering) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IEthToken { function balanceOf(uint256) external view returns (uint256); function transferFromTo(address _from, address _to, uint256 _amount) external; function totalSupply() external view returns (uint256); function name() external pure returns (string memory); function symbol() external pure returns (string memory); function decimals() external pure returns (uint8); function mint(address _account, uint256 _amount) external; function withdraw(address _l1Receiver) external payable; event Mint(address indexed account, uint256 amount); event Transfer(address indexed from, address indexed to, uint256 value); event Withdrawal(address indexed _l2Sender, address indexed _l1Receiver, uint256 _amount); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; struct ImmutableData { uint256 index; bytes32 value; } interface IImmutableSimulator { function getImmutable(address _dest, uint256 _index) external view returns (bytes32); function setImmutables(address _dest, ImmutableData[] calldata _immutables) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IKnownCodesStorage { event MarkedAsKnown(bytes32 indexed bytecodeHash, bool indexed sendBytecodeToL1); function markFactoryDeps(bool _shouldSendToL1, bytes32[] calldata _hashes) external; function markBytecodeAsPublished( bytes32 _bytecodeHash, bytes32 _l1PreimageHash, uint256 _l1PreimageBytesLen ) external; function getMarker(bytes32 _hash) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IL1Messenger { // Possibly in the future we will be able to track the messages sent to L1 with // some hooks in the VM. For now, it is much easier to track them with L2 events. event L1MessageSent(address indexed _sender, bytes32 indexed _hash, bytes _message); function sendToL1(bytes memory _message) external returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @author Matter Labs * @dev Interface of the nonce holder contract -- a contract used by the system to ensure * that there is always a unique identifier for a transaction with a particular account (we call it nonce). * In other words, the pair of (address, nonce) should always be unique. * @dev Custom accounts should use methods of this contract to store nonces or other possible unique identifiers * for the transaction. */ interface INonceHolder { event ValueSetUnderNonce(address indexed accountAddress, uint256 indexed key, uint256 value); /// @dev Returns the current minimal nonce for account. function getMinNonce(address _address) external view returns (uint256); /// @dev Returns the raw version of the current minimal nonce /// (equal to minNonce + 2^128 * deployment nonce). function getRawNonce(address _address) external view returns (uint256); /// @dev Increases the minimal nonce for the msg.sender. function increaseMinNonce(uint256 _value) external returns (uint256); /// @dev Sets the nonce value `key` as used. function setValueUnderNonce(uint256 _key, uint256 _value) external; /// @dev Gets the value stored inside a custom nonce. function getValueUnderNonce(uint256 _key) external view returns (uint256); /// @dev A convenience method to increment the minimal nonce if it is equal /// to the `_expectedNonce`. function incrementMinNonceIfEquals(uint256 _expectedNonce) external; /// @dev Returns the deployment nonce for the accounts used for CREATE opcode. function getDeploymentNonce(address _address) external view returns (uint256); /// @dev Increments the deployment nonce for the account and returns the previous one. function incrementDeploymentNonce(address _address) external returns (uint256); /// @dev Determines whether a certain nonce has been already used for an account. function validateNonceUsage(address _address, uint256 _key, bool _shouldBeUsed) external view; /// @dev Returns whether a nonce has been used for an account. function isNonceUsed(address _address, uint256 _nonce) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../libraries/TransactionHelper.sol"; enum ExecutionResult { Revert, Success } bytes4 constant PAYMASTER_VALIDATION_SUCCESS_MAGIC = IPaymaster.validateAndPayForPaymasterTransaction.selector; interface IPaymaster { /// @dev Called by the bootloader to verify that the paymaster agrees to pay for the /// fee for the transaction. This transaction should also send the necessary amount of funds onto the bootloader /// address. /// @param _txHash The hash of the transaction /// @param _suggestedSignedHash The hash of the transaction that is signed by an EOA /// @param _transaction The transaction itself. /// @return magic The value that should be equal to the signature of the validateAndPayForPaymasterTransaction /// if the paymaster agrees to pay for the transaction. /// @return context The "context" of the transaction: an array of bytes of length at most 1024 bytes, which will be /// passed to the `postTransaction` method of the account. /// @dev The developer should strive to preserve as many steps as possible both for valid /// and invalid transactions as this very method is also used during the gas fee estimation /// (without some of the necessary data, e.g. signature). function validateAndPayForPaymasterTransaction( bytes32 _txHash, bytes32 _suggestedSignedHash, Transaction calldata _transaction ) external payable returns (bytes4 magic, bytes memory context); /// @dev Called by the bootloader after the execution of the transaction. Please note that /// there is no guarantee that this method will be called at all. Unlike the original EIP4337, /// this method won't be called if the transaction execution results in out-of-gas. /// @param _context, the context of the execution, returned by the "validateAndPayForPaymasterTransaction" method. /// @param _transaction, the users' transaction. /// @param _txResult, the result of the transaction execution (success or failure). /// @param _maxRefundedGas, the upper bound on the amout of gas that could be refunded to the paymaster. /// @dev The exact amount refunded depends on the gas spent by the "postOp" itself and so the developers should /// take that into account. function postTransaction( bytes calldata _context, Transaction calldata _transaction, bytes32 _txHash, bytes32 _suggestedSignedHash, ExecutionResult _txResult, uint256 _maxRefundedGas ) external payable; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @author Matter Labs * @dev The interface that is used for encoding/decoding of * different types of paymaster flows. * @notice This is NOT an interface to be implementated * by contracts. It is just used for encoding. */ interface IPaymasterFlow { function general(bytes calldata input) external; function approvalBased(address _token, uint256 _minAllowance, bytes calldata _innerInput) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @author Matter Labs * @notice Contract that stores some of the context variables, that may be either * block-scoped, tx-scoped or system-wide. */ interface ISystemContext { function chainId() external view returns (uint256); function origin() external view returns (address); function gasPrice() external view returns (uint256); function blockGasLimit() external view returns (uint256); function coinbase() external view returns (address); function difficulty() external view returns (uint256); function baseFee() external view returns (uint256); function blockHash(uint256 _block) external view returns (bytes32); function getBlockHashEVM(uint256 _block) external view returns (bytes32); function getBlockNumberAndTimestamp() external view returns (uint256 blockNumber, uint256 blockTimestamp); // Note, that for now, the implementation of the bootloader allows this variables to // be incremented multiple times inside a block, so it should not relied upon right now. function getBlockNumber() external view returns (uint256); function getBlockTimestamp() external view returns (uint256); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; import "./SystemContractHelper.sol"; import "./Utils.sol"; import {SHA256_SYSTEM_CONTRACT, KECCAK256_SYSTEM_CONTRACT} from "../Constants.sol"; /** * @author Matter Labs * @notice This library is used to perform ultra-efficient calls using zkEVM-specific features. * @dev EVM calls always accept a memory slice as input and return a memory slice as output. * Therefore, even if the user has a ready-made calldata slice, they still need to copy it to memory * before calling. This is especially inefficient for large inputs (proxies, multi-calls, etc.). * In turn, zkEVM operates over a fat pointer, which is a set of (memory page, offset, start, length) in the memory/calldata/returndata. * This allows forwarding the calldata slice as is, without copying it to memory. * @dev Fat pointer is not just an integer, it is an extended data type supported on the VM level. * zkEVM creates the wellformed fat pointers for all the calldata/returndata regions, later * the contract may manipulate the already created fat pointers to forward a slice of the data, but not * to create new fat pointers! * @dev The allowed operation on fat pointers are: * 1. `ptr.add` - Transforms `ptr.offset` into `ptr.offset + u32(_value)`. If overflow happens then it panics. * 2. `ptr.sub` - Transforms `ptr.offset` into `ptr.offset - u32(_value)`. If underflow happens then it panics. * 3. `ptr.pack` - Do the concatenation between the lowest 128 bits of the pointer itself and the highest 128 bits of `_value`. It is typically used to prepare the ABI for external calls. * 4. `ptr.shrink` - Transforms `ptr.length` into `ptr.length - u32(_shrink)`. If underflow happens then it panics. * @dev The call opcodes accept the fat pointer and change it to its canonical form before passing it to the child call * 1. `ptr.start` is transformed into `ptr.offset + ptr.start` * 2. `ptr.length` is transformed into `ptr.length - ptr.offset` * 3. `ptr.offset` is transformed into `0` */ library EfficientCall { /// @notice Call the `keccak256` without copying calldata to memory. /// @param _data The preimage data. /// @return The `keccak256` hash. function keccak(bytes calldata _data) internal view returns (bytes32) { bytes memory returnData = staticCall(gasleft(), KECCAK256_SYSTEM_CONTRACT, _data); require(returnData.length == 32, "keccak256 returned invalid data"); return bytes32(returnData); } /// @notice Call the `sha256` precompile without copying calldata to memory. /// @param _data The preimage data. /// @return The `sha256` hash. function sha(bytes calldata _data) internal view returns (bytes32) { bytes memory returnData = staticCall(gasleft(), SHA256_SYSTEM_CONTRACT, _data); require(returnData.length == 32, "sha returned invalid data"); return bytes32(returnData); } /// @notice Perform a `call` without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _value The `msg.value` to send. /// @param _data The calldata to use for the call. /// @param _isSystem Whether the call should contain the `isSystem` flag. /// @return returnData The copied to memory return data. function call( uint256 _gas, address _address, uint256 _value, bytes calldata _data, bool _isSystem ) internal returns (bytes memory returnData) { bool success = rawCall(_gas, _address, _value, _data, _isSystem); returnData = _verifyCallResult(success); } /// @notice Perform a `staticCall` without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _data The calldata to use for the call. /// @return returnData The copied to memory return data. function staticCall( uint256 _gas, address _address, bytes calldata _data ) internal view returns (bytes memory returnData) { bool success = rawStaticCall(_gas, _address, _data); returnData = _verifyCallResult(success); } /// @notice Perform a `delegateCall` without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _data The calldata to use for the call. /// @return returnData The copied to memory return data. function delegateCall( uint256 _gas, address _address, bytes calldata _data ) internal returns (bytes memory returnData) { bool success = rawDelegateCall(_gas, _address, _data); returnData = _verifyCallResult(success); } /// @notice Perform a `mimicCall` (a call with custom msg.sender) without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _data The calldata to use for the call. /// @param _whoToMimic The `msg.sender` for the next call. /// @param _isConstructor Whether the call should contain the `isConstructor` flag. /// @param _isSystem Whether the call should contain the `isSystem` flag. /// @return returnData The copied to memory return data. function mimicCall( uint256 _gas, address _address, bytes calldata _data, address _whoToMimic, bool _isConstructor, bool _isSystem ) internal returns (bytes memory returnData) { bool success = rawMimicCall(_gas, _address, _data, _whoToMimic, _isConstructor, _isSystem); returnData = _verifyCallResult(success); } /// @notice Perform a `call` without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _value The `msg.value` to send. /// @param _data The calldata to use for the call. /// @param _isSystem Whether the call should contain the `isSystem` flag. /// @return success whether the call was successful. function rawCall( uint256 _gas, address _address, uint256 _value, bytes calldata _data, bool _isSystem ) internal returns (bool success) { if (_value == 0) { _loadFarCallABIIntoActivePtr(_gas, _data, false, _isSystem); address callAddr = RAW_FAR_CALL_BY_REF_CALL_ADDRESS; assembly { success := call(_address, callAddr, 0, 0, 0xFFFF, 0, 0) } } else { _loadFarCallABIIntoActivePtr(_gas, _data, false, true); // If there is provided `msg.value` call the `MsgValueSimulator` to forward ether. address msgValueSimulator = MSG_VALUE_SYSTEM_CONTRACT; address callAddr = SYSTEM_CALL_BY_REF_CALL_ADDRESS; // We need to supply the mask to the MsgValueSimulator to denote // that the call should be a system one. uint256 forwardMask = _isSystem ? MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT : 0; assembly { success := call(msgValueSimulator, callAddr, _value, _address, 0xFFFF, forwardMask, 0) } } } /// @notice Perform a `staticCall` without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _data The calldata to use for the call. /// @return success whether the call was successful. function rawStaticCall(uint256 _gas, address _address, bytes calldata _data) internal view returns (bool success) { _loadFarCallABIIntoActivePtr(_gas, _data, false, false); address callAddr = RAW_FAR_CALL_BY_REF_CALL_ADDRESS; assembly { success := staticcall(_address, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Perform a `delegatecall` without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _data The calldata to use for the call. /// @return success whether the call was successful. function rawDelegateCall(uint256 _gas, address _address, bytes calldata _data) internal returns (bool success) { _loadFarCallABIIntoActivePtr(_gas, _data, false, false); address callAddr = RAW_FAR_CALL_BY_REF_CALL_ADDRESS; assembly { success := delegatecall(_address, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Perform a `mimicCall` (call with custom msg.sender) without copying calldata to memory. /// @param _gas The gas to use for the call. /// @param _address The address to call. /// @param _data The calldata to use for the call. /// @param _whoToMimic The `msg.sender` for the next call. /// @param _isConstructor Whether the call should contain the `isConstructor` flag. /// @param _isSystem Whether the call should contain the `isSystem` flag. /// @return success whether the call was successful. /// @dev If called not in kernel mode, it will result in a revert (enforced by the VM) function rawMimicCall( uint256 _gas, address _address, bytes calldata _data, address _whoToMimic, bool _isConstructor, bool _isSystem ) internal returns (bool success) { _loadFarCallABIIntoActivePtr(_gas, _data, _isConstructor, _isSystem); address callAddr = MIMIC_CALL_BY_REF_CALL_ADDRESS; uint256 cleanupMask = ADDRESS_MASK; assembly { // Clearing values before usage in assembly, since Solidity // doesn't do it by default _whoToMimic := and(_whoToMimic, cleanupMask) success := call(_address, callAddr, 0, 0, _whoToMimic, 0, 0) } } /// @dev Verify that a low-level call was successful, and revert if it wasn't, by bubbling the revert reason. /// @param _success Whether the call was successful. /// @return returnData The copied to memory return data. function _verifyCallResult(bool _success) private pure returns (bytes memory returnData) { if (_success) { uint256 size; assembly { size := returndatasize() } returnData = new bytes(size); assembly { returndatacopy(add(returnData, 0x20), 0, size) } } else { propagateRevert(); } } /// @dev Propagate the revert reason from the current call to the caller. function propagateRevert() internal pure { assembly { let size := returndatasize() returndatacopy(0, 0, size) revert(0, size) } } /// @dev Load the far call ABI into active ptr, that will be used for the next call by reference. /// @param _gas The gas to be passed to the call. /// @param _data The calldata to be passed to the call. /// @param _isConstructor Whether the call is a constructor call. /// @param _isSystem Whether the call is a system call. function _loadFarCallABIIntoActivePtr( uint256 _gas, bytes calldata _data, bool _isConstructor, bool _isSystem ) private view { SystemContractHelper.loadCalldataIntoActivePtr(); // Currently, zkEVM considers the pointer valid if(ptr.offset < ptr.length || (ptr.length == 0 && ptr.offset == 0)), otherwise panics. // So, if the data is empty we need to make the `ptr.length = ptr.offset = 0`, otherwise follow standard logic. if (_data.length == 0) { // Safe to cast, offset is never bigger than `type(uint32).max` SystemContractHelper.ptrShrinkIntoActive(uint32(msg.data.length)); } else { uint256 dataOffset; assembly { dataOffset := _data.offset } // Safe to cast, offset is never bigger than `type(uint32).max` SystemContractHelper.ptrAddIntoActive(uint32(dataOffset)); // Safe to cast, `data.length` is never bigger than `type(uint32).max` uint32 shrinkTo = uint32(msg.data.length - (_data.length + dataOffset)); SystemContractHelper.ptrShrinkIntoActive(shrinkTo); } uint32 gas = Utils.safeCastToU32(_gas); uint256 farCallAbi = SystemContractsCaller.getFarCallABIWithEmptyFatPointer( gas, // Only rollup is supported for now 0, CalldataForwardingMode.ForwardFatPointer, _isConstructor, _isSystem ); SystemContractHelper.ptrPackIntoActivePtr(farCallAbi); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library RLPEncoder { function encodeAddress(address _val) internal pure returns (bytes memory encoded) { // The size is equal to 20 bytes of the address itself + 1 for encoding bytes length in RLP. encoded = new bytes(0x15); bytes20 shiftedVal = bytes20(_val); assembly { // In the first byte we write the encoded length as 0x80 + 0x14 == 0x94. mstore(add(encoded, 0x20), 0x9400000000000000000000000000000000000000000000000000000000000000) // Write address data without stripping zeros. mstore(add(encoded, 0x21), shiftedVal) } } function encodeUint256(uint256 _val) internal pure returns (bytes memory encoded) { unchecked { if (_val < 128) { encoded = new bytes(1); // Handle zero as a non-value, since stripping zeroes results in an empty byte array encoded[0] = (_val == 0) ? bytes1(uint8(128)) : bytes1(uint8(_val)); } else { uint256 hbs = _highestByteSet(_val); encoded = new bytes(hbs + 2); encoded[0] = bytes1(uint8(hbs + 0x81)); uint256 lbs = 31 - hbs; uint256 shiftedVal = _val << (lbs * 8); assembly { mstore(add(encoded, 0x21), shiftedVal) } } } } /// @notice Encodes the size of bytes in RLP format. /// @param _len The length of the bytes to encode. It has a `uint64` type since as larger values are not supported. /// NOTE: panics if the length is 1 since the length encoding is ambiguous in this case. function encodeNonSingleBytesLen(uint64 _len) internal pure returns (bytes memory) { assert(_len != 1); return _encodeLength(_len, 0x80); } /// @notice Encodes the size of list items in RLP format. /// @param _len The length of the bytes to encode. It has a `uint64` type since as larger values are not supported. function encodeListLen(uint64 _len) internal pure returns (bytes memory) { return _encodeLength(_len, 0xc0); } function _encodeLength(uint64 _len, uint256 _offset) private pure returns (bytes memory encoded) { unchecked { if (_len < 56) { encoded = new bytes(1); encoded[0] = bytes1(uint8(_len + _offset)); } else { uint256 hbs = _highestByteSet(uint256(_len)); encoded = new bytes(hbs + 2); encoded[0] = bytes1(uint8(_offset + hbs + 56)); uint256 lbs = 31 - hbs; uint256 shiftedVal = uint256(_len) << (lbs * 8); assembly { mstore(add(encoded, 0x21), shiftedVal) } } } } /// @notice Computes the index of the highest byte set in number. /// @notice Uses little endian ordering (The least significant byte has index `0`). /// NOTE: returns `0` for `0` function _highestByteSet(uint256 _number) private pure returns (uint256 hbs) { unchecked { if (_number > type(uint128).max) { _number >>= 128; hbs += 16; } if (_number > type(uint64).max) { _number >>= 64; hbs += 8; } if (_number > type(uint32).max) { _number >>= 32; hbs += 4; } if (_number > type(uint16).max) { _number >>= 16; hbs += 2; } if (_number > type(uint8).max) { hbs += 1; } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8; import {MAX_SYSTEM_CONTRACT_ADDRESS, MSG_VALUE_SYSTEM_CONTRACT} from "../Constants.sol"; import "./SystemContractsCaller.sol"; import "./Utils.sol"; uint256 constant UINT32_MASK = 0xffffffff; uint256 constant UINT128_MASK = 0xffffffffffffffffffffffffffffffff; /// @dev The mask that is used to convert any uint256 to a proper address. /// It needs to be padded with `00` to be treated as uint256 by Solidity uint256 constant ADDRESS_MASK = 0x00ffffffffffffffffffffffffffffffffffffffff; struct ZkSyncMeta { uint32 gasPerPubdataByte; uint32 heapSize; uint32 auxHeapSize; uint8 shardId; uint8 callerShardId; uint8 codeShardId; } enum Global { CalldataPtr, CallFlags, ExtraABIData1, ExtraABIData2, ReturndataPtr } /** * @author Matter Labs * @notice Library used for accessing zkEVM-specific opcodes, needed for the development * of system contracts. * @dev While this library will be eventually available to public, some of the provided * methods won't work for non-system contracts. We will not recommend this library * for external use. */ library SystemContractHelper { /// @notice Send an L2Log to L1. /// @param _isService The `isService` flag. /// @param _key The `key` part of the L2Log. /// @param _value The `value` part of the L2Log. /// @dev The meaning of all these parameters is context-dependent, but they /// have no intrinsic meaning per se. function toL1(bool _isService, bytes32 _key, bytes32 _value) internal { address callAddr = TO_L1_CALL_ADDRESS; assembly { // Ensuring that the type is bool _isService := and(_isService, 1) // This `success` is always 0, but the method always succeeds // (except for the cases when there is not enough gas) let success := call(_isService, callAddr, _key, _value, 0xFFFF, 0, 0) } } /// @notice Get address of the currently executed code. /// @dev This allows differentiating between `call` and `delegatecall`. /// During the former `this` and `codeAddress` are the same, while /// during the latter they are not. function getCodeAddress() internal view returns (address addr) { address callAddr = CODE_ADDRESS_CALL_ADDRESS; assembly { addr := staticcall(0, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Provide a compiler hint, by placing calldata fat pointer into virtual `ACTIVE_PTR`, /// that can be manipulated by `ptr.add`/`ptr.sub`/`ptr.pack`/`ptr.shrink` later. /// @dev This allows making a call by forwarding calldata pointer to the child call. /// It is a much more efficient way to forward calldata, than standard EVM bytes copying. function loadCalldataIntoActivePtr() internal view { address callAddr = LOAD_CALLDATA_INTO_ACTIVE_PTR_CALL_ADDRESS; assembly { pop(staticcall(0, callAddr, 0, 0xFFFF, 0, 0)) } } /// @notice Compiler simulation of the `ptr.pack` opcode for the virtual `ACTIVE_PTR` pointer. /// @dev Do the concatenation between lowest part of `ACTIVE_PTR` and highest part of `_farCallAbi` /// forming packed fat pointer for a far call or ret ABI when necessary. /// Note: Panics if the lowest 128 bits of `_farCallAbi` are not zeroes. function ptrPackIntoActivePtr(uint256 _farCallAbi) internal view { address callAddr = PTR_PACK_INTO_ACTIVE_CALL_ADDRESS; assembly { pop(staticcall(_farCallAbi, callAddr, 0, 0xFFFF, 0, 0)) } } /// @notice Compiler simulation of the `ptr.add` opcode for the virtual `ACTIVE_PTR` pointer. /// @dev Transforms `ACTIVE_PTR.offset` into `ACTIVE_PTR.offset + u32(_value)`. If overflow happens then it panics. function ptrAddIntoActive(uint32 _value) internal view { address callAddr = PTR_ADD_INTO_ACTIVE_CALL_ADDRESS; uint256 cleanupMask = UINT32_MASK; assembly { // Clearing input params as they are not cleaned by Solidity by default _value := and(_value, cleanupMask) pop(staticcall(_value, callAddr, 0, 0xFFFF, 0, 0)) } } /// @notice Compiler simulation of the `ptr.shrink` opcode for the virtual `ACTIVE_PTR` pointer. /// @dev Transforms `ACTIVE_PTR.length` into `ACTIVE_PTR.length - u32(_shrink)`. If underflow happens then it panics. function ptrShrinkIntoActive(uint32 _shrink) internal view { address callAddr = PTR_SHRINK_INTO_ACTIVE_CALL_ADDRESS; uint256 cleanupMask = UINT32_MASK; assembly { // Clearing input params as they are not cleaned by Solidity by default _shrink := and(_shrink, cleanupMask) pop(staticcall(_shrink, callAddr, 0, 0xFFFF, 0, 0)) } } /// @notice packs precompile parameters into one word /// @param _inputMemoryOffset The memory offset in 32-byte words for the input data for calling the precompile. /// @param _inputMemoryLength The length of the input data in words. /// @param _outputMemoryOffset The memory offset in 32-byte words for the output data. /// @param _outputMemoryLength The length of the output data in words. /// @param _perPrecompileInterpreted The constant, the meaning of which is defined separately for /// each precompile. For information, please read the documentation of the precompilecall log in /// the VM. function packPrecompileParams( uint32 _inputMemoryOffset, uint32 _inputMemoryLength, uint32 _outputMemoryOffset, uint32 _outputMemoryLength, uint64 _perPrecompileInterpreted ) internal pure returns (uint256 rawParams) { rawParams = _inputMemoryOffset; rawParams |= uint256(_inputMemoryLength) << 32; rawParams |= uint256(_outputMemoryOffset) << 64; rawParams |= uint256(_outputMemoryLength) << 96; rawParams |= uint256(_perPrecompileInterpreted) << 192; } /// @notice Call precompile with given parameters. /// @param _rawParams The packed precompile params. They can be retrieved by /// the `packPrecompileParams` method. /// @param _gasToBurn The number of gas to burn during this call. /// @return success Whether the call was successful. /// @dev The list of currently available precompiles sha256, keccak256, ecrecover. /// NOTE: The precompile type depends on `this` which calls precompile, which means that only /// system contracts corresponding to the list of precompiles above can do `precompileCall`. /// @dev If used not in the `sha256`, `keccak256` or `ecrecover` contracts, it will just burn the gas provided. function precompileCall(uint256 _rawParams, uint32 _gasToBurn) internal view returns (bool success) { address callAddr = PRECOMPILE_CALL_ADDRESS; // After `precompileCall` gas will be burned down to 0 if there are not enough of them, // thats why it should be checked before the call. require(gasleft() >= _gasToBurn); uint256 cleanupMask = UINT32_MASK; assembly { // Clearing input params as they are not cleaned by Solidity by default _gasToBurn := and(_gasToBurn, cleanupMask) success := staticcall(_rawParams, callAddr, _gasToBurn, 0xFFFF, 0, 0) } } /// @notice Set `msg.value` to next far call. /// @param _value The msg.value that will be used for the *next* call. /// @dev If called not in kernel mode, it will result in a revert (enforced by the VM) function setValueForNextFarCall(uint128 _value) internal returns (bool success) { uint256 cleanupMask = UINT128_MASK; address callAddr = SET_CONTEXT_VALUE_CALL_ADDRESS; assembly { // Clearing input params as they are not cleaned by Solidity by default _value := and(_value, cleanupMask) success := call(0, callAddr, _value, 0, 0xFFFF, 0, 0) } } /// @notice Initialize a new event. /// @param initializer The event initializing value. /// @param value1 The first topic or data chunk. function eventInitialize(uint256 initializer, uint256 value1) internal { address callAddr = EVENT_INITIALIZE_ADDRESS; assembly { pop(call(initializer, callAddr, value1, 0, 0xFFFF, 0, 0)) } } /// @notice Continue writing the previously initialized event. /// @param value1 The first topic or data chunk. /// @param value2 The second topic or data chunk. function eventWrite(uint256 value1, uint256 value2) internal { address callAddr = EVENT_WRITE_ADDRESS; assembly { pop(call(value1, callAddr, value2, 0, 0xFFFF, 0, 0)) } } /// @notice Get the packed representation of the `ZkSyncMeta` from the current context. /// @return meta The packed representation of the ZkSyncMeta. /// @dev The fields in ZkSyncMeta are NOT tightly packed, i.e. there is a special rule on how /// they are packed. For more information, please read the documentation on ZkSyncMeta. function getZkSyncMetaBytes() internal view returns (uint256 meta) { address callAddr = META_CALL_ADDRESS; assembly { meta := staticcall(0, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Returns the bits [offset..offset+size-1] of the meta. /// @param meta Packed representation of the ZkSyncMeta. /// @param offset The offset of the bits. /// @param size The size of the extracted number in bits. /// @return result The extracted number. function extractNumberFromMeta(uint256 meta, uint256 offset, uint256 size) internal pure returns (uint256 result) { // Firstly, we delete all the bits after the field uint256 shifted = (meta << (256 - size - offset)); // Then we shift everything back result = (shifted >> (256 - size)); } /// @notice Given the packed representation of `ZkSyncMeta`, retrieves the number of gas /// that a single byte sent to L1 as pubdata costs. /// @param meta Packed representation of the ZkSyncMeta. /// @return gasPerPubdataByte The current price in gas per pubdata byte. function getGasPerPubdataByteFromMeta(uint256 meta) internal pure returns (uint32 gasPerPubdataByte) { gasPerPubdataByte = uint32(extractNumberFromMeta(meta, META_GAS_PER_PUBDATA_BYTE_OFFSET, 32)); } /// @notice Given the packed representation of `ZkSyncMeta`, retrieves the number of the current size /// of the heap in bytes. /// @param meta Packed representation of the ZkSyncMeta. /// @return heapSize The size of the memory in bytes byte. /// @dev The following expression: getHeapSizeFromMeta(getZkSyncMetaBytes()) is /// equivalent to the MSIZE in Solidity. function getHeapSizeFromMeta(uint256 meta) internal pure returns (uint32 heapSize) { heapSize = uint32(extractNumberFromMeta(meta, META_HEAP_SIZE_OFFSET, 32)); } /// @notice Given the packed representation of `ZkSyncMeta`, retrieves the number of the current size /// of the auxilary heap in bytes. /// @param meta Packed representation of the ZkSyncMeta. /// @return auxHeapSize The size of the auxilary memory in bytes byte. /// @dev You can read more on auxilary memory in the VM1.2 documentation. function getAuxHeapSizeFromMeta(uint256 meta) internal pure returns (uint32 auxHeapSize) { auxHeapSize = uint32(extractNumberFromMeta(meta, META_AUX_HEAP_SIZE_OFFSET, 32)); } /// @notice Given the packed representation of `ZkSyncMeta`, retrieves the shardId of `this`. /// @param meta Packed representation of the ZkSyncMeta. /// @return shardId The shardId of `this`. /// @dev Currently only shard 0 (zkRollup) is supported. function getShardIdFromMeta(uint256 meta) internal pure returns (uint8 shardId) { shardId = uint8(extractNumberFromMeta(meta, META_SHARD_ID_OFFSET, 8)); } /// @notice Given the packed representation of `ZkSyncMeta`, retrieves the shardId of /// the msg.sender. /// @param meta Packed representation of the ZkSyncMeta. /// @return callerShardId The shardId of the msg.sender. /// @dev Currently only shard 0 (zkRollup) is supported. function getCallerShardIdFromMeta(uint256 meta) internal pure returns (uint8 callerShardId) { callerShardId = uint8(extractNumberFromMeta(meta, META_CALLER_SHARD_ID_OFFSET, 8)); } /// @notice Given the packed representation of `ZkSyncMeta`, retrieves the shardId of /// the currently executed code. /// @param meta Packed representation of the ZkSyncMeta. /// @return codeShardId The shardId of the currently executed code. /// @dev Currently only shard 0 (zkRollup) is supported. function getCodeShardIdFromMeta(uint256 meta) internal pure returns (uint8 codeShardId) { codeShardId = uint8(extractNumberFromMeta(meta, META_CODE_SHARD_ID_OFFSET, 8)); } /// @notice Retrieves the ZkSyncMeta structure. /// @return meta The ZkSyncMeta execution context parameters. function getZkSyncMeta() internal view returns (ZkSyncMeta memory meta) { uint256 metaPacked = getZkSyncMetaBytes(); meta.gasPerPubdataByte = getGasPerPubdataByteFromMeta(metaPacked); meta.shardId = getShardIdFromMeta(metaPacked); meta.callerShardId = getCallerShardIdFromMeta(metaPacked); meta.codeShardId = getCodeShardIdFromMeta(metaPacked); } /// @notice Returns the call flags for the current call. /// @return callFlags The bitmask of the callflags. /// @dev Call flags is the value of the first register /// at the start of the call. /// @dev The zero bit of the callFlags indicates whether the call is /// a constructor call. The first bit of the callFlags indicates whether /// the call is a system one. function getCallFlags() internal view returns (uint256 callFlags) { address callAddr = CALLFLAGS_CALL_ADDRESS; assembly { callFlags := staticcall(0, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Returns the current calldata pointer. /// @return ptr The current calldata pointer. /// @dev NOTE: This file is just an integer and it can not be used /// to forward the calldata to the next calls in any way. function getCalldataPtr() internal view returns (uint256 ptr) { address callAddr = PTR_CALLDATA_CALL_ADDRESS; assembly { ptr := staticcall(0, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Returns the N-th extraAbiParam for the current call. /// @return extraAbiData The value of the N-th extraAbiParam for this call. /// @dev It is equal to the value of the (N+2)-th register /// at the start of the call. function getExtraAbiData(uint256 index) internal view returns (uint256 extraAbiData) { require(index < 10, "There are only 10 accessible registers"); address callAddr = GET_EXTRA_ABI_DATA_ADDRESS; assembly { extraAbiData := staticcall(index, callAddr, 0, 0xFFFF, 0, 0) } } /// @notice Retuns whether the current call is a system call. /// @return `true` or `false` based on whether the current call is a system call. function isSystemCall() internal view returns (bool) { uint256 callFlags = getCallFlags(); // When the system call is passed, the 2-bit it set to 1 return (callFlags & 2) != 0; } /// @notice Returns whether the address is a system contract. /// @param _address The address to test /// @return `true` or `false` based on whether the `_address` is a system contract. function isSystemContract(address _address) internal pure returns (bool) { return uint160(_address) <= uint160(MAX_SYSTEM_CONTRACT_ADDRESS); } } /// @dev Solidity does not allow exporting modifiers via libraries, so /// the only way to do reuse modifiers is to have a base contract abstract contract ISystemContract { /// @notice Modifier that makes sure that the method /// can only be called via a system call. modifier onlySystemCall() { require( SystemContractHelper.isSystemCall() || SystemContractHelper.isSystemContract(msg.sender), "This method require system call flag" ); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8; import {MSG_VALUE_SYSTEM_CONTRACT, MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT} from "../Constants.sol"; import "./Utils.sol"; // Addresses used for the compiler to be replaced with the // zkSync-specific opcodes during the compilation. // IMPORTANT: these are just compile-time constants and are used // only if used in-place by Yul optimizer. address constant TO_L1_CALL_ADDRESS = address((1 << 16) - 1); address constant CODE_ADDRESS_CALL_ADDRESS = address((1 << 16) - 2); address constant PRECOMPILE_CALL_ADDRESS = address((1 << 16) - 3); address constant META_CALL_ADDRESS = address((1 << 16) - 4); address constant MIMIC_CALL_CALL_ADDRESS = address((1 << 16) - 5); address constant SYSTEM_MIMIC_CALL_CALL_ADDRESS = address((1 << 16) - 6); address constant MIMIC_CALL_BY_REF_CALL_ADDRESS = address((1 << 16) - 7); address constant SYSTEM_MIMIC_CALL_BY_REF_CALL_ADDRESS = address((1 << 16) - 8); address constant RAW_FAR_CALL_CALL_ADDRESS = address((1 << 16) - 9); address constant RAW_FAR_CALL_BY_REF_CALL_ADDRESS = address((1 << 16) - 10); address constant SYSTEM_CALL_CALL_ADDRESS = address((1 << 16) - 11); address constant SYSTEM_CALL_BY_REF_CALL_ADDRESS = address((1 << 16) - 12); address constant SET_CONTEXT_VALUE_CALL_ADDRESS = address((1 << 16) - 13); address constant SET_PUBDATA_PRICE_CALL_ADDRESS = address((1 << 16) - 14); address constant INCREMENT_TX_COUNTER_CALL_ADDRESS = address((1 << 16) - 15); address constant PTR_CALLDATA_CALL_ADDRESS = address((1 << 16) - 16); address constant CALLFLAGS_CALL_ADDRESS = address((1 << 16) - 17); address constant PTR_RETURNDATA_CALL_ADDRESS = address((1 << 16) - 18); address constant EVENT_INITIALIZE_ADDRESS = address((1 << 16) - 19); address constant EVENT_WRITE_ADDRESS = address((1 << 16) - 20); address constant LOAD_CALLDATA_INTO_ACTIVE_PTR_CALL_ADDRESS = address((1 << 16) - 21); address constant LOAD_LATEST_RETURNDATA_INTO_ACTIVE_PTR_CALL_ADDRESS = address((1 << 16) - 22); address constant PTR_ADD_INTO_ACTIVE_CALL_ADDRESS = address((1 << 16) - 23); address constant PTR_SHRINK_INTO_ACTIVE_CALL_ADDRESS = address((1 << 16) - 24); address constant PTR_PACK_INTO_ACTIVE_CALL_ADDRESS = address((1 << 16) - 25); address constant MULTIPLICATION_HIGH_ADDRESS = address((1 << 16) - 26); address constant GET_EXTRA_ABI_DATA_ADDRESS = address((1 << 16) - 27); // All the offsets are in bits uint256 constant META_GAS_PER_PUBDATA_BYTE_OFFSET = 0 * 8; uint256 constant META_HEAP_SIZE_OFFSET = 8 * 8; uint256 constant META_AUX_HEAP_SIZE_OFFSET = 12 * 8; uint256 constant META_SHARD_ID_OFFSET = 28 * 8; uint256 constant META_CALLER_SHARD_ID_OFFSET = 29 * 8; uint256 constant META_CODE_SHARD_ID_OFFSET = 30 * 8; /// @notice The way to forward the calldata: /// - Use the current heap (i.e. the same as on EVM). /// - Use the auxiliary heap. /// - Forward via a pointer /// @dev Note, that currently, users do not have access to the auxiliary /// heap and so the only type of forwarding that will be used by the users /// are UseHeap and ForwardFatPointer for forwarding a slice of the current calldata /// to the next call. enum CalldataForwardingMode { UseHeap, ForwardFatPointer, UseAuxHeap } /** * @author Matter Labs * @notice A library that allows calling contracts with the `isSystem` flag. * @dev It is needed to call ContractDeployer and NonceHolder. */ library SystemContractsCaller { /// @notice Makes a call with the `isSystem` flag. /// @param gasLimit The gas limit for the call. /// @param to The address to call. /// @param value The value to pass with the transaction. /// @param data The calldata. /// @return success Whether the transaction has been successful. /// @dev Note, that the `isSystem` flag can only be set when calling system contracts. function systemCall(uint32 gasLimit, address to, uint256 value, bytes memory data) internal returns (bool success) { address callAddr = SYSTEM_CALL_CALL_ADDRESS; uint32 dataStart; assembly { dataStart := add(data, 0x20) } uint32 dataLength = uint32(Utils.safeCastToU32(data.length)); uint256 farCallAbi = SystemContractsCaller.getFarCallABI( 0, 0, dataStart, dataLength, gasLimit, // Only rollup is supported for now 0, CalldataForwardingMode.UseHeap, false, true ); if (value == 0) { // Doing the system call directly assembly { success := call(to, callAddr, 0, 0, farCallAbi, 0, 0) } } else { address msgValueSimulator = MSG_VALUE_SYSTEM_CONTRACT; // We need to supply the mask to the MsgValueSimulator to denote // that the call should be a system one. uint256 forwardMask = MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT; assembly { success := call(msgValueSimulator, callAddr, value, to, farCallAbi, forwardMask, 0) } } } /// @notice Makes a call with the `isSystem` flag. /// @param gasLimit The gas limit for the call. /// @param to The address to call. /// @param value The value to pass with the transaction. /// @param data The calldata. /// @return success Whether the transaction has been successful. /// @return returnData The returndata of the transaction (revert reason in case the transaction has failed). /// @dev Note, that the `isSystem` flag can only be set when calling system contracts. function systemCallWithReturndata( uint32 gasLimit, address to, uint128 value, bytes memory data ) internal returns (bool success, bytes memory returnData) { success = systemCall(gasLimit, to, value, data); uint256 size; assembly { size := returndatasize() } returnData = new bytes(size); assembly { returndatacopy(add(returnData, 0x20), 0, size) } } /// @notice Makes a call with the `isSystem` flag. /// @param gasLimit The gas limit for the call. /// @param to The address to call. /// @param value The value to pass with the transaction. /// @param data The calldata. /// @return returnData The returndata of the transaction. In case the transaction reverts, the error /// bubbles up to the parent frame. /// @dev Note, that the `isSystem` flag can only be set when calling system contracts. function systemCallWithPropagatedRevert( uint32 gasLimit, address to, uint128 value, bytes memory data ) internal returns (bytes memory returnData) { bool success; (success, returnData) = systemCallWithReturndata(gasLimit, to, value, data); if (!success) { assembly { let size := mload(returnData) revert(add(returnData, 0x20), size) } } } /// @notice Calculates the packed representation of the FarCallABI. /// @param dataOffset Calldata offset in memory. Provide 0 unless using custom pointer. /// @param memoryPage Memory page to use. Provide 0 unless using custom pointer. /// @param dataStart The start of the calldata slice. Provide the offset in memory /// if not using custom pointer. /// @param dataLength The calldata length. Provide the length of the calldata in bytes /// unless using custom pointer. /// @param gasPassed The gas to pass with the call. /// @param shardId Of the account to call. Currently only 0 is supported. /// @param forwardingMode The forwarding mode to use: /// - provide CalldataForwardingMode.UseHeap when using your current memory /// - provide CalldataForwardingMode.ForwardFatPointer when using custom pointer. /// @param isConstructorCall Whether the call will be a call to the constructor /// (ignored when the caller is not a system contract). /// @param isSystemCall Whether the call will have the `isSystem` flag. /// @return farCallAbi The far call ABI. /// @dev The `FarCallABI` has the following structure: /// pub struct FarCallABI { /// pub memory_quasi_fat_pointer: FatPointer, /// pub gas_passed: u32, /// pub shard_id: u8, /// pub forwarding_mode: FarCallForwardPageType, /// pub constructor_call: bool, /// pub to_system: bool, /// } /// /// The FatPointer struct: /// /// pub struct FatPointer { /// pub offset: u32, // offset relative to `start` /// pub memory_page: u32, // memory page where slice is located /// pub start: u32, // absolute start of the slice /// pub length: u32, // length of the slice /// } /// /// @dev Note, that the actual layout is the following: /// /// [0..32) bits -- the calldata offset /// [32..64) bits -- the memory page to use. Can be left blank in most of the cases. /// [64..96) bits -- the absolute start of the slice /// [96..128) bits -- the length of the slice. /// [128..192) bits -- empty bits. /// [192..224) bits -- gasPassed. /// [224..232) bits -- forwarding_mode /// [232..240) bits -- shard id. /// [240..248) bits -- constructor call flag /// [248..256] bits -- system call flag function getFarCallABI( uint32 dataOffset, uint32 memoryPage, uint32 dataStart, uint32 dataLength, uint32 gasPassed, uint8 shardId, CalldataForwardingMode forwardingMode, bool isConstructorCall, bool isSystemCall ) internal pure returns (uint256 farCallAbi) { // Fill in the call parameter fields farCallAbi = getFarCallABIWithEmptyFatPointer( gasPassed, shardId, forwardingMode, isConstructorCall, isSystemCall ); // Fill in the fat pointer fields farCallAbi |= dataOffset; farCallAbi |= (uint256(memoryPage) << 32); farCallAbi |= (uint256(dataStart) << 64); farCallAbi |= (uint256(dataLength) << 96); } /// @notice Calculates the packed representation of the FarCallABI with zero fat pointer fields. /// @param gasPassed The gas to pass with the call. /// @param shardId Of the account to call. Currently only 0 is supported. /// @param forwardingMode The forwarding mode to use: /// - provide CalldataForwardingMode.UseHeap when using your current memory /// - provide CalldataForwardingMode.ForwardFatPointer when using custom pointer. /// @param isConstructorCall Whether the call will be a call to the constructor /// (ignored when the caller is not a system contract). /// @param isSystemCall Whether the call will have the `isSystem` flag. /// @return farCallAbiWithEmptyFatPtr The far call ABI with zero fat pointer fields. function getFarCallABIWithEmptyFatPointer( uint32 gasPassed, uint8 shardId, CalldataForwardingMode forwardingMode, bool isConstructorCall, bool isSystemCall ) internal pure returns (uint256 farCallAbiWithEmptyFatPtr) { farCallAbiWithEmptyFatPtr |= (uint256(gasPassed) << 192); farCallAbiWithEmptyFatPtr |= (uint256(forwardingMode) << 224); farCallAbiWithEmptyFatPtr |= (uint256(shardId) << 232); if (isConstructorCall) { farCallAbiWithEmptyFatPtr |= (1 << 240); } if (isSystemCall) { farCallAbiWithEmptyFatPtr |= (1 << 248); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../openzeppelin/token/ERC20/IERC20.sol"; import "../openzeppelin/token/ERC20/utils/SafeERC20.sol"; import "../interfaces/IPaymasterFlow.sol"; import "../interfaces/IContractDeployer.sol"; import {ETH_TOKEN_SYSTEM_CONTRACT, BOOTLOADER_FORMAL_ADDRESS} from "../Constants.sol"; import "./RLPEncoder.sol"; import "./EfficientCall.sol"; /// @dev The type id of zkSync's EIP-712-signed transaction. uint8 constant EIP_712_TX_TYPE = 0x71; /// @dev The type id of legacy transactions. uint8 constant LEGACY_TX_TYPE = 0x0; /// @dev The type id of legacy transactions. uint8 constant EIP_2930_TX_TYPE = 0x01; /// @dev The type id of EIP1559 transactions. uint8 constant EIP_1559_TX_TYPE = 0x02; /// @notice Structure used to represent zkSync transaction. struct Transaction { // The type of the transaction. uint256 txType; // The caller. uint256 from; // The callee. uint256 to; // The gasLimit to pass with the transaction. // It has the same meaning as Ethereum's gasLimit. uint256 gasLimit; // The maximum amount of gas the user is willing to pay for a byte of pubdata. uint256 gasPerPubdataByteLimit; // The maximum fee per gas that the user is willing to pay. // It is akin to EIP1559's maxFeePerGas. uint256 maxFeePerGas; // The maximum priority fee per gas that the user is willing to pay. // It is akin to EIP1559's maxPriorityFeePerGas. uint256 maxPriorityFeePerGas; // The transaction's paymaster. If there is no paymaster, it is equal to 0. uint256 paymaster; // The nonce of the transaction. uint256 nonce; // The value to pass with the transaction. uint256 value; // In the future, we might want to add some // new fields to the struct. The `txData` struct // is to be passed to account and any changes to its structure // would mean a breaking change to these accounts. In order to prevent this, // we should keep some fields as "reserved". // It is also recommended that their length is fixed, since // it would allow easier proof integration (in case we will need // some special circuit for preprocessing transactions). uint256[4] reserved; // The transaction's calldata. bytes data; // The signature of the transaction. bytes signature; // The properly formatted hashes of bytecodes that must be published on L1 // with the inclusion of this transaction. Note, that a bytecode has been published // before, the user won't pay fees for its republishing. bytes32[] factoryDeps; // The input to the paymaster. bytes paymasterInput; // Reserved dynamic type for the future use-case. Using it should be avoided, // But it is still here, just in case we want to enable some additional functionality. bytes reservedDynamic; } /** * @author Matter Labs * @notice Library is used to help custom accounts to work with common methods for the Transaction type. */ library TransactionHelper { using SafeERC20 for IERC20; /// @notice The EIP-712 typehash for the contract's domain bytes32 constant EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId)"); bytes32 constant EIP712_TRANSACTION_TYPE_HASH = keccak256( "Transaction(uint256 txType,uint256 from,uint256 to,uint256 gasLimit,uint256 gasPerPubdataByteLimit,uint256 maxFeePerGas,uint256 maxPriorityFeePerGas,uint256 paymaster,uint256 nonce,uint256 value,bytes data,bytes32[] factoryDeps,bytes paymasterInput)" ); /// @notice Whether the token is Ethereum. /// @param _addr The address of the token /// @return `true` or `false` based on whether the token is Ether. /// @dev This method assumes that address is Ether either if the address is 0 (for convenience) /// or if the address is the address of the L2EthToken system contract. function isEthToken(uint256 _addr) internal pure returns (bool) { return _addr == uint256(uint160(address(ETH_TOKEN_SYSTEM_CONTRACT))) || _addr == 0; } /// @notice Calculate the suggested signed hash of the transaction, /// i.e. the hash that is signed by EOAs and is recommended to be signed by other accounts. function encodeHash(Transaction calldata _transaction) internal view returns (bytes32 resultHash) { if (_transaction.txType == LEGACY_TX_TYPE) { resultHash = _encodeHashLegacyTransaction(_transaction); } else if (_transaction.txType == EIP_712_TX_TYPE) { resultHash = _encodeHashEIP712Transaction(_transaction); } else if (_transaction.txType == EIP_1559_TX_TYPE) { resultHash = _encodeHashEIP1559Transaction(_transaction); } else if (_transaction.txType == EIP_2930_TX_TYPE) { resultHash = _encodeHashEIP2930Transaction(_transaction); } else { // Currently no other transaction types are supported. // Any new transaction types will be processed in a similar manner. revert("Encoding unsupported tx"); } } /// @notice Encode hash of the zkSync native transaction type. /// @return keccak256 hash of the EIP-712 encoded representation of transaction function _encodeHashEIP712Transaction(Transaction calldata _transaction) private view returns (bytes32) { bytes32 structHash = keccak256( abi.encode( EIP712_TRANSACTION_TYPE_HASH, _transaction.txType, _transaction.from, _transaction.to, _transaction.gasLimit, _transaction.gasPerPubdataByteLimit, _transaction.maxFeePerGas, _transaction.maxPriorityFeePerGas, _transaction.paymaster, _transaction.nonce, _transaction.value, EfficientCall.keccak(_transaction.data), keccak256(abi.encodePacked(_transaction.factoryDeps)), EfficientCall.keccak(_transaction.paymasterInput) ) ); bytes32 domainSeparator = keccak256( abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256("zkSync"), keccak256("2"), block.chainid) ); return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } /// @notice Encode hash of the legacy transaction type. /// @return keccak256 of the serialized RLP encoded representation of transaction function _encodeHashLegacyTransaction(Transaction calldata _transaction) private view returns (bytes32) { // Hash of legacy transactions are encoded as one of the: // - RLP(nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0) // - RLP(nonce, gasPrice, gasLimit, to, value, data) // // In this RLP encoding, only the first one above list appears, so we encode each element // inside list and then concatenate the length of all elements with them. bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); // Encode `gasPrice` and `gasLimit` together to prevent "stack too deep error". bytes memory encodedGasParam; { bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); encodedGasParam = bytes.concat(encodedGasPrice, encodedGasLimit); } bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. bytes memory encodedDataLength; { // Safe cast, because the length of the transaction data can't be so large. uint64 txDataLen = uint64(_transaction.data.length); if (txDataLen != 1) { // If the length is not equal to one, then only using the length can it be encoded definitely. encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); } else if (_transaction.data[0] >= 0x80) { // If input is a byte in [0x80, 0xff] range, RLP encoding will concatenates 0x81 with the byte. encodedDataLength = hex"81"; } // Otherwise the length is not encoded at all. } // Encode `chainId` according to EIP-155, but only if the `chainId` is specified in the transaction. bytes memory encodedChainId; if (_transaction.reserved[0] != 0) { encodedChainId = bytes.concat(RLPEncoder.encodeUint256(block.chainid), hex"80_80"); } bytes memory encodedListLength; unchecked { uint256 listLength = encodedNonce.length + encodedGasParam.length + encodedTo.length + encodedValue.length + encodedDataLength.length + _transaction.data.length + encodedChainId.length; // Safe cast, because the length of the list can't be so large. encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); } return keccak256( bytes.concat( encodedListLength, encodedNonce, encodedGasParam, encodedTo, encodedValue, encodedDataLength, _transaction.data, encodedChainId ) ); } /// @notice Encode hash of the EIP2930 transaction type. /// @return keccak256 of the serialized RLP encoded representation of transaction function _encodeHashEIP2930Transaction(Transaction calldata _transaction) private view returns (bytes32) { // Hash of EIP2930 transactions is encoded the following way: // H(0x01 || RLP(chain_id, nonce, gas_price, gas_limit, destination, amount, data, access_list)) // // Note, that on zkSync access lists are not supported and should always be empty. // Encode all fixed-length params to avoid "stack too deep error" bytes memory encodedFixedLengthParams; { bytes memory encodedChainId = RLPEncoder.encodeUint256(block.chainid); bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); bytes memory encodedGasPrice = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); encodedFixedLengthParams = bytes.concat( encodedChainId, encodedNonce, encodedGasPrice, encodedGasLimit, encodedTo, encodedValue ); } // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. bytes memory encodedDataLength; { // Safe cast, because the length of the transaction data can't be so large. uint64 txDataLen = uint64(_transaction.data.length); if (txDataLen != 1) { // If the length is not equal to one, then only using the length can it be encoded definitely. encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); } else if (_transaction.data[0] >= 0x80) { // If input is a byte in [0x80, 0xff] range, RLP encoding will concatenates 0x81 with the byte. encodedDataLength = hex"81"; } // Otherwise the length is not encoded at all. } // On zkSync, access lists are always zero length (at least for now). bytes memory encodedAccessListLength = RLPEncoder.encodeListLen(0); bytes memory encodedListLength; unchecked { uint256 listLength = encodedFixedLengthParams.length + encodedDataLength.length + _transaction.data.length + encodedAccessListLength.length; // Safe cast, because the length of the list can't be so large. encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); } return keccak256( bytes.concat( "\x01", encodedListLength, encodedFixedLengthParams, encodedDataLength, _transaction.data, encodedAccessListLength ) ); } /// @notice Encode hash of the EIP1559 transaction type. /// @return keccak256 of the serialized RLP encoded representation of transaction function _encodeHashEIP1559Transaction(Transaction calldata _transaction) private view returns (bytes32) { // Hash of EIP1559 transactions is encoded the following way: // H(0x02 || RLP(chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list)) // // Note, that on zkSync access lists are not supported and should always be empty. // Encode all fixed-length params to avoid "stack too deep error" bytes memory encodedFixedLengthParams; { bytes memory encodedChainId = RLPEncoder.encodeUint256(block.chainid); bytes memory encodedNonce = RLPEncoder.encodeUint256(_transaction.nonce); bytes memory encodedMaxPriorityFeePerGas = RLPEncoder.encodeUint256(_transaction.maxPriorityFeePerGas); bytes memory encodedMaxFeePerGas = RLPEncoder.encodeUint256(_transaction.maxFeePerGas); bytes memory encodedGasLimit = RLPEncoder.encodeUint256(_transaction.gasLimit); bytes memory encodedTo = RLPEncoder.encodeAddress(address(uint160(_transaction.to))); bytes memory encodedValue = RLPEncoder.encodeUint256(_transaction.value); encodedFixedLengthParams = bytes.concat( encodedChainId, encodedNonce, encodedMaxPriorityFeePerGas, encodedMaxFeePerGas, encodedGasLimit, encodedTo, encodedValue ); } // Encode only the length of the transaction data, and not the data itself, // so as not to copy to memory a potentially huge transaction data twice. bytes memory encodedDataLength; { // Safe cast, because the length of the transaction data can't be so large. uint64 txDataLen = uint64(_transaction.data.length); if (txDataLen != 1) { // If the length is not equal to one, then only using the length can it be encoded definitely. encodedDataLength = RLPEncoder.encodeNonSingleBytesLen(txDataLen); } else if (_transaction.data[0] >= 0x80) { // If input is a byte in [0x80, 0xff] range, RLP encoding will concatenates 0x81 with the byte. encodedDataLength = hex"81"; } // Otherwise the length is not encoded at all. } // On zkSync, access lists are always zero length (at least for now). bytes memory encodedAccessListLength = RLPEncoder.encodeListLen(0); bytes memory encodedListLength; unchecked { uint256 listLength = encodedFixedLengthParams.length + encodedDataLength.length + _transaction.data.length + encodedAccessListLength.length; // Safe cast, because the length of the list can't be so large. encodedListLength = RLPEncoder.encodeListLen(uint64(listLength)); } return keccak256( bytes.concat( "\x02", encodedListLength, encodedFixedLengthParams, encodedDataLength, _transaction.data, encodedAccessListLength ) ); } /// @notice Processes the common paymaster flows, e.g. setting proper allowance /// for tokens, etc. For more information on the expected behavior, check out /// the "Paymaster flows" section in the documentation. function processPaymasterInput(Transaction calldata _transaction) internal { require(_transaction.paymasterInput.length >= 4, "The standard paymaster input must be at least 4 bytes long"); bytes4 paymasterInputSelector = bytes4(_transaction.paymasterInput[0:4]); if (paymasterInputSelector == IPaymasterFlow.approvalBased.selector) { require( _transaction.paymasterInput.length >= 68, "The approvalBased paymaster input must be at least 68 bytes long" ); // While the actual data consists of address, uint256 and bytes data, // the data is needed only for the paymaster, so we ignore it here for the sake of optimization (address token, uint256 minAllowance) = abi.decode(_transaction.paymasterInput[4:68], (address, uint256)); address paymaster = address(uint160(_transaction.paymaster)); uint256 currentAllowance = IERC20(token).allowance(address(this), paymaster); if (currentAllowance < minAllowance) { // Some tokens, e.g. USDT require that the allowance is firsty set to zero // and only then updated to the new value. IERC20(token).safeApprove(paymaster, 0); IERC20(token).safeApprove(paymaster, minAllowance); } } else if (paymasterInputSelector == IPaymasterFlow.general.selector) { // Do nothing. general(bytes) paymaster flow means that the paymaster must interpret these bytes on his own. } else { revert("Unsupported paymaster flow"); } } /// @notice Pays the required fee for the transaction to the bootloader. /// @dev Currently it pays the maximum amount "_transaction.maxFeePerGas * _transaction.gasLimit", /// it will change in the future. function payToTheBootloader(Transaction calldata _transaction) internal returns (bool success) { address bootloaderAddr = BOOTLOADER_FORMAL_ADDRESS; uint256 amount = _transaction.maxFeePerGas * _transaction.gasLimit; assembly { success := call(gas(), bootloaderAddr, amount, 0, 0, 0, 0) } } // Returns the balance required to process the transaction. function totalRequiredBalance(Transaction calldata _transaction) internal pure returns (uint256 requiredBalance) { if (address(uint160(_transaction.paymaster)) != address(0)) { // Paymaster pays for the fee requiredBalance = _transaction.value; } else { // The user should have enough balance for both the fee and the value of the transaction requiredBalance = _transaction.maxFeePerGas * _transaction.gasLimit + _transaction.value; } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; import "./EfficientCall.sol"; /** * @author Matter Labs * @dev Common utilities used in zkSync system contracts */ library Utils { /// @dev Bit mask of bytecode hash "isConstructor" marker bytes32 constant IS_CONSTRUCTOR_BYTECODE_HASH_BIT_MASK = 0x00ff000000000000000000000000000000000000000000000000000000000000; /// @dev Bit mask to set the "isConstructor" marker in the bytecode hash bytes32 constant SET_IS_CONSTRUCTOR_MARKER_BIT_MASK = 0x0001000000000000000000000000000000000000000000000000000000000000; function safeCastToU128(uint256 _x) internal pure returns (uint128) { require(_x <= type(uint128).max, "Overflow"); return uint128(_x); } function safeCastToU32(uint256 _x) internal pure returns (uint32) { require(_x <= type(uint32).max, "Overflow"); return uint32(_x); } function safeCastToU24(uint256 _x) internal pure returns (uint24) { require(_x <= type(uint24).max, "Overflow"); return uint24(_x); } /// @return codeLength The bytecode length in bytes function bytecodeLenInBytes(bytes32 _bytecodeHash) internal pure returns (uint256 codeLength) { codeLength = bytecodeLenInWords(_bytecodeHash) << 5; // _bytecodeHash * 32 } /// @return codeLengthInWords The bytecode length in machine words function bytecodeLenInWords(bytes32 _bytecodeHash) internal pure returns (uint256 codeLengthInWords) { unchecked { codeLengthInWords = uint256(uint8(_bytecodeHash[2])) * 256 + uint256(uint8(_bytecodeHash[3])); } } /// @notice Denotes whether bytecode hash corresponds to a contract that already constructed function isContractConstructed(bytes32 _bytecodeHash) internal pure returns (bool) { return _bytecodeHash[1] == 0x00; } /// @notice Denotes whether bytecode hash corresponds to a contract that is on constructor or has already been constructed function isContractConstructing(bytes32 _bytecodeHash) internal pure returns (bool) { return _bytecodeHash[1] == 0x01; } /// @notice Sets "isConstructor" flag to TRUE for the bytecode hash /// @param _bytecodeHash The bytecode hash for which it is needed to set the constructing flag /// @return The bytecode hash with "isConstructor" flag set to TRUE function constructingBytecodeHash(bytes32 _bytecodeHash) internal pure returns (bytes32) { // Clear the "isConstructor" marker and set it to 0x01. return constructedBytecodeHash(_bytecodeHash) | SET_IS_CONSTRUCTOR_MARKER_BIT_MASK; } /// @notice Sets "isConstructor" flag to FALSE for the bytecode hash /// @param _bytecodeHash The bytecode hash for which it is needed to set the constructing flag /// @return The bytecode hash with "isConstructor" flag set to FALSE function constructedBytecodeHash(bytes32 _bytecodeHash) internal pure returns (bytes32) { return _bytecodeHash & ~IS_CONSTRUCTOR_BYTECODE_HASH_BIT_MASK; } /// @notice Validate the bytecode format and calculate its hash. /// @param _bytecode The bytecode to hash. /// @return hashedBytecode The 32-byte hash of the bytecode. /// Note: The function reverts the execution if the bytecode has non expected format: /// - Bytecode bytes length is not a multiple of 32 /// - Bytecode bytes length is not less than 2^21 bytes (2^16 words) /// - Bytecode words length is not odd function hashL2Bytecode(bytes calldata _bytecode) internal view returns (bytes32 hashedBytecode) { // Note that the length of the bytecode must be provided in 32-byte words. require(_bytecode.length % 32 == 0, "po"); uint256 bytecodeLenInWords = _bytecode.length / 32; require(bytecodeLenInWords < 2 ** 16, "pp"); // bytecode length must be less than 2^16 words require(bytecodeLenInWords % 2 == 1, "pr"); // bytecode length in words must be odd hashedBytecode = EfficientCall.sha(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // Setting the version of the hash hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248))); // Setting the length hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transfer.selector, to, value) ); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, value) ); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require( oldAllowance >= value, "SafeERC20: decreased allowance below zero" ); uint256 newAllowance = oldAllowance - value; _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require( nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed" ); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall( data, "SafeERC20: low-level call failed" ); if (returndata.length > 0) { // Return data is optional require( abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed" ); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require( address(this).balance >= amount, "Address: insufficient balance" ); (bool success, ) = recipient.call{value: amount}(""); require( success, "Address: unable to send value, recipient may have reverted" ); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue( target, data, 0, "Address: low-level call failed" ); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue( target, data, value, "Address: low-level call with value failed" ); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require( address(this).balance >= value, "Address: insufficient balance for call" ); (bool success, bytes memory returndata) = target.call{value: value}( data ); return verifyCallResultFromTarget( target, success, returndata, errorMessage ); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall( target, data, "Address: low-level static call failed" ); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget( target, success, returndata, errorMessage ); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall( target, data, "Address: low-level delegate call failed" ); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget( target, success, returndata, errorMessage ); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.7.6; library ExcessivelySafeCall { uint256 constant LOW_28_MASK = 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff; /// @notice Use when you _really_ really _really_ don't trust the called /// contract. This prevents the called contract from causing reversion of /// the caller in as many ways as we can. /// @dev The main difference between this and a solidity low-level call is /// that we limit the number of bytes that the callee can cause to be /// copied to caller memory. This prevents stupid things like malicious /// contracts returning 10,000,000 bytes causing a local OOG when copying /// to memory. /// @param _target The address to call /// @param _gas The amount of gas to forward to the remote contract /// @param _maxCopy The maximum number of bytes of returndata to copy /// to memory. /// @param _calldata The data to send to the remote contract /// @return success and returndata, as `.call()`. Returndata is capped to /// `_maxCopy` bytes. function excessivelySafeCall( address _target, uint256 _gas, uint16 _maxCopy, bytes memory _calldata ) internal returns (bool, bytes memory) { // set up for assembly call uint256 _toCopy; bool _success; bytes memory _returnData = new bytes(_maxCopy); // dispatch message to recipient // by assembly calling "handle" function // we call via assembly to avoid memcopying a very large returndata // returned by a malicious contract assembly { _success := call( _gas, // gas _target, // recipient 0, // ether value add(_calldata, 0x20), // inloc mload(_calldata), // inlen 0, // outloc 0 // outlen ) // limit our copy to 256 bytes _toCopy := returndatasize() if gt(_toCopy, _maxCopy) { _toCopy := _maxCopy } // Store the length of the copied bytes mstore(_returnData, _toCopy) // copy the bytes from returndata[0:_toCopy] returndatacopy(add(_returnData, 0x20), 0, _toCopy) } return (_success, _returnData); } /// @notice Use when you _really_ really _really_ don't trust the called /// contract. This prevents the called contract from causing reversion of /// the caller in as many ways as we can. /// @dev The main difference between this and a solidity low-level call is /// that we limit the number of bytes that the callee can cause to be /// copied to caller memory. This prevents stupid things like malicious /// contracts returning 10,000,000 bytes causing a local OOG when copying /// to memory. /// @param _target The address to call /// @param _gas The amount of gas to forward to the remote contract /// @param _maxCopy The maximum number of bytes of returndata to copy /// to memory. /// @param _calldata The data to send to the remote contract /// @return success and returndata, as `.call()`. Returndata is capped to /// `_maxCopy` bytes. function excessivelySafeStaticCall( address _target, uint256 _gas, uint16 _maxCopy, bytes memory _calldata ) internal view returns (bool, bytes memory) { // set up for assembly call uint256 _toCopy; bool _success; bytes memory _returnData = new bytes(_maxCopy); // dispatch message to recipient // by assembly calling "handle" function // we call via assembly to avoid memcopying a very large returndata // returned by a malicious contract assembly { _success := staticcall( _gas, // gas _target, // recipient add(_calldata, 0x20), // inloc mload(_calldata), // inlen 0, // outloc 0 // outlen ) // limit our copy to 256 bytes _toCopy := returndatasize() if gt(_toCopy, _maxCopy) { _toCopy := _maxCopy } // Store the length of the copied bytes mstore(_returnData, _toCopy) // copy the bytes from returndata[0:_toCopy] returndatacopy(add(_returnData, 0x20), 0, _toCopy) } return (_success, _returnData); } /** * @notice Swaps function selectors in encoded contract calls * @dev Allows reuse of encoded calldata for functions with identical * argument types but different names. It simply swaps out the first 4 bytes * for the new selector. This function modifies memory in place, and should * only be used with caution. * @param _newSelector The new 4-byte selector * @param _buf The encoded contract args */ function swapSelector(bytes4 _newSelector, bytes memory _buf) internal pure { require(_buf.length >= 4); uint256 _mask = LOW_28_MASK; assembly { // load the first word of let _word := mload(add(_buf, 0x20)) // mask out the top 4 bytes // /x _word := and(_word, _mask) _word := or(_newSelector, _word) mstore(add(_buf, 0x20), _word) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC1271 standard signature validation method for * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. * * _Available since v4.1._ */ interface IERC1271Upgradeable { /** * @dev Should return whether the signature provided is valid for the provided data * @param hash Hash of the data to be signed * @param signature Signature byte array associated with _data */ function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized != type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ```solidity * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSetUpgradeable { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol) pragma solidity ^0.8.0; /** * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified * proxy whose upgrades are fully controlled by the current implementation. */ interface IERC1822Proxiable { /** * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation * address. * * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this * function revert if invoked through a proxy. */ function proxiableUUID() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol) pragma solidity ^0.8.0; /** * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC. * * _Available since v4.8.3._ */ interface IERC1967 { /** * @dev Emitted when the implementation is upgraded. */ event Upgraded(address indexed implementation); /** * @dev Emitted when the admin account has changed. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Emitted when the beacon is changed. */ event BeaconUpgraded(address indexed beacon); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol) pragma solidity ^0.8.0; 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 ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (proxy/beacon/BeaconProxy.sol) pragma solidity ^0.8.0; import "./IBeacon.sol"; import "../Proxy.sol"; import "../ERC1967/ERC1967Upgrade.sol"; /** * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}. * * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't * conflict with the storage layout of the implementation behind the proxy. * * _Available since v3.4._ */ contract BeaconProxy is Proxy, ERC1967Upgrade { /** * @dev Initializes the proxy with `beacon`. * * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity * constructor. * * Requirements: * * - `beacon` must be a contract with the interface {IBeacon}. */ constructor(address beacon, bytes memory data) payable { _upgradeBeaconToAndCall(beacon, data, false); } /** * @dev Returns the current beacon address. */ function _beacon() internal view virtual returns (address) { return _getBeacon(); } /** * @dev Returns the current implementation address of the associated beacon. */ function _implementation() internal view virtual override returns (address) { return IBeacon(_getBeacon()).implementation(); } /** * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}. * * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. * * Requirements: * * - `beacon` must be a contract. * - The implementation returned by `beacon` must be a contract. */ function _setBeacon(address beacon, bytes memory data) internal virtual { _upgradeBeaconToAndCall(beacon, data, false); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol) pragma solidity ^0.8.0; /** * @dev This is the interface that {BeaconProxy} expects of its beacon. */ interface IBeacon { /** * @dev Must return an address that can be used as a delegate call target. * * {BeaconProxy} will check that this address is a contract. */ function implementation() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (proxy/beacon/UpgradeableBeacon.sol) pragma solidity ^0.8.0; import "./IBeacon.sol"; import "../../access/Ownable.sol"; import "../../utils/Address.sol"; /** * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their * implementation contract, which is where they will delegate all function calls. * * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon. */ contract UpgradeableBeacon is IBeacon, Ownable { address private _implementation; /** * @dev Emitted when the implementation returned by the beacon is changed. */ event Upgraded(address indexed implementation); /** * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the * beacon. */ constructor(address implementation_) { _setImplementation(implementation_); } /** * @dev Returns the current implementation address. */ function implementation() public view virtual override returns (address) { return _implementation; } /** * @dev Upgrades the beacon to a new implementation. * * Emits an {Upgraded} event. * * Requirements: * * - msg.sender must be the owner of the contract. * - `newImplementation` must be a contract. */ function upgradeTo(address newImplementation) public virtual onlyOwner { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Sets the implementation contract address for this beacon * * Requirements: * * - `newImplementation` must be a contract. */ function _setImplementation(address newImplementation) private { require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract"); _implementation = newImplementation; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol) pragma solidity ^0.8.0; import "../Proxy.sol"; import "./ERC1967Upgrade.sol"; /** * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an * implementation address that can be changed. This address is stored in storage in the location specified by * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the * implementation behind the proxy. */ contract ERC1967Proxy is Proxy, ERC1967Upgrade { /** * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. * * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded * function call, and allows initializing the storage of the proxy like a Solidity constructor. */ constructor(address _logic, bytes memory _data) payable { _upgradeToAndCall(_logic, _data, false); } /** * @dev Returns the current implementation address. */ function _implementation() internal view virtual override returns (address impl) { return ERC1967Upgrade._getImplementation(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol) pragma solidity ^0.8.2; import "../beacon/IBeacon.sol"; import "../../interfaces/IERC1967.sol"; import "../../interfaces/draft-IERC1822.sol"; import "../../utils/Address.sol"; import "../../utils/StorageSlot.sol"; /** * @dev This abstract contract provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. * * _Available since v4.1._ */ abstract contract ERC1967Upgrade is IERC1967 { // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Returns the current implementation address. */ function _getImplementation() internal view returns (address) { return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; } /** * @dev Stores a new address in the EIP1967 implementation slot. */ function _setImplementation(address newImplementation) private { require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; } /** * @dev Perform implementation upgrade * * Emits an {Upgraded} event. */ function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Perform implementation upgrade with additional setup call. * * Emits an {Upgraded} event. */ function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal { _upgradeTo(newImplementation); if (data.length > 0 || forceCall) { Address.functionDelegateCall(newImplementation, data); } } /** * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. * * Emits an {Upgraded} event. */ function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal { // Upgrades from old implementations will perform a rollback test. This test requires the new // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing // this special case will break upgrade paths from old UUPS implementation to new ones. if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) { _setImplementation(newImplementation); } else { try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID"); } catch { revert("ERC1967Upgrade: new implementation is not UUPS"); } _upgradeToAndCall(newImplementation, data, forceCall); } } /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Returns the current admin. */ function _getAdmin() internal view returns (address) { return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; } /** * @dev Stores a new address in the EIP1967 admin slot. */ function _setAdmin(address newAdmin) private { require(newAdmin != address(0), "ERC1967: new admin is the zero address"); StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; } /** * @dev Changes the admin of the proxy. * * Emits an {AdminChanged} event. */ function _changeAdmin(address newAdmin) internal { emit AdminChanged(_getAdmin(), newAdmin); _setAdmin(newAdmin); } /** * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. */ bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; /** * @dev Returns the current beacon. */ function _getBeacon() internal view returns (address) { return StorageSlot.getAddressSlot(_BEACON_SLOT).value; } /** * @dev Stores a new beacon in the EIP1967 beacon slot. */ function _setBeacon(address newBeacon) private { require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); require( Address.isContract(IBeacon(newBeacon).implementation()), "ERC1967: beacon implementation is not a contract" ); StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; } /** * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). * * Emits a {BeaconUpgraded} event. */ function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal { _setBeacon(newBeacon); emit BeaconUpgraded(newBeacon); if (data.length > 0 || forceCall) { Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol) pragma solidity ^0.8.0; /** * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to * be specified by overriding the virtual {_implementation} function. * * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a * different contract through the {_delegate} function. * * The success and return data of the delegated call will be returned back to the caller of the proxy. */ abstract contract Proxy { /** * @dev Delegates the current call to `implementation`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _delegate(address implementation) internal virtual { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } /** * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function * and {_fallback} should delegate. */ function _implementation() internal view virtual returns (address); /** * @dev Delegates the current call to the address returned by `_implementation()`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _fallback() internal virtual { _beforeFallback(); _delegate(_implementation()); } /** * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other * function in the contract matches the call data. */ fallback() external payable virtual { _fallback(); } /** * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data * is empty. */ receive() external payable virtual { _fallback(); } /** * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` * call, or as part of the Solidity `fallback` or `receive` functions. * * If overridden should call `super._beforeFallback()`. */ function _beforeFallback() internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/transparent/TransparentUpgradeableProxy.sol) pragma solidity ^0.8.0; import "../ERC1967/ERC1967Proxy.sol"; /** * @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy} * does not implement this interface directly, and some of its functions are implemented by an internal dispatch * mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not * include them in the ABI so this interface must be used to interact with it. */ interface ITransparentUpgradeableProxy is IERC1967 { function admin() external view returns (address); function implementation() external view returns (address); function changeAdmin(address) external; function upgradeTo(address) external; function upgradeToAndCall(address, bytes memory) external payable; } /** * @dev This contract implements a proxy that is upgradeable by an admin. * * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector * clashing], which can potentially be used in an attack, this contract uses the * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two * things that go hand in hand: * * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if * that call matches one of the admin functions exposed by the proxy itself. * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the * implementation. If the admin tries to call a function on the implementation it will fail with an error that says * "admin cannot fallback to proxy target". * * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due * to sudden errors when trying to call a function from the proxy implementation. * * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way, * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy. * * NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not * inherit from that interface, and instead the admin functions are implicitly implemented using a custom dispatch * mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to * fully implement transparency without decoding reverts caused by selector clashes between the proxy and the * implementation. * * WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the compiler * will not check that there are no selector conflicts, due to the note above. A selector clash between any new function * and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This could * render the admin operations inaccessible, which could prevent upgradeability. Transparency may also be compromised. */ contract TransparentUpgradeableProxy is ERC1967Proxy { /** * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}. */ constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) { _changeAdmin(admin_); } /** * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin. * * CAUTION: This modifier is deprecated, as it could cause issues if the modified function has arguments, and the * implementation provides a function with the same selector. */ modifier ifAdmin() { if (msg.sender == _getAdmin()) { _; } else { _fallback(); } } /** * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior */ function _fallback() internal virtual override { if (msg.sender == _getAdmin()) { bytes memory ret; bytes4 selector = msg.sig; if (selector == ITransparentUpgradeableProxy.upgradeTo.selector) { ret = _dispatchUpgradeTo(); } else if (selector == ITransparentUpgradeableProxy.upgradeToAndCall.selector) { ret = _dispatchUpgradeToAndCall(); } else if (selector == ITransparentUpgradeableProxy.changeAdmin.selector) { ret = _dispatchChangeAdmin(); } else if (selector == ITransparentUpgradeableProxy.admin.selector) { ret = _dispatchAdmin(); } else if (selector == ITransparentUpgradeableProxy.implementation.selector) { ret = _dispatchImplementation(); } else { revert("TransparentUpgradeableProxy: admin cannot fallback to proxy target"); } assembly { return(add(ret, 0x20), mload(ret)) } } else { super._fallback(); } } /** * @dev Returns the current admin. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function _dispatchAdmin() private returns (bytes memory) { _requireZeroValue(); address admin = _getAdmin(); return abi.encode(admin); } /** * @dev Returns the current implementation. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` */ function _dispatchImplementation() private returns (bytes memory) { _requireZeroValue(); address implementation = _implementation(); return abi.encode(implementation); } /** * @dev Changes the admin of the proxy. * * Emits an {AdminChanged} event. */ function _dispatchChangeAdmin() private returns (bytes memory) { _requireZeroValue(); address newAdmin = abi.decode(msg.data[4:], (address)); _changeAdmin(newAdmin); return ""; } /** * @dev Upgrade the implementation of the proxy. */ function _dispatchUpgradeTo() private returns (bytes memory) { _requireZeroValue(); address newImplementation = abi.decode(msg.data[4:], (address)); _upgradeToAndCall(newImplementation, bytes(""), false); return ""; } /** * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the * proxied contract. */ function _dispatchUpgradeToAndCall() private returns (bytes memory) { (address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes)); _upgradeToAndCall(newImplementation, data, true); return ""; } /** * @dev Returns the current admin. * * CAUTION: This function is deprecated. Use {ERC1967Upgrade-_getAdmin} instead. */ function _admin() internal view virtual returns (address) { return _getAdmin(); } /** * @dev To keep this contract fully transparent, all `ifAdmin` functions must be payable. This helper is here to * emulate some proxy functions being non-payable while still allowing value to pass through. */ function _requireZeroValue() private { require(msg.value == 0); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev _Available since v3.1._ */ interface IERC1155Receiver is IERC165 { /** * @dev Handles the receipt of a single ERC1155 token type. This function is * called at the end of a `safeTransferFrom` after the balance has been updated. * * NOTE: To accept the transfer, this must return * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` * (i.e. 0xf23a6e61, or its own function selector). * * @param operator The address which initiated the transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param id The ID of the token being transferred * @param value The amount of tokens being transferred * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4); /** * @dev Handles the receipt of a multiple ERC1155 token types. This function * is called at the end of a `safeBatchTransferFrom` after the balances have * been updated. * * NOTE: To accept the transfer(s), this must return * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` * (i.e. 0xbc197c81, or its own function selector). * * @param operator The address which initiated the batch transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param ids An array containing ids of each token being transferred (order and length must match values array) * @param values An array containing amounts of each token being transferred (order and length must match ids array) * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer(address from, address to, uint256 amount) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV // Deprecated in v4.8 } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) { // 32 is the length in bytes of hash, // enforced by the type signature above /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") mstore(0x1c, hash) message := keccak256(0x00, 0x3c) } } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, "\x19\x01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) data := keccak256(ptr, 0x42) } } /** * @dev Returns an Ethereum Signed Data with intended validator, created from a * `validator` and `data` according to the version 0 of EIP-191. * * See {recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x00", validator, data)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol) pragma solidity ^0.8.8; import "./ECDSA.sol"; import "../ShortStrings.sol"; import "../../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 specified in the EIP is very generic, and such a generic 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 their contracts 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. * * _Available since v3.4._ * * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment */ 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 ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); } /** * @dev See {EIP-5267}. * * _Available since v4.9._ */ function eip712Domain() public view virtual override returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ) { return ( hex"0f", // 01111 _name.toStringWithFallback(_nameFallback), _version.toStringWithFallback(_versionFallback), block.chainid, address(this), bytes32(0), new uint256[](0) ); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/introspection/ERC165Checker.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Library used to query support of an interface declared via {IERC165}. * * Note that these functions return the actual result of the query: they do not * `revert` if an interface is not supported. It is up to the caller to decide * what to do in these cases. */ library ERC165Checker { // As per the EIP-165 spec, no interface should ever match 0xffffffff bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff; /** * @dev Returns true if `account` supports the {IERC165} interface. */ function supportsERC165(address account) internal view returns (bool) { // Any contract that implements ERC165 must explicitly indicate support of // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid return supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) && !supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID); } /** * @dev Returns true if `account` supports the interface defined by * `interfaceId`. Support for {IERC165} itself is queried automatically. * * See {IERC165-supportsInterface}. */ function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) { // query support of both ERC165 as per the spec and support of _interfaceId return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId); } /** * @dev Returns a boolean array where each value corresponds to the * interfaces passed in and whether they're supported or not. This allows * you to batch check interfaces for a contract where your expectation * is that some interfaces may not be supported. * * See {IERC165-supportsInterface}. * * _Available since v3.4._ */ function getSupportedInterfaces( address account, bytes4[] memory interfaceIds ) internal view returns (bool[] memory) { // an array of booleans corresponding to interfaceIds and whether they're supported or not bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); // query support of ERC165 itself if (supportsERC165(account)) { // query support of each interface in interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]); } } return interfaceIdsSupported; } /** * @dev Returns true if `account` supports all the interfaces defined in * `interfaceIds`. Support for {IERC165} itself is queried automatically. * * Batch-querying can lead to gas savings by skipping repeated checks for * {IERC165} support. * * See {IERC165-supportsInterface}. */ function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) { // query support of ERC165 itself if (!supportsERC165(account)) { return false; } // query support of each interface in interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) { return false; } } // all interfaces supported return true; } /** * @notice Query if a contract implements an interface, does not check ERC165 support * @param account The address of the contract to query for support of an interface * @param interfaceId The interface identifier, as specified in ERC-165 * @return true if the contract at account indicates support of the interface with * identifier interfaceId, false otherwise * @dev Assumes that account contains a contract that supports ERC165, otherwise * the behavior of this method is undefined. This precondition can be checked * with {supportsERC165}. * * Some precompiled contracts will falsely indicate support for a given interface, so caution * should be exercised when using this function. * * Interface identification is specified in ERC-165. */ function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) { // prepare call bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId); // perform static call bool success; uint256 returnSize; uint256 returnValue; assembly { success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20) returnSize := returndatasize() returnValue := mload(0x00) } return success && returnSize >= 0x20 && returnValue > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @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 up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev 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^256 and mod 2^256 - 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^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) 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^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv 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. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); 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^256 / 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^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. 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^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // 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^256. Since the preconditions guarantee that the outcome is // less than 2^256, 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; } } /** * @notice 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) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 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 + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * 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 + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * 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; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 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 + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @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 { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol) pragma solidity ^0.8.8; import "./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 v4.9.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.0; /** * @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 ERC1967 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(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._ * _Available since v4.9 for `string`, `bytes`._ */ 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 v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @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), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(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) { 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] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); 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 keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ```solidity * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.4.22 <0.9.0; library console { address constant CONSOLE_ADDRESS = 0x000000000000000000636F6e736F6c652e6c6f67; function _sendLogPayloadImplementation(bytes memory payload) internal view { address consoleAddress = CONSOLE_ADDRESS; /// @solidity memory-safe-assembly assembly { pop( staticcall( gas(), consoleAddress, add(payload, 32), mload(payload), 0, 0 ) ) } } function _castToPure( function(bytes memory) internal view fnIn ) internal pure returns (function(bytes memory) pure fnOut) { assembly { fnOut := fnIn } } function _sendLogPayload(bytes memory payload) internal pure { _castToPure(_sendLogPayloadImplementation)(payload); } function log() internal pure { _sendLogPayload(abi.encodeWithSignature("log()")); } function logInt(int256 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(int256)", p0)); } function logUint(uint256 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); } function logString(string memory p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function logBool(bool p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function logAddress(address p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function logBytes(bytes memory p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); } function logBytes1(bytes1 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); } function logBytes2(bytes2 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); } function logBytes3(bytes3 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); } function logBytes4(bytes4 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); } function logBytes5(bytes5 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); } function logBytes6(bytes6 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); } function logBytes7(bytes7 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); } function logBytes8(bytes8 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); } function logBytes9(bytes9 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); } function logBytes10(bytes10 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); } function logBytes11(bytes11 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); } function logBytes12(bytes12 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); } function logBytes13(bytes13 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); } function logBytes14(bytes14 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); } function logBytes15(bytes15 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); } function logBytes16(bytes16 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); } function logBytes17(bytes17 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); } function logBytes18(bytes18 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); } function logBytes19(bytes19 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); } function logBytes20(bytes20 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); } function logBytes21(bytes21 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); } function logBytes22(bytes22 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); } function logBytes23(bytes23 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); } function logBytes24(bytes24 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); } function logBytes25(bytes25 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); } function logBytes26(bytes26 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); } function logBytes27(bytes27 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); } function logBytes28(bytes28 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); } function logBytes29(bytes29 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); } function logBytes30(bytes30 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); } function logBytes31(bytes31 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); } function logBytes32(bytes32 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); } function log(uint256 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); } function log(string memory p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function log(bool p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function log(address p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function log(uint256 p0, uint256 p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1)); } function log(uint256 p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1)); } function log(uint256 p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1)); } function log(uint256 p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1)); } function log(string memory p0, uint256 p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1)); } function log(string memory p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); } function log(string memory p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); } function log(string memory p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); } function log(bool p0, uint256 p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1)); } function log(bool p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); } function log(bool p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); } function log(bool p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); } function log(address p0, uint256 p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1)); } function log(address p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); } function log(address p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); } function log(address p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); } function log(uint256 p0, uint256 p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2)); } function log(uint256 p0, string memory p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2)); } function log(uint256 p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2)); } function log(uint256 p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2)); } function log(uint256 p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2)); } function log(uint256 p0, bool p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2)); } function log(uint256 p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2)); } function log(uint256 p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2)); } function log(uint256 p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2)); } function log(uint256 p0, address p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2)); } function log(uint256 p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2)); } function log(uint256 p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2)); } function log(uint256 p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2)); } function log(string memory p0, uint256 p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2)); } function log(string memory p0, uint256 p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2)); } function log(string memory p0, uint256 p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2)); } function log(string memory p0, uint256 p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2)); } function log(string memory p0, string memory p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2)); } function log(string memory p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); } function log(string memory p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); } function log(string memory p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); } function log(string memory p0, bool p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2)); } function log(string memory p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); } function log(string memory p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); } function log(string memory p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); } function log(string memory p0, address p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2)); } function log(string memory p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); } function log(string memory p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); } function log(string memory p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); } function log(bool p0, uint256 p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2)); } function log(bool p0, uint256 p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2)); } function log(bool p0, uint256 p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2)); } function log(bool p0, uint256 p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2)); } function log(bool p0, string memory p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2)); } function log(bool p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); } function log(bool p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); } function log(bool p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); } function log(bool p0, bool p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2)); } function log(bool p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); } function log(bool p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); } function log(bool p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); } function log(bool p0, address p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2)); } function log(bool p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); } function log(bool p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); } function log(bool p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); } function log(address p0, uint256 p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2)); } function log(address p0, uint256 p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2)); } function log(address p0, uint256 p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2)); } function log(address p0, uint256 p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2)); } function log(address p0, string memory p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2)); } function log(address p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); } function log(address p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); } function log(address p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); } function log(address p0, bool p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2)); } function log(address p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); } function log(address p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); } function log(address p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); } function log(address p0, address p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2)); } function log(address p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); } function log(address p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); } function log(address p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library to encode strings in Base64. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol) /// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <[email protected]>. library Base64 { /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// See: https://datatracker.ietf.org/doc/html/rfc4648 /// @param fileSafe Whether to replace '+' with '-' and '/' with '_'. /// @param noPadding Whether to strip away the padding. function encode(bytes memory data, bool fileSafe, bool noPadding) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let dataLength := mload(data) if dataLength { // Multiply by 4/3 rounded up. // The `shl(2, ...)` is equivalent to multiplying by 4. let encodedLength := shl(2, div(add(dataLength, 2), 3)) // Set `result` to point to the start of the free memory. result := mload(0x40) // Store the table into the scratch space. // Offsetted by -1 byte so that the `mload` will load the character. // We will rewrite the free memory pointer at `0x40` later with // the allocated size. // The magic constant 0x0670 will turn "-_" into "+/". mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef") mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670))) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) let end := add(ptr, encodedLength) let dataEnd := add(add(0x20, data), dataLength) let dataEndValue := mload(dataEnd) // Cache the value at the `dataEnd` slot. mstore(dataEnd, 0x00) // Zeroize the `dataEnd` slot to clear dirty bits. // Run over the input, 3 bytes at a time. for {} 1 {} { data := add(data, 3) // Advance 3 bytes. let input := mload(data) // Write 4 bytes. Optimized for fewer stack operations. mstore8(0, mload(and(shr(18, input), 0x3F))) mstore8(1, mload(and(shr(12, input), 0x3F))) mstore8(2, mload(and(shr(6, input), 0x3F))) mstore8(3, mload(and(input, 0x3F))) mstore(ptr, mload(0x00)) ptr := add(ptr, 4) // Advance 4 bytes. if iszero(lt(ptr, end)) { break } } mstore(dataEnd, dataEndValue) // Restore the cached value at `dataEnd`. mstore(0x40, add(end, 0x20)) // Allocate the memory. // Equivalent to `o = [0, 2, 1][dataLength % 3]`. let o := div(2, mod(dataLength, 3)) // Offset `ptr` and pad with '='. We can simply write over the end. mstore(sub(ptr, o), shl(240, 0x3d3d)) // Set `o` to zero if there is padding. o := mul(iszero(iszero(noPadding)), o) mstore(sub(ptr, o), 0) // Zeroize the slot after the string. mstore(result, sub(encodedLength, o)) // Store the length. } } } /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// Equivalent to `encode(data, false, false)`. function encode(bytes memory data) internal pure returns (string memory result) { result = encode(data, false, false); } /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// Equivalent to `encode(data, fileSafe, false)`. function encode(bytes memory data, bool fileSafe) internal pure returns (string memory result) { result = encode(data, fileSafe, false); } /// @dev Decodes base64 encoded `data`. /// /// Supports: /// - RFC 4648 (both standard and file-safe mode). /// - RFC 3501 (63: ','). /// /// Does not support: /// - Line breaks. /// /// Note: For performance reasons, /// this function will NOT revert on invalid `data` inputs. /// Outputs for invalid inputs will simply be undefined behaviour. /// It is the user's responsibility to ensure that the `data` /// is a valid base64 encoded string. function decode(string memory data) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { let dataLength := mload(data) if dataLength { let decodedLength := mul(shr(2, dataLength), 3) for {} 1 {} { // If padded. if iszero(and(dataLength, 3)) { let t := xor(mload(add(data, dataLength)), 0x3d3d) // forgefmt: disable-next-item decodedLength := sub( decodedLength, add(iszero(byte(30, t)), iszero(byte(31, t))) ) break } // If non-padded. decodedLength := add(decodedLength, sub(and(dataLength, 3), 1)) break } result := mload(0x40) // Write the length of the bytes. mstore(result, decodedLength) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) let end := add(ptr, decodedLength) // Load the table into the scratch space. // Constants are optimized for smaller bytecode with zero gas overhead. // `m` also doubles as the mask of the upper 6 bits. let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc mstore(0x5b, m) mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064) mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4) for {} 1 {} { // Read 4 bytes. data := add(data, 4) let input := mload(data) // Write 3 bytes. // forgefmt: disable-next-item mstore(ptr, or( and(m, mload(byte(28, input))), shr(6, or( and(m, mload(byte(29, input))), shr(6, or( and(m, mload(byte(30, input))), shr(6, mload(byte(31, input))) )) )) )) ptr := add(ptr, 3) if iszero(lt(ptr, end)) { break } } mstore(0x40, add(end, 0x20)) // Allocate the memory. mstore(end, 0) // Zeroize the slot after the bytes. mstore(0x60, 0) // Restore the zero slot. } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for parsing JSONs. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/JSONParserLib.sol) library JSONParserLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The input is invalid. error ParsingFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // There are 6 types of variables in JSON (excluding undefined). /// @dev For denoting that an item has not been initialized. /// A item returned from `parse` will never be of an undefined type. /// Parsing a invalid JSON string will simply revert. uint8 internal constant TYPE_UNDEFINED = 0; /// @dev Type representing an array (e.g. `[1,2,3]`). uint8 internal constant TYPE_ARRAY = 1; /// @dev Type representing an object (e.g. `{"a":"A","b":"B"}`). uint8 internal constant TYPE_OBJECT = 2; /// @dev Type representing a number (e.g. `-1.23e+21`). uint8 internal constant TYPE_NUMBER = 3; /// @dev Type representing a string (e.g. `"hello"`). uint8 internal constant TYPE_STRING = 4; /// @dev Type representing a boolean (i.e. `true` or `false`). uint8 internal constant TYPE_BOOLEAN = 5; /// @dev Type representing null (i.e. `null`). uint8 internal constant TYPE_NULL = 6; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STRUCTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev A pointer to a parsed JSON node. struct Item { // Do NOT modify the `_data` directly. uint256 _data; } // Private constants for packing `_data`. uint256 private constant _BITPOS_STRING = 32 * 7 - 8; uint256 private constant _BITPOS_KEY_LENGTH = 32 * 6 - 8; uint256 private constant _BITPOS_KEY = 32 * 5 - 8; uint256 private constant _BITPOS_VALUE_LENGTH = 32 * 4 - 8; uint256 private constant _BITPOS_VALUE = 32 * 3 - 8; uint256 private constant _BITPOS_CHILD = 32 * 2 - 8; uint256 private constant _BITPOS_SIBLING_OR_PARENT = 32 * 1 - 8; uint256 private constant _BITMASK_POINTER = 0xffffffff; uint256 private constant _BITMASK_TYPE = 7; uint256 private constant _KEY_INITED = 1 << 3; uint256 private constant _VALUE_INITED = 1 << 4; uint256 private constant _CHILDREN_INITED = 1 << 5; uint256 private constant _PARENT_IS_ARRAY = 1 << 6; uint256 private constant _PARENT_IS_OBJECT = 1 << 7; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* JSON PARSING OPERATION */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Parses the JSON string `s`, and returns the root. /// Reverts if `s` is not a valid JSON as specified in RFC 8259. /// Object items WILL simply contain all their children, inclusive of repeated keys, /// in the same order which they appear in the JSON string. /// /// Note: For efficiency, this function WILL NOT make a copy of `s`. /// The parsed tree WILL contain offsets to `s`. /// Do NOT pass in a string that WILL be modified later on. function parse(string memory s) internal pure returns (Item memory result) { /// @solidity memory-safe-assembly assembly { mstore(0x40, result) // We will use our own allocation instead. } bytes32 r = _query(_toInput(s), 255); /// @solidity memory-safe-assembly assembly { result := r } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* JSON ITEM OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // Note: // - An item is a node in the JSON tree. // - The value of a string item WILL be double-quoted, JSON encoded. // - We make a distinction between `index` and `key`. // - Items in arrays are located by `index` (uint256). // - Items in objects are located by `key` (string). // - Keys are always strings, double-quoted, JSON encoded. // // These design choices are made to balance between efficiency and ease-of-use. /// @dev Returns the string value of the item. /// This is its exact string representation in the original JSON string. /// The returned string WILL have leading and trailing whitespace trimmed. /// All inner whitespace WILL be preserved, exactly as it is in the original JSON string. /// If the item's type is string, the returned string WILL be double-quoted, JSON encoded. /// /// Note: This function lazily instantiates and caches the returned string. /// Do NOT modify the returned string. function value(Item memory item) internal pure returns (string memory result) { bytes32 r = _query(_toInput(item), 0); /// @solidity memory-safe-assembly assembly { result := r } } /// @dev Returns the index of the item in the array. /// It the item's parent is not an array, returns 0. function index(Item memory item) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { if and(mload(item), _PARENT_IS_ARRAY) { result := and(_BITMASK_POINTER, shr(_BITPOS_KEY, mload(item))) } } } /// @dev Returns the key of the item in the object. /// It the item's parent is not an object, returns an empty string. /// The returned string WILL be double-quoted, JSON encoded. /// /// Note: This function lazily instantiates and caches the returned string. /// Do NOT modify the returned string. function key(Item memory item) internal pure returns (string memory result) { if (item._data & _PARENT_IS_OBJECT != 0) { bytes32 r = _query(_toInput(item), 1); /// @solidity memory-safe-assembly assembly { result := r } } } /// @dev Returns the key of the item in the object. /// It the item is neither an array nor object, returns an empty array. /// /// Note: This function lazily instantiates and caches the returned array. /// Do NOT modify the returned array. function children(Item memory item) internal pure returns (Item[] memory result) { bytes32 r = _query(_toInput(item), 3); /// @solidity memory-safe-assembly assembly { result := r } } /// @dev Returns the number of children. /// It the item is neither an array nor object, returns zero. function size(Item memory item) internal pure returns (uint256 result) { bytes32 r = _query(_toInput(item), 3); /// @solidity memory-safe-assembly assembly { result := mload(r) } } /// @dev Returns the item at index `i` for (array). /// If `item` is not an array, the result's type WILL be undefined. /// If there is no item with the index, the result's type WILL be undefined. function at(Item memory item, uint256 i) internal pure returns (Item memory result) { /// @solidity memory-safe-assembly assembly { mstore(0x40, result) // Free the default allocation. We'll allocate manually. } bytes32 r = _query(_toInput(item), 3); /// @solidity memory-safe-assembly assembly { result := mload(add(add(r, 0x20), shl(5, i))) if iszero(and(lt(i, mload(r)), eq(and(mload(item), _BITMASK_TYPE), TYPE_ARRAY))) { result := 0x60 // Reset to the zero pointer. } } } /// @dev Returns the item at key `k` for (object). /// If `item` is not an object, the result's type WILL be undefined. /// The key MUST be double-quoted, JSON encoded. This is for efficiency reasons. /// - Correct : `item.at('"k"')`. /// - Wrong : `item.at("k")`. /// For duplicated keys, the last item with the key WILL be returned. /// If there is no item with the key, the result's type WILL be undefined. function at(Item memory item, string memory k) internal pure returns (Item memory result) { /// @solidity memory-safe-assembly assembly { mstore(0x40, result) // Free the default allocation. We'll allocate manually. result := 0x60 // Initialize to the zero pointer. } if (isObject(item)) { bytes32 kHash = keccak256(bytes(k)); Item[] memory r = children(item); // We'll just do a linear search. The alternatives are very bloated. for (uint256 i = r.length << 5; i != 0;) { /// @solidity memory-safe-assembly assembly { item := mload(add(r, i)) i := sub(i, 0x20) } if (keccak256(bytes(key(item))) != kHash) continue; result = item; break; } } } /// @dev Returns the item's type. function getType(Item memory item) internal pure returns (uint8 result) { result = uint8(item._data & _BITMASK_TYPE); } /// Note: All types are mutually exclusive. /// @dev Returns whether the item is of type undefined. function isUndefined(Item memory item) internal pure returns (bool result) { result = item._data & _BITMASK_TYPE == TYPE_UNDEFINED; } /// @dev Returns whether the item is of type array. function isArray(Item memory item) internal pure returns (bool result) { result = item._data & _BITMASK_TYPE == TYPE_ARRAY; } /// @dev Returns whether the item is of type object. function isObject(Item memory item) internal pure returns (bool result) { result = item._data & _BITMASK_TYPE == TYPE_OBJECT; } /// @dev Returns whether the item is of type number. function isNumber(Item memory item) internal pure returns (bool result) { result = item._data & _BITMASK_TYPE == TYPE_NUMBER; } /// @dev Returns whether the item is of type string. function isString(Item memory item) internal pure returns (bool result) { result = item._data & _BITMASK_TYPE == TYPE_STRING; } /// @dev Returns whether the item is of type boolean. function isBoolean(Item memory item) internal pure returns (bool result) { result = item._data & _BITMASK_TYPE == TYPE_BOOLEAN; } /// @dev Returns whether the item is of type null. function isNull(Item memory item) internal pure returns (bool result) { result = item._data & _BITMASK_TYPE == TYPE_NULL; } /// @dev Returns the item's parent. /// If the item does not have a parent, the result's type will be undefined. function parent(Item memory item) internal pure returns (Item memory result) { /// @solidity memory-safe-assembly assembly { mstore(0x40, result) // Free the default allocation. We've already allocated. result := and(shr(_BITPOS_SIBLING_OR_PARENT, mload(item)), _BITMASK_POINTER) if iszero(result) { result := 0x60 } // Reset to the zero pointer. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* UTILITY FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Parses an unsigned integer from a string (in decimal, i.e. base 10). /// Reverts if `s` is not a valid uint256 string matching the RegEx `^[0-9]+$`, /// or if the parsed number is too big for a uint256. function parseUint(string memory s) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { let n := mload(s) let preMulOverflowThres := div(not(0), 10) for { let i := 0 } 1 {} { i := add(i, 1) let digit := sub(and(mload(add(s, i)), 0xff), 48) let mulOverflowed := gt(result, preMulOverflowThres) let product := mul(10, result) result := add(product, digit) n := mul(n, iszero(or(or(mulOverflowed, lt(result, product)), gt(digit, 9)))) if iszero(lt(i, n)) { break } } if iszero(n) { mstore(0x00, 0x10182796) // `ParsingFailed()`. revert(0x1c, 0x04) } } } /// @dev Parses a signed integer from a string (in decimal, i.e. base 10). /// Reverts if `s` is not a valid int256 string matching the RegEx `^[+-]?[0-9]+$`, /// or if the parsed number cannot fit within `[-2**255 .. 2**255 - 1]`. function parseInt(string memory s) internal pure returns (int256 result) { uint256 n = bytes(s).length; uint256 sign; uint256 isNegative; /// @solidity memory-safe-assembly assembly { if n { let c := and(mload(add(s, 1)), 0xff) isNegative := eq(c, 45) if or(eq(c, 43), isNegative) { sign := c s := add(s, 1) mstore(s, sub(n, 1)) } if iszero(or(sign, lt(sub(c, 48), 10))) { s := 0x60 } } } uint256 x = parseUint(s); /// @solidity memory-safe-assembly assembly { if iszero(lt(x, add(shl(255, 1), isNegative))) { mstore(0x00, 0x10182796) // `ParsingFailed()`. revert(0x1c, 0x04) } if sign { mstore(s, sign) s := sub(s, 1) mstore(s, n) } result := xor(x, mul(xor(x, add(not(x), 1)), isNegative)) } } /// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16). /// Reverts if `s` is not a valid uint256 hex string matching the RegEx /// `^(0[xX])?[0-9a-fA-F]+$`, or if the parsed number cannot fit within `[0 .. 2**256 - 1]`. function parseUintFromHex(string memory s) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { let n := mload(s) // Skip two if starts with '0x' or '0X'. let i := shl(1, and(eq(0x3078, or(shr(240, mload(add(s, 0x20))), 0x20)), gt(n, 1))) for {} 1 {} { i := add(i, 1) let c := byte( and(0x1f, shr(and(mload(add(s, i)), 0xff), 0x3e4088843e41bac000000000000)), 0x3010a071000000b0104040208000c05090d060e0f ) n := mul(n, iszero(or(iszero(c), shr(252, result)))) result := add(shl(4, result), sub(c, 1)) if iszero(lt(i, n)) { break } } if iszero(n) { mstore(0x00, 0x10182796) // `ParsingFailed()`. revert(0x1c, 0x04) } } } /// @dev Decodes a JSON encoded string. /// The string MUST be double-quoted, JSON encoded. /// Reverts if the string is invalid. /// As you can see, it's pretty complex for a deceptively simple looking task. function decodeString(string memory s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { function fail() { mstore(0x00, 0x10182796) // `ParsingFailed()`. revert(0x1c, 0x04) } function decodeUnicodeEscapeSequence(pIn_, end_) -> _unicode, _pOut { _pOut := add(pIn_, 4) let b_ := iszero(gt(_pOut, end_)) let t_ := mload(pIn_) // Load the whole word. for { let i_ := 0 } iszero(eq(i_, 4)) { i_ := add(i_, 1) } { let c_ := sub(byte(i_, t_), 48) if iszero(and(shr(c_, 0x7e0000007e03ff), b_)) { fail() } // Not hexadecimal. c_ := sub(c_, add(mul(gt(c_, 16), 7), shl(5, gt(c_, 48)))) _unicode := add(shl(4, _unicode), c_) } } function decodeUnicodeCodePoint(pIn_, end_) -> _unicode, _pOut { _unicode, _pOut := decodeUnicodeEscapeSequence(pIn_, end_) if iszero(or(lt(_unicode, 0xd800), gt(_unicode, 0xdbff))) { let t_ := mload(_pOut) // Load the whole word. end_ := mul(end_, eq(shr(240, t_), 0x5c75)) // Fail if not starting with '\\u'. t_, _pOut := decodeUnicodeEscapeSequence(add(_pOut, 2), end_) _unicode := add(0x10000, add(shl(10, and(0x3ff, _unicode)), and(0x3ff, t_))) } } function appendCodePointAsUTF8(pIn_, c_) -> _pOut { if iszero(gt(c_, 0x7f)) { mstore8(pIn_, c_) _pOut := add(pIn_, 1) leave } mstore8(0x1f, c_) mstore8(0x1e, shr(6, c_)) if iszero(gt(c_, 0x7ff)) { mstore(pIn_, shl(240, or(0xc080, and(0x1f3f, mload(0x00))))) _pOut := add(pIn_, 2) leave } mstore8(0x1d, shr(12, c_)) if iszero(gt(c_, 0xffff)) { mstore(pIn_, shl(232, or(0xe08080, and(0x0f3f3f, mload(0x00))))) _pOut := add(pIn_, 3) leave } mstore8(0x1c, shr(18, c_)) mstore(pIn_, shl(224, or(0xf0808080, and(0x073f3f3f, mload(0x00))))) _pOut := add(pIn_, shl(2, lt(c_, 0x110000))) } function chr(p_) -> _c { _c := byte(0, mload(p_)) } let n := mload(s) let end := add(add(s, n), 0x1f) if iszero(and(gt(n, 1), eq(0x2222, or(and(0xff00, mload(add(s, 2))), chr(end))))) { fail() // Fail if not double-quoted. } let out := add(mload(0x40), 0x20) for { let curr := add(s, 0x21) } iszero(eq(curr, end)) {} { let c := chr(curr) curr := add(curr, 1) // Not '\\'. if iszero(eq(c, 92)) { // Not '"'. if iszero(eq(c, 34)) { mstore8(out, c) out := add(out, 1) continue } curr := end } if iszero(eq(curr, end)) { let escape := chr(curr) curr := add(curr, 1) // '"', '/', '\\'. if and(shr(escape, 0x100000000000800400000000), 1) { mstore8(out, escape) out := add(out, 1) continue } // 'u'. if eq(escape, 117) { escape, curr := decodeUnicodeCodePoint(curr, end) out := appendCodePointAsUTF8(out, escape) continue } // `{'b':'\b', 'f':'\f', 'n':'\n', 'r':'\r', 't':'\t'}`. escape := byte(sub(escape, 85), 0x080000000c000000000000000a0000000d0009) if escape { mstore8(out, escape) out := add(out, 1) continue } } fail() break } mstore(out, 0) // Zeroize the last slot. result := mload(0x40) mstore(result, sub(out, add(result, 0x20))) // Store the length. mstore(0x40, add(out, 0x20)) // Allocate the memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PRIVATE HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Performs a query on the input with the given mode. function _query(bytes32 input, uint256 mode) private pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { function fail() { mstore(0x00, 0x10182796) // `ParsingFailed()`. revert(0x1c, 0x04) } function chr(p_) -> _c { _c := byte(0, mload(p_)) } function skipWhitespace(pIn_, end_) -> _pOut { for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } { if iszero(and(shr(chr(_pOut), 0x100002600), 1)) { leave } // Not in ' \n\r\t'. } } function setP(packed_, bitpos_, p_) -> _packed { // Perform an out-of-gas revert if `p_` exceeds `_BITMASK_POINTER`. returndatacopy(returndatasize(), returndatasize(), gt(p_, _BITMASK_POINTER)) _packed := or(and(not(shl(bitpos_, _BITMASK_POINTER)), packed_), shl(bitpos_, p_)) } function getP(packed_, bitpos_) -> _p { _p := and(_BITMASK_POINTER, shr(bitpos_, packed_)) } function mallocItem(s_, packed_, pStart_, pCurr_, type_) -> _item { _item := mload(0x40) // forgefmt: disable-next-item packed_ := setP(setP(packed_, _BITPOS_VALUE, sub(pStart_, add(s_, 0x20))), _BITPOS_VALUE_LENGTH, sub(pCurr_, pStart_)) mstore(_item, or(packed_, type_)) mstore(0x40, add(_item, 0x20)) // Allocate memory. } function parseValue(s_, sibling_, pIn_, end_) -> _item, _pOut { let packed_ := setP(mload(0x00), _BITPOS_SIBLING_OR_PARENT, sibling_) _pOut := skipWhitespace(pIn_, end_) if iszero(lt(_pOut, end_)) { leave } for { let c_ := chr(_pOut) } 1 {} { // If starts with '"'. if eq(c_, 34) { let pStart_ := _pOut _pOut := parseStringSub(s_, packed_, _pOut, end_) _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_STRING) break } // If starts with '['. if eq(c_, 91) { _item, _pOut := parseArray(s_, packed_, _pOut, end_) break } // If starts with '{'. if eq(c_, 123) { _item, _pOut := parseObject(s_, packed_, _pOut, end_) break } // If starts with any in '0123456789-'. if and(shr(c_, shl(45, 0x1ff9)), 1) { _item, _pOut := parseNumber(s_, packed_, _pOut, end_) break } if iszero(gt(add(_pOut, 4), end_)) { let pStart_ := _pOut let w_ := shr(224, mload(_pOut)) // 'true' in hex format. if eq(w_, 0x74727565) { _pOut := add(_pOut, 4) _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN) break } // 'null' in hex format. if eq(w_, 0x6e756c6c) { _pOut := add(_pOut, 4) _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_NULL) break } } if iszero(gt(add(_pOut, 5), end_)) { let pStart_ := _pOut let w_ := shr(216, mload(_pOut)) // 'false' in hex format. if eq(w_, 0x66616c7365) { _pOut := add(_pOut, 5) _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN) break } } fail() break } _pOut := skipWhitespace(_pOut, end_) } function parseArray(s_, packed_, pIn_, end_) -> _item, _pOut { let j_ := 0 for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } { if iszero(lt(_pOut, end_)) { fail() } if iszero(_item) { _pOut := skipWhitespace(_pOut, end_) if eq(chr(_pOut), 93) { break } // ']'. } _item, _pOut := parseValue(s_, _item, _pOut, end_) if _item { // forgefmt: disable-next-item mstore(_item, setP(or(_PARENT_IS_ARRAY, mload(_item)), _BITPOS_KEY, j_)) j_ := add(j_, 1) let c_ := chr(_pOut) if eq(c_, 93) { break } // ']'. if eq(c_, 44) { continue } // ','. } _pOut := end_ } _pOut := add(_pOut, 1) packed_ := setP(packed_, _BITPOS_CHILD, _item) _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_ARRAY) } function parseObject(s_, packed_, pIn_, end_) -> _item, _pOut { for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } { if iszero(lt(_pOut, end_)) { fail() } if iszero(_item) { _pOut := skipWhitespace(_pOut, end_) if eq(chr(_pOut), 125) { break } // '}'. } _pOut := skipWhitespace(_pOut, end_) let pKeyStart_ := _pOut let pKeyEnd_ := parseStringSub(s_, _item, _pOut, end_) _pOut := skipWhitespace(pKeyEnd_, end_) // If ':'. if eq(chr(_pOut), 58) { _item, _pOut := parseValue(s_, _item, add(_pOut, 1), end_) if _item { // forgefmt: disable-next-item mstore(_item, setP(setP(or(_PARENT_IS_OBJECT, mload(_item)), _BITPOS_KEY_LENGTH, sub(pKeyEnd_, pKeyStart_)), _BITPOS_KEY, sub(pKeyStart_, add(s_, 0x20)))) let c_ := chr(_pOut) if eq(c_, 125) { break } // '}'. if eq(c_, 44) { continue } // ','. } } _pOut := end_ } _pOut := add(_pOut, 1) packed_ := setP(packed_, _BITPOS_CHILD, _item) _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_OBJECT) } function checkStringU(p_, o_) { // If not in '0123456789abcdefABCDEF', revert. if iszero(and(shr(sub(chr(add(p_, o_)), 48), 0x7e0000007e03ff), 1)) { fail() } if iszero(eq(o_, 5)) { checkStringU(p_, add(o_, 1)) } } function parseStringSub(s_, packed_, pIn_, end_) -> _pOut { if iszero(lt(pIn_, end_)) { fail() } for { _pOut := add(pIn_, 1) } 1 {} { let c_ := chr(_pOut) if eq(c_, 34) { break } // '"'. // Not '\'. if iszero(eq(c_, 92)) { _pOut := add(_pOut, 1) continue } c_ := chr(add(_pOut, 1)) // '"', '\', '//', 'b', 'f', 'n', 'r', 't'. if and(shr(sub(c_, 34), 0x510110400000000002001), 1) { _pOut := add(_pOut, 2) continue } // 'u'. if eq(c_, 117) { checkStringU(_pOut, 2) _pOut := add(_pOut, 6) continue } _pOut := end_ break } if iszero(lt(_pOut, end_)) { fail() } _pOut := add(_pOut, 1) } function skip0To9s(pIn_, end_, atLeastOne_) -> _pOut { for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } { if iszero(lt(sub(chr(_pOut), 48), 10)) { break } // Not '0'..'9'. } if and(atLeastOne_, eq(pIn_, _pOut)) { fail() } } function parseNumber(s_, packed_, pIn_, end_) -> _item, _pOut { _pOut := pIn_ if eq(chr(_pOut), 45) { _pOut := add(_pOut, 1) } // '-'. if iszero(lt(sub(chr(_pOut), 48), 10)) { fail() } // Not '0'..'9'. let c_ := chr(_pOut) _pOut := add(_pOut, 1) if iszero(eq(c_, 48)) { _pOut := skip0To9s(_pOut, end_, 0) } // Not '0'. if eq(chr(_pOut), 46) { _pOut := skip0To9s(add(_pOut, 1), end_, 1) } // '.'. let t_ := mload(_pOut) // 'E', 'e'. if eq(or(0x20, byte(0, t_)), 101) { // forgefmt: disable-next-item _pOut := skip0To9s(add(byte(sub(byte(1, t_), 14), 0x010001), // '+', '-'. add(_pOut, 1)), end_, 1) } _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_NUMBER) } function copyStr(s_, offset_, len_) -> _sCopy { _sCopy := mload(0x40) s_ := add(s_, offset_) let w_ := not(0x1f) for { let i_ := and(add(len_, 0x1f), w_) } 1 {} { mstore(add(_sCopy, i_), mload(add(s_, i_))) i_ := add(i_, w_) // `sub(i_, 0x20)`. if iszero(i_) { break } } mstore(_sCopy, len_) // Copy the length. mstore(add(add(_sCopy, 0x20), len_), 0) // Zeroize the last slot. mstore(0x40, add(add(_sCopy, 0x40), len_)) // Allocate memory. } function value(item_) -> _value { let packed_ := mload(item_) _value := getP(packed_, _BITPOS_VALUE) // The offset in the string. if iszero(and(_VALUE_INITED, packed_)) { let s_ := getP(packed_, _BITPOS_STRING) _value := copyStr(s_, _value, getP(packed_, _BITPOS_VALUE_LENGTH)) packed_ := setP(packed_, _BITPOS_VALUE, _value) mstore(s_, or(_VALUE_INITED, packed_)) } } function children(item_) -> _arr { _arr := 0x60 // Initialize to the zero pointer. let packed_ := mload(item_) for {} iszero(gt(and(_BITMASK_TYPE, packed_), TYPE_OBJECT)) {} { if or(iszero(packed_), iszero(item_)) { break } if and(packed_, _CHILDREN_INITED) { _arr := getP(packed_, _BITPOS_CHILD) break } _arr := mload(0x40) let o_ := add(_arr, 0x20) for { let h_ := getP(packed_, _BITPOS_CHILD) } h_ {} { mstore(o_, h_) let q_ := mload(h_) let y_ := getP(q_, _BITPOS_SIBLING_OR_PARENT) mstore(h_, setP(q_, _BITPOS_SIBLING_OR_PARENT, item_)) h_ := y_ o_ := add(o_, 0x20) } let w_ := not(0x1f) let n_ := add(w_, sub(o_, _arr)) mstore(_arr, shr(5, n_)) mstore(0x40, o_) // Allocate memory. packed_ := setP(packed_, _BITPOS_CHILD, _arr) mstore(item_, or(_CHILDREN_INITED, packed_)) // Reverse the array. if iszero(lt(n_, 0x40)) { let lo_ := add(_arr, 0x20) let hi_ := add(_arr, n_) for {} 1 {} { let temp_ := mload(lo_) mstore(lo_, mload(hi_)) mstore(hi_, temp_) hi_ := add(hi_, w_) lo_ := add(lo_, 0x20) if iszero(lt(lo_, hi_)) { break } } } break } } function getStr(item_, bitpos_, bitposLength_, bitmaskInited_) -> _result { _result := 0x60 // Initialize to the zero pointer. let packed_ := mload(item_) if or(iszero(item_), iszero(packed_)) { leave } _result := getP(packed_, bitpos_) if iszero(and(bitmaskInited_, packed_)) { let s_ := getP(packed_, _BITPOS_STRING) _result := copyStr(s_, _result, getP(packed_, bitposLength_)) mstore(item_, or(bitmaskInited_, setP(packed_, bitpos_, _result))) } } switch mode // Get value. case 0 { result := getStr(input, _BITPOS_VALUE, _BITPOS_VALUE_LENGTH, _VALUE_INITED) } // Get key. case 1 { result := getStr(input, _BITPOS_KEY, _BITPOS_KEY_LENGTH, _KEY_INITED) } // Get children. case 3 { result := children(input) } // Parse. default { let p := add(input, 0x20) let e := add(p, mload(input)) if iszero(eq(p, e)) { let c := chr(e) mstore8(e, 34) // Place a '"' at the end to speed up parsing. // The `34 << 248` makes `mallocItem` preserve '"' at the end. mstore(0x00, setP(shl(248, 34), _BITPOS_STRING, input)) result, p := parseValue(input, 0, p, e) mstore8(e, c) // Restore the original char at the end. } if or(lt(p, e), iszero(result)) { fail() } } } } /// @dev Casts the input to a bytes32. function _toInput(string memory input) private pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { result := input } } /// @dev Casts the input to a bytes32. function _toInput(Item memory input) private pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { result := input } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for byte related operations. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBytes.sol) library LibBytes { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STRUCTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Goated bytes storage struct that totally MOGs, no cap, fr. /// Uses less gas and bytecode than Solidity's native bytes storage. It's meta af. /// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight. struct BytesStorage { bytes32 _spacer; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The constant returned when the `search` is not found in the bytes. uint256 internal constant NOT_FOUND = type(uint256).max; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* BYTE STORAGE OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sets the value of the bytes storage `$` to `s`. function set(BytesStorage storage $, bytes memory s) internal { /// @solidity memory-safe-assembly assembly { let n := mload(s) let packed := or(0xff, shl(8, n)) for { let i := 0 } 1 {} { if iszero(gt(n, 0xfe)) { i := 0x1f packed := or(n, shl(8, mload(add(s, i)))) if iszero(gt(n, i)) { break } } let o := add(s, 0x20) mstore(0x00, $.slot) for { let p := keccak256(0x00, 0x20) } 1 {} { sstore(add(p, shr(5, i)), mload(add(o, i))) i := add(i, 0x20) if iszero(lt(i, n)) { break } } break } sstore($.slot, packed) } } /// @dev Sets the value of the bytes storage `$` to `s`. function setCalldata(BytesStorage storage $, bytes calldata s) internal { /// @solidity memory-safe-assembly assembly { let packed := or(0xff, shl(8, s.length)) for { let i := 0 } 1 {} { if iszero(gt(s.length, 0xfe)) { i := 0x1f packed := or(s.length, shl(8, shr(8, calldataload(s.offset)))) if iszero(gt(s.length, i)) { break } } mstore(0x00, $.slot) for { let p := keccak256(0x00, 0x20) } 1 {} { sstore(add(p, shr(5, i)), calldataload(add(s.offset, i))) i := add(i, 0x20) if iszero(lt(i, s.length)) { break } } break } sstore($.slot, packed) } } /// @dev Sets the value of the bytes storage `$` to the empty bytes. function clear(BytesStorage storage $) internal { delete $._spacer; } /// @dev Returns whether the value stored is `$` is the empty bytes "". function isEmpty(BytesStorage storage $) internal view returns (bool) { return uint256($._spacer) & 0xff == uint256(0); } /// @dev Returns the length of the value stored in `$`. function length(BytesStorage storage $) internal view returns (uint256 result) { result = uint256($._spacer); /// @solidity memory-safe-assembly assembly { let n := and(0xff, result) result := or(mul(shr(8, result), eq(0xff, n)), mul(n, iszero(eq(0xff, n)))) } } /// @dev Returns the value stored in `$`. function get(BytesStorage storage $) internal view returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let o := add(result, 0x20) let packed := sload($.slot) let n := shr(8, packed) for { let i := 0 } 1 {} { if iszero(eq(and(packed, 0xff), 0xff)) { mstore(o, packed) n := and(0xff, packed) i := 0x1f if iszero(gt(n, i)) { break } } mstore(0x00, $.slot) for { let p := keccak256(0x00, 0x20) } 1 {} { mstore(add(o, i), sload(add(p, shr(5, i)))) i := add(i, 0x20) if iszero(lt(i, n)) { break } } break } mstore(result, n) // Store the length of the memory. mstore(add(o, n), 0) // Zeroize the slot after the bytes. mstore(0x40, add(add(o, n), 0x20)) // Allocate memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* BYTES OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`. function replace(bytes memory subject, bytes memory needle, bytes memory replacement) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let needleLen := mload(needle) let replacementLen := mload(replacement) let d := sub(result, subject) // Memory difference. let i := add(subject, 0x20) // Subject bytes pointer. mstore(0x00, add(i, mload(subject))) // End of subject. if iszero(gt(needleLen, mload(subject))) { let subjectSearchEnd := add(sub(mload(0x00), needleLen), 1) let h := 0 // The hash of `needle`. if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) } let s := mload(add(needle, 0x20)) for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 {} { let t := mload(i) // Whether the first `needleLen % 32` bytes of `subject` and `needle` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(i, needleLen), h)) { mstore(add(i, d), t) i := add(i, 1) if iszero(lt(i, subjectSearchEnd)) { break } continue } } // Copy the `replacement` one word at a time. for { let j := 0 } 1 {} { mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j))) j := add(j, 0x20) if iszero(lt(j, replacementLen)) { break } } d := sub(add(d, replacementLen), needleLen) if needleLen { i := add(i, needleLen) if iszero(lt(i, subjectSearchEnd)) { break } continue } } mstore(add(i, d), t) i := add(i, 1) if iszero(lt(i, subjectSearchEnd)) { break } } } let end := mload(0x00) let n := add(sub(d, add(result, 0x20)), end) // Copy the rest of the bytes one word at a time. for {} lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) } let o := add(i, d) mstore(o, 0) // Zeroize the slot after the bytes. mstore(0x40, add(o, 0x20)) // Allocate memory. mstore(result, n) // Store the length. } } /// @dev Returns the byte index of the first location of `needle` in `subject`, /// needleing from left to right, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. function indexOf(bytes memory subject, bytes memory needle, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := not(0) // Initialize to `NOT_FOUND`. for { let subjectLen := mload(subject) } 1 {} { if iszero(mload(needle)) { result := from if iszero(gt(from, subjectLen)) { break } result := subjectLen break } let needleLen := mload(needle) let subjectStart := add(subject, 0x20) subject := add(subjectStart, from) let end := add(sub(add(subjectStart, subjectLen), needleLen), 1) let m := shl(3, sub(0x20, and(needleLen, 0x1f))) let s := mload(add(needle, 0x20)) if iszero(and(lt(subject, end), lt(from, subjectLen))) { break } if iszero(lt(needleLen, 0x20)) { for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} { if iszero(shr(m, xor(mload(subject), s))) { if eq(keccak256(subject, needleLen), 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 `needle` in `subject`, /// needleing from left to right. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. function indexOf(bytes memory subject, bytes memory needle) internal pure returns (uint256) { return indexOf(subject, needle, 0); } /// @dev Returns the byte index of the first location of `needle` in `subject`, /// needleing from right to left, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. function lastIndexOf(bytes memory subject, bytes memory needle, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { result := not(0) // Initialize to `NOT_FOUND`. let needleLen := mload(needle) if gt(needleLen, mload(subject)) { break } let w := result let fromMax := sub(mload(subject), needleLen) 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(needle, 0x20), needleLen) } 1 {} { if eq(keccak256(subject, needleLen), 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 `needle` in `subject`, /// needleing from right to left. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. function lastIndexOf(bytes memory subject, bytes memory needle) internal pure returns (uint256) { return lastIndexOf(subject, needle, type(uint256).max); } /// @dev Returns true if `needle` is found in `subject`, false otherwise. function contains(bytes memory subject, bytes memory needle) internal pure returns (bool) { return indexOf(subject, needle) != NOT_FOUND; } /// @dev Returns whether `subject` starts with `needle`. function startsWith(bytes memory subject, bytes memory needle) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let n := mload(needle) // Just using keccak256 directly is actually cheaper. let t := eq(keccak256(add(subject, 0x20), n), keccak256(add(needle, 0x20), n)) result := lt(gt(n, mload(subject)), t) } } /// @dev Returns whether `subject` ends with `needle`. function endsWith(bytes memory subject, bytes memory needle) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let n := mload(needle) let notInRange := gt(n, mload(subject)) // `subject + 0x20 + max(subject.length - needle.length, 0)`. let t := add(add(subject, 0x20), mul(iszero(notInRange), sub(mload(subject), n))) // Just using keccak256 directly is actually cheaper. result := gt(eq(keccak256(t, n), keccak256(add(needle, 0x20), n)), notInRange) } } /// @dev Returns `subject` repeated `times`. function repeat(bytes memory subject, uint256 times) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { let l := mload(subject) // Subject length. if iszero(or(iszero(times), iszero(l))) { result := mload(0x40) subject := add(subject, 0x20) let o := add(result, 0x20) for {} 1 {} { // Copy the `subject` one word at a time. for { let j := 0 } 1 {} { mstore(add(o, j), mload(add(subject, j))) j := add(j, 0x20) if iszero(lt(j, l)) { break } } o := add(o, l) times := sub(times, 1) if iszero(times) { break } } mstore(o, 0) // Zeroize the slot after the bytes. mstore(0x40, add(o, 0x20)) // Allocate memory. mstore(result, sub(o, add(result, 0x20))) // Store the length. } } } /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive). /// `start` and `end` are byte offsets. function slice(bytes memory subject, uint256 start, uint256 end) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { let l := mload(subject) // Subject length. if iszero(gt(l, end)) { end := l } if iszero(gt(l, start)) { start := l } if lt(start, end) { result := mload(0x40) let n := sub(end, start) let i := add(subject, start) let w := not(0x1f) // Copy the `subject` one word at a time, backwards. for { let j := and(add(n, 0x1f), w) } 1 {} { mstore(add(result, j), mload(add(i, j))) j := add(j, w) // `sub(j, 0x20)`. if iszero(j) { break } } let o := add(add(result, 0x20), n) mstore(o, 0) // Zeroize the slot after the bytes. mstore(0x40, add(o, 0x20)) // Allocate memory. mstore(result, n) // Store the length. } } } /// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes. /// `start` is a byte offset. function slice(bytes memory subject, uint256 start) internal pure returns (bytes memory result) { result = slice(subject, start, type(uint256).max); } /// @dev Reduces the size of `subject` to `n`. /// If `n` is greater than the size of `subject`, this will be a no-op. function truncate(bytes memory subject, uint256 n) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := subject mstore(mul(lt(n, mload(result)), result), n) } } /// @dev Returns a copy of `subject`, with the length reduced to `n`. /// If `n` is greater than the size of `subject`, this will be a no-op. function truncatedCalldata(bytes calldata subject, uint256 n) internal pure returns (bytes calldata result) { /// @solidity memory-safe-assembly assembly { result.offset := subject.offset result.length := xor(n, mul(xor(n, subject.length), lt(subject.length, n))) } } /// @dev Returns all the indices of `needle` in `subject`. /// The indices are byte offsets. function indicesOf(bytes memory subject, bytes memory needle) internal pure returns (uint256[] memory result) { /// @solidity memory-safe-assembly assembly { let searchLen := mload(needle) if iszero(gt(searchLen, mload(subject))) { result := mload(0x40) let i := add(subject, 0x20) let o := add(result, 0x20) let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1) let h := 0 // The hash of `needle`. if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) } let s := mload(add(needle, 0x20)) for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 {} { let t := mload(i) // Whether the first `searchLen % 32` bytes of `subject` and `needle` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(i, searchLen), h)) { i := add(i, 1) if iszero(lt(i, subjectSearchEnd)) { break } continue } } mstore(o, sub(i, add(subject, 0x20))) // Append to `result`. o := add(o, 0x20) i := add(i, searchLen) // Advance `i` by `searchLen`. if searchLen { if iszero(lt(i, subjectSearchEnd)) { break } continue } } i := add(i, 1) if iszero(lt(i, subjectSearchEnd)) { break } } mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`. // Allocate memory for result. // We allocate one more word, so this array can be recycled for {split}. mstore(0x40, add(o, 0x20)) } } } /// @dev Returns a arrays of bytess based on the `delimiter` inside of the `subject` bytes. function split(bytes memory subject, bytes memory delimiter) internal pure returns (bytes[] 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)) for { let prevIndex := 0 } 1 {} { let index := mload(indexPtr) mstore(indexPtr, 0x60) if iszero(eq(index, prevIndex)) { let element := mload(0x40) let l := sub(index, prevIndex) mstore(element, l) // Store the length of the element. // Copy the `subject` one word at a time, backwards. for { let o := and(add(l, 0x1f), w) } 1 {} { mstore(add(element, o), mload(add(add(subject, prevIndex), o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the bytes. // Allocate memory for the length and the bytes, rounded up to a multiple of 32. mstore(0x40, add(element, and(add(l, 0x3f), w))) mstore(indexPtr, element) // Store the `element` into the array. } 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 bytes of `a` and `b`. /// Cheaper than `bytes.concat()` and does not de-align the free memory pointer. function concat(bytes memory a, bytes memory b) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) let w := not(0x1f) let aLen := mload(a) // Copy `a` one word at a time, backwards. for { let o := and(add(aLen, 0x20), w) } 1 {} { mstore(add(result, o), mload(add(a, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let bLen := mload(b) let output := add(result, aLen) // Copy `b` one word at a time, backwards. for { let o := and(add(bLen, 0x20), w) } 1 {} { mstore(add(output, o), mload(add(b, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let totalLen := add(aLen, bLen) let last := add(add(result, 0x20), totalLen) mstore(last, 0) // Zeroize the slot after the bytes. mstore(result, totalLen) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate memory. } } /// @dev Returns whether `a` equals `b`. function eq(bytes memory a, bytes 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 bytes. function eqs(bytes 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 Directly returns `a` without copying. function directReturn(bytes memory a) internal pure { assembly { // Assumes that the bytes does not start from the scratch space. let retStart := sub(a, 0x20) let retUnpaddedSize := add(mload(a), 0x40) // Right pad with zeroes. Just in case the bytes is produced // by a method that doesn't zero right pad. mstore(add(retStart, retUnpaddedSize), 0) mstore(retStart, 0x20) // Store the return offset. // End the transaction, returning the bytes. return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize))) } } /// @dev Returns the word at `offset`, without any bounds checks. /// To load an address, you can use `address(bytes20(load(a, offset)))`. function load(bytes memory a, uint256 offset) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { result := mload(add(add(a, 0x20), offset)) } } /// @dev Returns the word at `offset`, without any bounds checks. /// To load an address, you can use `address(bytes20(loadCalldata(a, offset)))`. function loadCalldata(bytes calldata a, uint256 offset) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { result := calldataload(add(a.offset, offset)) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import { DEPLOYER_SYSTEM_CONTRACT } from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; import { IContractDeployer } from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IContractDeployer.sol"; import { SystemContractsCaller } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractsCaller.sol"; import { ISsoAccount } from "./interfaces/ISsoAccount.sol"; import { Errors } from "./libraries/Errors.sol"; /// @title AAFactory /// @author Matter Labs /// @custom:security-contact [email protected] /// @dev This contract is used to deploy SSO accounts as beacon proxies. contract AAFactory { // Order of Layout: State /// @dev The bytecode hash of the beacon proxy, used for deploying proxy accounts. bytes32 public immutable beaconProxyBytecodeHash; /// @dev The address of the SsoBeacon contract used for the SSO accounts' beacon proxies. address public immutable beacon; /// @dev The address of the pass key module address address public immutable passKeyModule; /// @dev The address of the session key module address address public immutable sessionKeyModule; /// @notice A mapping from unique account IDs to their corresponding deployed account addresses. mapping(bytes32 accountId => address deployedAccount) public accountMappings; // Order of Layout: Events /// @notice Emitted when a new account is successfully created. /// @param accountAddress The address of the newly created account. /// @param uniqueAccountId A unique identifier for the account. event AccountCreated(address indexed accountAddress, bytes32 uniqueAccountId); // Order of Layout: Errors error EMPTY_BEACON_BYTECODE_HASH(); error EMPTY_BEACON_ADDRESS(); error EMPTY_PASSKEY_ADDRESS(); error EMPTY_SESSIONKEY_ADDRESS(); // Order of Layout: Functions /// @notice Constructor that initializes the factory with a beacon proxy bytecode hash and implementation contract address. /// @param _beaconProxyBytecodeHash The bytecode hash of the beacon proxy. /// @param _beacon The address of the UpgradeableBeacon contract used for the SSO accounts' beacon proxies. /// @param _passKeyModule The address of the UpgradeableBeacon contract used for the SSO accounts' passkey proxies. /// @param _sessionKeyModule The address of the UpgradeableBeacon contract used for the SSO accounts' sessionkey proxies. constructor(bytes32 _beaconProxyBytecodeHash, address _beacon, address _passKeyModule, address _sessionKeyModule) { if (_beaconProxyBytecodeHash == bytes32(0)) { revert EMPTY_BEACON_BYTECODE_HASH(); } beaconProxyBytecodeHash = _beaconProxyBytecodeHash; if (_beacon == address(0)) { revert EMPTY_BEACON_ADDRESS(); } beacon = _beacon; if (_passKeyModule == address(0)) { revert EMPTY_PASSKEY_ADDRESS(); } passKeyModule = _passKeyModule; if (_sessionKeyModule == address(0)) { revert EMPTY_SESSIONKEY_ADDRESS(); } sessionKeyModule = _sessionKeyModule; } function getEncodedBeacon() external view returns (bytes memory) { return abi.encode(beacon); } /// @notice Deploys a new SSO account as a beacon proxy with the specified parameters. /// @dev Uses `create2` to deploy a proxy account, allowing for deterministic addresses based on the provided unique id. /// @param uniqueId Use to generate a unique account id and deterministic address calculation (create2 salt). /// @param initialValidators An array of initial validators for the new account. /// @param initialK1Owners An array of initial owners of the K1 key for the new account. /// @return accountAddress The address of the newly deployed SSO account. function deployProxySsoAccount( bytes32 uniqueId, bytes[] memory initialValidators, address[] calldata initialK1Owners ) public returns (address accountAddress) { bytes32 uniqueAccountId = keccak256(abi.encodePacked(uniqueId, msg.sender)); address existingAccountAddress = accountMappings[uniqueAccountId]; if (existingAccountAddress != address(0)) { revert Errors.ACCOUNT_ALREADY_EXISTS(existingAccountAddress); } bytes memory returnData = SystemContractsCaller.systemCallWithPropagatedRevert( uint32(gasleft()), address(DEPLOYER_SYSTEM_CONTRACT), uint128(0), abi.encodeCall( DEPLOYER_SYSTEM_CONTRACT.create2Account, ( uniqueAccountId, beaconProxyBytecodeHash, abi.encode(beacon), IContractDeployer.AccountAbstractionVersion.Version1 ) ) ); (accountAddress) = abi.decode(returnData, (address)); accountMappings[uniqueAccountId] = accountAddress; // Initialize the newly deployed account with validators and K1 owners. ISsoAccount(accountAddress).initialize(initialValidators, initialK1Owners); emit AccountCreated(accountAddress, uniqueAccountId); } /// @notice Deploys a new SSO account as a beacon proxy with the specified parameters. /// @notice Requires at least of the following: passkey, session key, or owner address. /// @dev Uses `deployProxySsoAccount` with saved passkey and session key modules addresses. /// @param uniqueId Use to generate a account address with the msg.sender /// @param passKey Credential id, raw R1 public key, and origin domain for WebAuthN validation /// @param sessionKey Session spec for session validation /// @param ownerKeys An array of initial owners of the K1 key for the new account. /// @return accountAddress The address of the newly deployed SSO account. function deployModularAccount( bytes32 uniqueId, bytes calldata passKey, bytes calldata sessionKey, address[] calldata ownerKeys ) external returns (address accountAddress) { if (passKey.length == 0 && sessionKey.length == 0 && ownerKeys.length == 0) { revert Errors.INVALID_ACCOUNT_KEYS(); } bytes memory passKeyData = abi.encode(passKeyModule, passKey); bytes memory sessionKeyData = abi.encode(sessionKeyModule, sessionKey); bytes[] memory initialValidators = new bytes[](2); initialValidators[0] = passKeyData; initialValidators[1] = sessionKeyData; return deployProxySsoAccount(uniqueId, initialValidators, ownerKeys); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { BOOTLOADER_FORMAL_ADDRESS } from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; import { Errors } from "../libraries/Errors.sol"; /** * @title BootloaderAuth * @notice Abstract contract that allows only calls from bootloader * @author https://getclave.io */ abstract contract BootloaderAuth { modifier onlyBootloader() { if (msg.sender != BOOTLOADER_FORMAL_ADDRESS) { revert Errors.NOT_FROM_BOOTLOADER(msg.sender); } _; } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { Errors } from "../libraries/Errors.sol"; /** * @title SelfAuth * @notice Abstract contract that allows only calls by the self contract * @author https://getclave.io */ abstract contract SelfAuth { modifier onlySelf() { if (msg.sender != address(this)) { revert Errors.NOT_FROM_SELF(msg.sender); } _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import { EfficientCall } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/EfficientCall.sol"; import { Utils } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/Utils.sol"; import { DEPLOYER_SYSTEM_CONTRACT } from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; import { Errors } from "../libraries/Errors.sol"; import { SelfAuth } from "../auth/SelfAuth.sol"; /// @dev Represents an external call data. /// @param target The address to which the call will be made. /// @param allowFailure Flag that represents whether to revert the whole batch if the call fails. /// @param value The amount of Ether (in wei) to be sent along with the call. /// @param callData The calldata to be executed on the `target` address. struct Call { address target; bool allowFailure; uint256 value; bytes callData; } /// @title SSO Account /// @author Matter Labs /// @custom:security-contact [email protected] /// @notice Make multiple calls from Account in a single transaction. /// @notice The implementation is inspired by Clave wallet. abstract contract BatchCaller is SelfAuth { /// @notice Emits information about a failed call. /// @param index The index of the failed call in the batch. /// @param revertData The return data of the failed call. event BatchCallFailure(uint256 indexed index, bytes revertData); /// @notice Make multiple calls, ensure success if required. /// @dev The total Ether sent across all calls must be equal to `msg.value` to maintain the invariant /// that `msg.value` + `tx.fee` is the maximum amount of Ether that can be spent on the transaction. /// @param _calls Array of Call structs, each representing an individual external call to be made. function batchCall(Call[] calldata _calls) external payable onlySelf { uint256 totalValue; uint256 len = _calls.length; for (uint256 i = 0; i < len; ++i) { totalValue += _calls[i].value; bool success; uint32 gas = Utils.safeCastToU32(gasleft()); if (_calls[i].target == address(DEPLOYER_SYSTEM_CONTRACT)) { bytes4 selector = bytes4(_calls[i].callData[:4]); // Check that called function is the deployment method, // the other deployer methods are not supposed to be called from the account. // NOTE: DefaultAccount has the same behavior. bool isSystemCall = selector == DEPLOYER_SYSTEM_CONTRACT.create.selector || selector == DEPLOYER_SYSTEM_CONTRACT.create2.selector || selector == DEPLOYER_SYSTEM_CONTRACT.createAccount.selector || selector == DEPLOYER_SYSTEM_CONTRACT.create2Account.selector; // Note, that the deployer contract can only be called with a "isSystemCall" flag. success = EfficientCall.rawCall({ _gas: gas, _address: _calls[i].target, _value: _calls[i].value, _data: _calls[i].callData, _isSystem: isSystemCall }); } else { success = EfficientCall.rawCall(gas, _calls[i].target, _calls[i].value, _calls[i].callData, false); } if (!success) { emit BatchCallFailure(i, getReturnData()); if (!_calls[i].allowFailure) { EfficientCall.propagateRevert(); } } } if (totalValue != msg.value) { revert Errors.BATCH_MSG_VALUE_MISMATCH(msg.value, totalValue); } } function getReturnData() private pure returns (bytes memory data) { assembly { let size := returndatasize() data := mload(0x40) mstore(data, size) returndatacopy(add(data, 0x20), 0, size) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import { EfficientCall } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/EfficientCall.sol"; /// @title EfficientProxy /// @author Matter Labs /// @custom:security-contact [email protected] /// @notice This contract implement ultra-efficient way for executing delegate calls. It is compatible with /// OpenZeppelin proxy implementations. abstract contract EfficientProxy { function _delegate(address implementation) internal virtual { // Use the EfficientCall library to forward calldata to the implementation contract, // instead of copying it from calldata to memory. bytes memory data = EfficientCall.delegateCall(gasleft(), implementation, msg.data); assembly { return(add(data, 0x20), mload(data)) } } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { IERC1271Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol"; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; import { SignatureDecoder } from "../libraries/SignatureDecoder.sol"; import { IModuleValidator } from "../interfaces/IModuleValidator.sol"; import { OwnerManager } from "../managers/OwnerManager.sol"; import { ValidatorManager } from "../managers/ValidatorManager.sol"; /// @title ERC1271Handler /// @author Matter Labs /// @notice Contract which provides ERC1271 signature validation /// @notice The implementation is inspired by Clave wallet. abstract contract ERC1271Handler is IERC1271Upgradeable, EIP712("Sso1271", "1.0.0"), OwnerManager, ValidatorManager { struct SsoMessage { bytes32 signedHash; } bytes32 private constant _SSO_MESSAGE_TYPEHASH = keccak256("SsoMessage(bytes32 signedHash)"); bytes4 private constant _ERC1271_MAGIC = 0x1626ba7e; /** * @dev Should return whether the signature provided is valid for the provided data. Does not run validation hooks. * @param hash bytes32 - Hash of the data that is signed * @param signature bytes calldata - K1 owner signature OR validator address concatenated to signature * @return magicValue bytes4 - Magic value if the signature is valid, 0 otherwise */ function isValidSignature(bytes32 hash, bytes memory signature) external view override returns (bytes4 magicValue) { if (signature.length == 65) { (address signer, ECDSA.RecoverError err) = ECDSA.tryRecover(hash, signature); return signer == address(0) || err != ECDSA.RecoverError.NoError || !_isK1Owner(signer) ? bytes4(0) : _ERC1271_MAGIC; } (bytes memory decodedSignature, address validator) = SignatureDecoder.decodeSignatureNoHookData(signature); bytes32 eip712Hash = _hashTypedDataV4(_ssoMessageHash(SsoMessage(hash))); bool isValid = _isModuleValidator(validator) && IModuleValidator(validator).validateSignature(eip712Hash, decodedSignature); magicValue = isValid ? _ERC1271_MAGIC : bytes4(0); } /** * @notice Returns the EIP-712 hash of the Sso message * @param ssoMessage SsoMessage calldata - The message containing signedHash * @return bytes32 - EIP712 hash of the message */ function getEip712Hash(SsoMessage calldata ssoMessage) external view returns (bytes32) { return _hashTypedDataV4(_ssoMessageHash(ssoMessage)); } /** * @notice Returns the typehash for the sso message struct * @return bytes32 - Sso message typehash */ function ssoMessageTypeHash() external pure returns (bytes32) { return _SSO_MESSAGE_TYPEHASH; } function _ssoMessageHash(SsoMessage memory ssoMessage) private pure returns (bytes32) { return keccak256(abi.encode(_SSO_MESSAGE_TYPEHASH, ssoMessage.signedHash)); } }
// SPDX-License-Identifier: MIT // A wrapper for the hardhat console so we can disable it at compile time // instead of commenting out all the console.log statements :) pragma solidity ^0.8.24; import "hardhat/console.sol"; library Logger { function logString(string memory message) internal view { if (block.chainid == 260) { console.log(message); } } function logAddress(address addressToLog) internal view { if (block.chainid == 260) { console.log(addressToLog); } } function logBytes32(bytes32 bytesToLog) internal view { if (block.chainid == 260) { console.logBytes32(bytesToLog); } } function logUint(uint256 intToLog) internal view { if (block.chainid == 260) { console.logUint(intToLog); } } function logBool(bool boolToLog) internal view { if (block.chainid == 260) { console.logBool(boolToLog); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import { ITimestampAsserter } from "../interfaces/ITimestampAsserter.sol"; import { Errors } from "../libraries/Errors.sol"; /// @title Timestamp asserter locator /// @author Matter Labs /// @custom:security-contact [email protected] /// @notice This library is used to locate the TimestampAsserter contract on different networks. /// @dev Might be removed in the future, when TimestampAsserter is deployed via create2 to the same address on all networks. library TimestampAsserterLocator { function locate() internal view returns (ITimestampAsserter) { // anvil-zksync (era-test-node) if (block.chainid == 260) { return ITimestampAsserter(address(0x0000000000000000000000000000000000808012)); } // era sepolia testnet if (block.chainid == 300) { return ITimestampAsserter(address(0xa64EC71Ee812ac62923c85cf0796aA58573c4Cf3)); } // era mainnet if (block.chainid == 324) { return ITimestampAsserter(address(0x958F70e4Fd676c9CeAaFe5c48cB78CDD08b4880d)); } revert Errors.NO_TIMESTAMP_ASSERTER(block.chainid); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; /** * @title Token callback handler * @notice Contract to handle ERC721 and ERC1155 token callbacks * @author https://getclave.io */ contract TokenCallbackHandler is IERC721Receiver, IERC1155Receiver { function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) { return IERC721Receiver.onERC721Received.selector; } function onERC1155Received( address, address, uint256, uint256, bytes calldata ) external pure override returns (bytes4) { return IERC1155Receiver.onERC1155Received.selector; } function onERC1155BatchReceived( address, address, uint256[] calldata, uint256[] calldata, bytes calldata ) external pure override returns (bytes4) { return IERC1155Receiver.onERC1155BatchReceived.selector; } /// @dev function visibility changed to public to allow overriding function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC721Receiver).interfaceId || interfaceId == type(IERC1155Receiver).interfaceId || interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT import { Errors } from "../libraries/Errors.sol"; /// @title Utility functions /// @dev Utility functions for used in ZKsync SSO contracts /// @author Matter Labs /// @custom:security-contact [email protected] library Utils { /// @dev Safely casts a uint256 to an address. /// @dev Revert if the value exceeds the maximum size for an address (160 bits). function safeCastToAddress(uint256 _value) internal pure returns (address) { if (_value > type(uint160).max) { revert Errors.ADDRESS_CAST_OVERFLOW(_value); } return address(uint160(_value)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import { IModuleValidator } from "./IModuleValidator.sol"; import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; /// @title IGuardianRecoveryValidator /// @notice Interface for managing guardian-based account recovery functionality /// @dev Extends IModuleValidator to provide recovery validation capabilities interface IGuardianRecoveryValidator is IModuleValidator { /// @notice Represents a guardian's information /// @param addr The address of the guardian /// @param isReady Whether the guardian has accepted being added as a guardian /// @param addedAt Timestamp when the guardian was added struct Guardian { address addr; bool isReady; uint64 addedAt; } /// @notice Represents a recovery request's data /// @param hashedCredentialId Hash of the credential ID used for recovery /// @param rawPublicKey The public key associated with the recovery /// @param timestamp When the recovery request was initiated struct RecoveryRequest { bytes32 hashedCredentialId; bytes32[2] rawPublicKey; uint256 timestamp; } /// @notice Error thrown when attempting to set self as guardian error GuardianCannotBeSelf(); /// @notice Error thrown when a guardian address is not found /// @param guardian The address of the guardian that was not found error GuardianNotFound(address guardian); /// @notice Error thrown when attempting to add a guardian that wasn't proposed /// @param guardian The address of the guardian that wasn't proposed error GuardianNotProposed(address guardian); /// @notice Error thrown when an account is already guarded by the specified guardian /// @param account The account address /// @param guardian The guardian address error AccountAlreadyGuardedByGuardian(address account, address guardian); /// @notice Error thrown when an account is not guarded by the specified address /// @param account The account address /// @param guardian The guardian address error AccountNotGuardedByAddress(address account, address guardian); /// @notice Error thrown when an account recovery is already in progress error AccountRecoveryInProgress(); /// @notice Error thrown when the WebAuthValidator is not enabled for the account error WebAuthValidatorNotEnabled(); /// @notice Error thrown when an invalid guardian address is provided error InvalidGuardianAddress(); /// @notice Error thrown when an invalid web auth validator address is provided error InvalidWebAuthValidatorAddress(); /// @notice Error thrown when an invalid account to guard address is provided error InvalidAccountToGuardAddress(); /// @notice Error thrown when an invalid account to recover address is provided error InvalidAccountToRecoverAddress(); /// @notice Error thrown when a non-function call transaction is detected error NonFunctionCallTransaction(); /// @notice Error thrown when an unknown hashed origin domain is provided /// @param hashedOriginDomain Hash of the unknown origin domain error UnknownHashedOriginDomain(bytes32 hashedOriginDomain); /// @notice Emitted when an origin domain is enabled for an account /// @param account The account that the origin domain is enabled for /// @param hashedOriginDomain Hash of the origin domain that is enabled event HashedOriginDomainEnabledForAccount(address indexed account, bytes32 indexed hashedOriginDomain); /// @notice Emitted when an origin domain is disabled for an account /// @param account The account that the origin domain is disabled for /// @param hashedOriginDomain Hash of the origin domain that is disabled event HashedOriginDomainDisabledForAccount(address indexed account, bytes32 indexed hashedOriginDomain); /// @notice Emitted when a recovery process is initiated /// @param account The account being recovered /// @param hashedOriginDomain Hash of the origin domain /// @param hashedCredentialId Hash of the credential ID /// @param guardian The guardian initiating the recovery event RecoveryInitiated( address indexed account, bytes32 indexed hashedOriginDomain, bytes32 indexed hashedCredentialId, address guardian ); /// @notice Emitted when a recovery process is successfully completed /// @param account The account that was recovered /// @param hashedOriginDomain Hash of the origin domain /// @param hashedCredentialId Hash of the credential ID event RecoveryFinished( address indexed account, bytes32 indexed hashedOriginDomain, bytes32 indexed hashedCredentialId ); /// @notice Emitted when a recovery process is discarded /// @param account The account for which recovery was discarded /// @param hashedOriginDomain Hash of the origin domain /// @param hashedCredentialId Hash of the credential ID event RecoveryDiscarded( address indexed account, bytes32 indexed hashedOriginDomain, bytes32 indexed hashedCredentialId ); /// @notice Emitted when a new guardian is proposed /// @param account The account proposing the guardian /// @param hashedOriginDomain Hash of the origin domain /// @param guardian The proposed guardian address event GuardianProposed(address indexed account, bytes32 indexed hashedOriginDomain, address indexed guardian); /// @notice Emitted when a guardian is successfully added /// @param account The account adding the guardian /// @param hashedOriginDomain Hash of the origin domain /// @param guardian The added guardian address event GuardianAdded(address indexed account, bytes32 indexed hashedOriginDomain, address indexed guardian); /// @notice Emitted when a guardian is removed /// @param account The account removing the guardian /// @param hashedOriginDomain Hash of the origin domain /// @param guardian The removed guardian address event GuardianRemoved(address indexed account, bytes32 indexed hashedOriginDomain, address indexed guardian); /// @notice Proposes a new guardian for an account /// @dev Takes an external account address, stores it as a pending guardian for the account /// and enables `addGuardian` to be called by the proposed guardian /// @param hashedOriginDomain Hash of the origin domain /// @param newGuardian Address of the proposed guardian function proposeGuardian(bytes32 hashedOriginDomain, address newGuardian) external; /// @notice Removes a guardian from an account /// @param hashedOriginDomain Hash of the origin domain /// @param guardianToRemove Address of the guardian to remove function removeGuardian(bytes32 hashedOriginDomain, address guardianToRemove) external; /// @notice Accepts being added as a guardian to an account /// @param hashedOriginDomain Hash of the origin domain /// @param accountToGuard Address of the account to be guarded /// @return bool Whether the guardian was successfully added function addGuardian(bytes32 hashedOriginDomain, address accountToGuard) external returns (bool); /// @notice Initiates a recovery process for an account /// @param accountToRecover Address of the account to recover /// @param hashedCredentialId Hash of the credential ID /// @param rawPublicKey Public key for the recovery /// @param hashedOriginDomain Hash of the origin domain function initRecovery( address accountToRecover, bytes32 hashedCredentialId, bytes32[2] memory rawPublicKey, bytes32 hashedOriginDomain ) external; /// @notice Discards an ongoing recovery process /// @param hashedOriginDomain Hash of the origin domain function discardRecovery(bytes32 hashedOriginDomain) external; /// @notice Retrieves all guardians for a specific address and origin domain /// @param hashedOriginDomain Hash of the origin domain /// @param addr Address to get guardians for /// @return Array of Guardian structs function guardiansFor(bytes32 hashedOriginDomain, address addr) external view returns (Guardian[] memory); /// @notice Retrieves all accounts guarded by a specific guardian and origin domain /// @param hashedOriginDomain Hash of the origin domain /// @param guardian Address of the guardian /// @return Array of account addresses function guardianOf(bytes32 hashedOriginDomain, address guardian) external view returns (address[] memory); /// @notice Retrieves pending recovery data for an account and origin domain pair /// @param hashedOriginDomain Hash of the origin domain /// @param account Address of the account /// @return RecoveryRequest struct containing recovery data function getPendingRecoveryData( bytes32 hashedOriginDomain, address account ) external view returns (RecoveryRequest memory); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { IModule } from "./IModule.sol"; /// @title Validation hook interface for native AA /// @author Initially getclave.io, updated by Matter Labs /// @notice Validation hooks trigger before each transaction, /// can be used to enforce additional restrictions on the account and/or transaction during the validation phase. interface IValidationHook is IModule, IERC165 { /// @notice Hook that triggers before each transaction during the validation phase. /// @param signedHash Hash of the transaction that is being validated. /// @param transaction Transaction that is being validated. /// @dev If reverts, the transaction is rejected from the mempool and not included in the block. function validationHook(bytes32 signedHash, Transaction calldata transaction) external; } /// @title Execution hook interface for native AA /// @author Initially getclave.io, updated by Matter Labs /// @notice Execution hooks trigger before and after each transaction, during the execution phase. interface IExecutionHook is IModule, IERC165 { /// @notice Hook that triggers before each transaction during the execution phase. /// @param transaction Transaction that is being executed. /// @return context Context that is passed to the postExecutionHook. function preExecutionHook(Transaction calldata transaction) external returns (bytes memory context); /// @notice Hook that triggers after each transaction during the execution phase. /// @param context Context that was returned by the preExecutionHook. function postExecutionHook(bytes calldata context) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; /** * @title Interface of the manager contract for hooks * @author https://getclave.io */ interface IHookManager { /** * @notice Event emitted when a hook is added * @param hook address - Address of the added hook */ event HookAdded(address indexed hook); /** * @notice Event emitted when a hook is removed * @param hook address - Address of the removed hook */ event HookRemoved(address indexed hook); /** * @notice Add a hook to the list of hooks and call its init function * @notice BEWARE: These global hooks can easily lock an account if a single hook always fails, * @notice or if there are too many hooks to execute within the gas limit. USE WITH EXTREME CAUTION! * @dev Can only be called by self * @param hook - Address of the hook * @param isValidation bool - True if the hook is a validation hook, false otherwise * @param initData bytes calldata - Data to pass to the hook's `onInstall` function */ function addHook(address hook, bool isValidation, bytes calldata initData) external; /** * @notice Remove a hook from the list of hooks * @dev Can only be called by self * @param hook address - Address of the hook to remove * @param isValidation bool - True if the hook is a validation hook, false otherwise * @param deinitData bytes calldata - Data to pass to the hook's `onUninstall` function */ function removeHook(address hook, bool isValidation, bytes calldata deinitData) external; /** * @notice Remove a hook from the list of hooks while ignoring reverts from its `onUninstall` teardown function * @dev Can only be called by self * @param hook address - Address of the hook to remove * @param isValidation bool - True if the hook is a validation hook, false otherwise * @param deinitData bytes calldata - Data to pass to the hook's `onUninstall` function */ function unlinkHook(address hook, bool isValidation, bytes calldata deinitData) external; /** * @notice Check if an address is in the list of hooks * @param addr address - Address to check * @return bool - True if the address is a hook, false otherwise */ function isHook(address addr) external view returns (bool); /** * @notice Get the list of validation or execution hooks * @param isValidation bool - True if the list of validation hooks should be returned, false otherwise * @return hookList address[] memory - List of validation or execution hooks */ function listHooks(bool isValidation) external view returns (address[] memory hookList); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /// @title Module interface /// @dev Interface for a module that can be added to SSO account (e.g. hook or validator). interface IModule { /// @dev This function is called by the smart account during installation of the module /// @param data arbitrary data that may be required on the module during `onInstall` initialization function onInstall(bytes calldata data) external; /// @dev This function is called by the smart account during uninstallation of the module /// @param data arbitrary data that may be required on the module during `onUninstall` de-initialization function onUninstall(bytes calldata data) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { IModule } from "./IModule.sol"; /** * @title Modular validator interface for native AA * @notice Validators are used for custom validation of transactions and/or message signatures */ interface IModuleValidator is IModule, IERC165 { /** * @notice Validate transaction for account * @param signedHash Hash of the transaction * @param transaction Transaction to validate * @return bool True if transaction is valid */ function validateTransaction(bytes32 signedHash, Transaction calldata transaction) external returns (bool); /** * @notice Validate signature for account (including via EIP-1271) * @dev If module is not supposed to validate signatures, it MUST return false * @param signedHash Hash of the message * @param signature Signature of the message * @return bool True if signature is valid */ function validateSignature(bytes32 signedHash, bytes calldata signature) external view returns (bool); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; /** * @title Interface of the manager contract for owners * @author https://getclave.io */ interface IOwnerManager { /** * @notice Event emitted when a k1 owner is added * @param addr address - k1 owner that has been added */ event K1OwnerAdded(address indexed addr); /** * @notice Event emitted when a k1 owner is removed * @param addr address - k1 owner that has been removed */ event K1OwnerRemoved(address indexed addr); /** * @notice Adds a k1 owner to the list of k1 owners * @dev Can only be called by self * @dev Address can not be the zero address * @param addr address - Address to add to the list of k1 owners */ function addK1Owner(address addr) external; /** * @notice Removes a k1 owner from the list of k1 owners * @dev Can only be called by self * @param addr address - Address to remove from the list of k1 owners */ function removeK1Owner(address addr) external; /** * @notice Checks if an address is in the list of k1 owners * @param addr address - Address to check * @return bool - True if the address is in the list, false otherwise */ function isK1Owner(address addr) external view returns (bool); /** * @notice Returns the list of k1 owners * @return k1OwnerList address[] memory - Array of k1 owner addresses */ function listK1Owners() external view returns (address[] memory k1OwnerList); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { IAccount } from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IAccount.sol"; import { IERC1271Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import { IHookManager } from "./IHookManager.sol"; import { IOwnerManager } from "./IOwnerManager.sol"; import { IValidatorManager } from "./IValidatorManager.sol"; /** * @title ISsoAccount * @notice Interface for the SSO contract * @dev Implementations of this interface are contracts that can be used as an SSO account (it's no longer Clave compatible) */ interface ISsoAccount is IERC1271Upgradeable, IERC721Receiver, IERC1155Receiver, IHookManager, IOwnerManager, IValidatorManager, IAccount { function initialize(bytes[] calldata initialValidators, address[] calldata initialK1Owners) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /// @title Timestamp asserter interface /// @author Matter Labs /// @custom:security-contact [email protected] /// @notice Used to assert that the current timestamp is within a given range in AA validation context. interface ITimestampAsserter { function assertTimestampInRange(uint256 start, uint256 end) external view; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; /** * @title Manager contract for validators * @author https://getclave.io */ interface IValidatorManager { /** * @notice Event emitted when a modular validator is added * @param validator address - Address of the added modular validator */ event ValidatorAdded(address indexed validator); /** * @notice Event emitted when a modular validator is removed * @param validator address - Address of the removed modular validator */ event ValidatorRemoved(address indexed validator); /** * @notice Adds a validator to the list of modular validators * @dev Can only be called by self * @param validator address - Address of the generic validator to add * @param initData - Data to pass to the validator's `onInstall` function */ function addModuleValidator(address validator, bytes memory initData) external; /** * @notice Removes a validator from the list of modular validators * @dev Can only be called by self * @param validator address - Address of the validator to remove * @param deinitData - Data to pass to the validator's `onUninstall` function */ function removeModuleValidator(address validator, bytes calldata deinitData) external; /** * @notice Removes a validator from the list of modular validators while ignoring reverts from its `onUninstall` teardown function. * @dev Can only be called by self * @param validator address - Address of the validator to remove * @param deinitData - Data to pass to the validator's `onUninstall` function */ function unlinkModuleValidator(address validator, bytes calldata deinitData) external; /** * @notice Checks if an address is in the modular validator list * @param validator address - Address of the validator to check * @return True if the address is a validator, false otherwise */ function isModuleValidator(address validator) external view returns (bool); /** * @notice Returns the list of modular validators * @return validatorList address[] memory - Array of modular validator addresses */ function listModuleValidators() external view returns (address[] memory validatorList); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; /// @title Errors /// @notice Errors used by ZKsync SSO and its components /// @author Initial version by getclave.io, modified by Matter Labs library Errors { // Account errors error INSUFFICIENT_FUNDS(uint256 required, uint256 available); error FEE_PAYMENT_FAILED(); error ACCOUNT_ALREADY_EXISTS(address account); error INVALID_ACCOUNT_KEYS(); error METHOD_NOT_IMPLEMENTED(); // ERC165 module errors error VALIDATOR_ERC165_FAIL(address validator); error HOOK_ERC165_FAIL(address hookAddress, bool isValidation); // Auth errors error NOT_FROM_BOOTLOADER(address notBootloader); error NOT_FROM_SELF(address notSelf); error NOT_FROM_INITIALIZED_ACCOUNT(address notInitialized); // Manager errors error OWNER_ALREADY_EXISTS(address owner); error OWNER_NOT_FOUND(address owner); error VALIDATOR_ALREADY_EXISTS(address validator); error VALIDATOR_NOT_FOUND(address validator); error HOOK_ALREADY_EXISTS(address hook, bool isValidation); error HOOK_NOT_FOUND(address hook, bool isValidation); // Sessions errors error UNINSTALL_WITH_OPEN_SESSIONS(uint256 openSessions); error SESSION_ZERO_SIGNER(); error SESSION_INVALID_SIGNER(address recovered, address expected); error SESSION_ALREADY_EXISTS(bytes32 sessionHash); error SESSION_UNLIMITED_FEES(); error SESSION_EXPIRES_TOO_SOON(uint256 expiresAt); error SESSION_NOT_ACTIVE(); error SESSION_LIFETIME_USAGE_EXCEEDED(uint256 lifetimeUsage, uint256 maxUsage); error SESSION_ALLOWANCE_EXCEEDED(uint256 allowance, uint256 maxAllowance, uint64 period); error SESSION_INVALID_DATA_LENGTH(uint256 actualLength, uint256 expectedMinimumLength); error SESSION_CONDITION_FAILED(bytes32 param, bytes32 refValue, uint8 condition); error SESSION_CALL_POLICY_VIOLATED(address target, bytes4 selector); error SESSION_TRANSFER_POLICY_VIOLATED(address target); error SESSION_MAX_VALUE_EXCEEDED(uint256 usedValue, uint256 maxValuePerUse); // Misc error BATCH_MSG_VALUE_MISMATCH(uint256 actualValue, uint256 expectedValue); error NO_TIMESTAMP_ASSERTER(uint256 chainId); error ADDRESS_CAST_OVERFLOW(uint256 value); error INVALID_PAYMASTER_INPUT(bytes input); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { IPaymasterFlow } from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IPaymasterFlow.sol"; import { TimestampAsserterLocator } from "../helpers/TimestampAsserterLocator.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { LibBytes } from "solady/src/utils/LibBytes.sol"; import { Errors } from "./Errors.sol"; import { Utils } from "../helpers/Utils.sol"; /// @title Session Library /// @author Matter Labs /// @notice Library for session management, used by SessionKeyValidator /// @custom:security-contact [email protected] library SessionLib { using SessionLib for SessionLib.Constraint; using SessionLib for SessionLib.UsageLimit; using LibBytes for bytes; /// @notice We do not permit opening multiple identical sessions (even after one is closed, e.g.). /// For each session key, its session status can only be changed /// from NotInitialized to Active, and from Active to Closed. enum Status { NotInitialized, Active, Closed } /// @notice This struct is used to track usage information for each session. /// Along with `status`, this is considered the session state. /// While everything else is considered the session spec, and is stored offchain. /// @dev Storage layout of this struct is unusual to conform to ERC-7562 storage access restrictions during validation. /// Each innermost mapping is always mapping(address account => ...). struct SessionStorage { mapping(address => Status) status; UsageTracker fee; // (target) => transfer value tracker mapping(address => UsageTracker) transferValue; // (target, selector) => call value tracker mapping(address => mapping(bytes4 => UsageTracker)) callValue; // (target, selector, index) => call parameter tracker // index is the constraint index in callPolicy, not the parameter index mapping(address => mapping(bytes4 => mapping(uint256 => UsageTracker))) params; } struct Constraint { Condition condition; uint64 index; bytes32 refValue; UsageLimit limit; } struct UsageTracker { // Used for LimitType.Lifetime mapping(address => uint256) lifetimeUsage; // Used for LimitType.Allowance // period => used that period mapping(uint64 => mapping(address => uint256)) allowanceUsage; } struct UsageLimit { LimitType limitType; uint256 limit; // ignored if limitType == Unlimited uint256 period; // ignored if limitType != Allowance } enum LimitType { Unlimited, Lifetime, Allowance } enum Condition { Unconstrained, Equal, Greater, Less, GreaterOrEqual, LessOrEqual, NotEqual } /// @notice This struct is provided by the account to create a session. /// It is used to define the session's policies, limits and constraints. /// Only its hash is stored onchain, and the full struct is provided with /// each transaction in calldata via `validatorData`, encoded in the signature. struct SessionSpec { address signer; uint256 expiresAt; UsageLimit feeLimit; CallSpec[] callPolicies; TransferSpec[] transferPolicies; } struct CallSpec { address target; bytes4 selector; uint256 maxValuePerUse; UsageLimit valueLimit; Constraint[] constraints; // add max data length restriction? // add max number of calls restriction? } struct TransferSpec { address target; uint256 maxValuePerUse; UsageLimit valueLimit; } struct LimitState { // this might also be limited by a constraint or `maxValuePerUse`, // which is not reflected here uint256 remaining; address target; // ignored for transfer value bytes4 selector; // ignored for transfer and call value uint256 index; } // Info about remaining session limits and its status struct SessionState { Status status; uint256 feesRemaining; LimitState[] transferValue; LimitState[] callValue; LimitState[] callParams; } /// @notice Checks if the limit is exceeded and updates the usage tracker. /// @param limit The limit to check. /// @param tracker The usage tracker to update. /// @param value The tracked value to check the limit against. /// @param period The period ID to check the limit against. Ignored if the limit is not of type Allowance. /// @dev Reverts if the limit is exceeded or the period is invalid. function checkAndUpdate( UsageLimit memory limit, UsageTracker storage tracker, uint256 value, uint64 period ) internal { if (limit.limitType == LimitType.Lifetime) { if (tracker.lifetimeUsage[msg.sender] + value > limit.limit) { revert Errors.SESSION_LIFETIME_USAGE_EXCEEDED(tracker.lifetimeUsage[msg.sender], limit.limit); } tracker.lifetimeUsage[msg.sender] += value; } else if (limit.limitType == LimitType.Allowance) { TimestampAsserterLocator.locate().assertTimestampInRange(period * limit.period, (period + 1) * limit.period - 1); if (tracker.allowanceUsage[period][msg.sender] + value > limit.limit) { revert Errors.SESSION_ALLOWANCE_EXCEEDED(tracker.allowanceUsage[period][msg.sender], limit.limit, period); } tracker.allowanceUsage[period][msg.sender] += value; } } /// @notice Checks if the constraint is met and update the usage tracker. /// @param constraint The constraint to check. /// @param tracker The usage tracker to update. /// @param data The transaction data to check the constraint against. /// @param period The period ID to check the allowances against. /// @dev Reverts if the constraint is not met. /// @dev Forwards the call to `checkAndUpdate(limit, ...)` on the limit of the constraint. function checkAndUpdate( Constraint memory constraint, UsageTracker storage tracker, bytes memory data, uint64 period ) internal { uint256 expectedLength = 4 + constraint.index * 32 + 32; if (data.length < expectedLength) { revert Errors.SESSION_INVALID_DATA_LENGTH(data.length, expectedLength); } bytes32 param = data.load(4 + constraint.index * 32); Condition condition = constraint.condition; bytes32 refValue = constraint.refValue; if ( (condition == Condition.Equal && param != refValue) || (condition == Condition.Greater && param <= refValue) || (condition == Condition.Less && param >= refValue) || (condition == Condition.GreaterOrEqual && param < refValue) || (condition == Condition.LessOrEqual && param > refValue) || (condition == Condition.NotEqual && param == refValue) ) { revert Errors.SESSION_CONDITION_FAILED(param, refValue, uint8(condition)); } constraint.limit.checkAndUpdate(tracker, uint256(param), period); } /// @notice Finds the call policy, checks if it is violated and updates the usage trackers. /// @param state The session storage to update. /// @param data The transaction data to check the call policy against. /// @param target The target address of the call. /// @param selector The 4-byte selector of the call. /// @param callPolicies The call policies to search through. /// @param periodIds The period IDs to check the allowances against. The length has to be at least `periodIdsOffset + callPolicies.length`. /// @param periodIdsOffset The offset in the `periodIds` array to start checking the constraints. /// @return The call policy that was found, reverts if not found or if the call is not allowed. function checkCallPolicy( SessionStorage storage state, bytes memory data, address target, bytes4 selector, CallSpec[] memory callPolicies, uint64[] memory periodIds, uint256 periodIdsOffset ) private returns (CallSpec memory) { CallSpec memory callPolicy; bool found = false; for (uint256 i = 0; i < callPolicies.length; i++) { if (callPolicies[i].target == target && callPolicies[i].selector == selector) { callPolicy = callPolicies[i]; found = true; break; } } if (!found) { revert Errors.SESSION_CALL_POLICY_VIOLATED(target, selector); } for (uint256 i = 0; i < callPolicy.constraints.length; i++) { callPolicy.constraints[i].checkAndUpdate(state.params[target][selector][i], data, periodIds[periodIdsOffset + i]); } return callPolicy; } /// @notice Validates the fee limit of the session and updates the tracker. /// Only performs the checks if the transaction is not using a paymaster. /// @param state The session storage to update. /// @param transaction The transaction to check the fee of. /// @param spec The session spec to check the fee limit against. /// @param periodId The period ID to check the fee limit against. Ignored if the limit is not of type Allowance. /// @dev Reverts if the fee limit is exceeded. /// @dev This is split from `validate` to prevent gas estimation failures. /// When this check was part of `validate`, gas estimation could fail due to /// fee limit being smaller than the upper bound of the gas estimation binary search. /// By splitting this check, we can now have this order of operations in `validateTransaction`: /// 1. session.validate() /// 2. ECDSA.tryRecover() /// 3. session.validateFeeLimit() /// This way, gas estimation will exit on step 2 instead of failing, but will still run through /// most of the computation needed to validate the session. function validateFeeLimit( SessionStorage storage state, Transaction calldata transaction, SessionSpec memory spec, uint64 periodId ) internal { // TODO: update fee allowance with the gasleft/refund at the end of execution // If a paymaster is paying the fee, we don't need to check the fee limit if (transaction.paymaster == 0) { uint256 fee = transaction.maxFeePerGas * transaction.gasLimit; spec.feeLimit.checkAndUpdate(state.fee, fee, periodId); } } /// @notice Validates the transaction against the session spec and updates the usage trackers. /// @param state The session storage to update. /// @param transaction The transaction to validate. /// @param spec The session spec to validate against. /// @param periodIds The period IDs to check the allowances against. /// @dev periodId is defined as block.timestamp / limit.period if limitType == Allowance, and 0 otherwise (which will be ignored). /// periodIds[0] is for fee limit (not used in this function), /// periodIds[1] is for value limit, /// peroidIds[2:2+n] are for `ERC20.approve()` constraints, where `n` is the number of constraints in the `ERC20.approve()` policy /// if an approval-based paymaster is used, 0 otherwise. /// periodIds[2+n:] are for call constraints, if there are any. /// It is required to pass them in (instead of computing via block.timestamp) since during validation /// we can only assert the range of the timestamp, but not access its value. function validate( SessionStorage storage state, Transaction calldata transaction, SessionSpec memory spec, uint64[] memory periodIds ) internal { if (state.status[msg.sender] != Status.Active) { revert Errors.SESSION_NOT_ACTIVE(); } TimestampAsserterLocator.locate().assertTimestampInRange(0, spec.expiresAt); address target = Utils.safeCastToAddress(transaction.to); // Validate paymaster input uint256 periodIdsOffset = 2; if (transaction.paymasterInput.length >= 4) { bytes4 paymasterInputSelector = bytes4(transaction.paymasterInput[:4]); // SsoAccount will automatically `approve()` a token for an approval-based paymaster in `prepareForPaymaster()` call. // We need to make sure that the session spec allows this. if (paymasterInputSelector == IPaymasterFlow.approvalBased.selector) { if (transaction.paymasterInput.length < 68) { revert Errors.INVALID_PAYMASTER_INPUT(transaction.paymasterInput); } (address token, uint256 amount, ) = abi.decode(transaction.paymasterInput[4:], (address, uint256, bytes)); address paymasterAddr = Utils.safeCastToAddress(transaction.paymaster); bytes memory data = abi.encodeCall(IERC20.approve, (paymasterAddr, amount)); // check that session allows .approve() for this token CallSpec memory approvePolicy = checkCallPolicy( state, data, token, IERC20.approve.selector, spec.callPolicies, periodIds, periodIdsOffset ); periodIdsOffset += approvePolicy.constraints.length; } } if (transaction.data.length >= 4) { bytes4 selector = bytes4(transaction.data[:4]); CallSpec memory callPolicy = checkCallPolicy( state, transaction.data, target, selector, spec.callPolicies, periodIds, periodIdsOffset ); if (transaction.value > callPolicy.maxValuePerUse) { revert Errors.SESSION_MAX_VALUE_EXCEEDED(transaction.value, callPolicy.maxValuePerUse); } callPolicy.valueLimit.checkAndUpdate(state.callValue[target][selector], transaction.value, periodIds[1]); } else { TransferSpec memory transferPolicy; bool found = false; for (uint256 i = 0; i < spec.transferPolicies.length; i++) { if (spec.transferPolicies[i].target == target) { transferPolicy = spec.transferPolicies[i]; found = true; break; } } if (!found) { revert Errors.SESSION_TRANSFER_POLICY_VIOLATED(target); } if (transaction.value > transferPolicy.maxValuePerUse) { revert Errors.SESSION_MAX_VALUE_EXCEEDED(transaction.value, transferPolicy.maxValuePerUse); } transferPolicy.valueLimit.checkAndUpdate(state.transferValue[target], transaction.value, periodIds[1]); } } /// @notice Getter for the remainder of a usage limit. /// @param limit The limit to check. /// @param tracker The corresponding usage tracker to get the usage from. /// @param account The account to get the usage for. /// @return The remaining limit. If unlimited, returns `type(uint256).max`. function remainingLimit( UsageLimit memory limit, UsageTracker storage tracker, address account ) private view returns (uint256) { if (limit.limitType == LimitType.Unlimited) { // this might be still limited by `maxValuePerUse` or a constraint return type(uint256).max; } if (limit.limitType == LimitType.Lifetime) { return limit.limit - tracker.lifetimeUsage[account]; } if (limit.limitType == LimitType.Allowance) { // this is not used during validation, so it's fine to use block.timestamp uint64 period = uint64(block.timestamp / limit.period); return limit.limit - tracker.allowanceUsage[period][account]; } } /// @notice Getter for the session state. /// @param session The session storage to get the state from. /// @param account The account to get the state for. /// @param spec The session spec to get the state for. /// @return The session state: status, remaining fee limit, transfer limits, call value and call parameter limits. function getState( SessionStorage storage session, address account, SessionSpec calldata spec ) internal view returns (SessionState memory) { uint256 totalConstraints = 0; for (uint256 i = 0; i < spec.callPolicies.length; i++) { totalConstraints += spec.callPolicies[i].constraints.length; } LimitState[] memory transferValue = new LimitState[](spec.transferPolicies.length); LimitState[] memory callValue = new LimitState[](spec.callPolicies.length); LimitState[] memory callParams = new LimitState[](totalConstraints); // there will be empty ones at the end uint256 paramLimitIndex = 0; for (uint256 i = 0; i < transferValue.length; i++) { TransferSpec memory transferSpec = spec.transferPolicies[i]; transferValue[i] = LimitState({ remaining: remainingLimit(transferSpec.valueLimit, session.transferValue[transferSpec.target], account), target: transferSpec.target, selector: bytes4(0), index: 0 }); } for (uint256 i = 0; i < callValue.length; i++) { CallSpec memory callSpec = spec.callPolicies[i]; callValue[i] = LimitState({ remaining: remainingLimit(callSpec.valueLimit, session.callValue[callSpec.target][callSpec.selector], account), target: callSpec.target, selector: callSpec.selector, index: 0 }); for (uint256 j = 0; j < callSpec.constraints.length; j++) { if (callSpec.constraints[j].limit.limitType != LimitType.Unlimited) { callParams[paramLimitIndex++] = LimitState({ remaining: remainingLimit( callSpec.constraints[j].limit, session.params[callSpec.target][callSpec.selector][j], account ), target: callSpec.target, selector: callSpec.selector, index: callSpec.constraints[j].index }); } } } // shrink array to actual size assembly { mstore(callParams, paramLimitIndex) } return SessionState({ status: session.status[account], feesRemaining: remainingLimit(spec.feeLimit, session.fee, account), transferValue: transferValue, callValue: callValue, callParams: callParams }); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; library SignatureDecoder { // Decode transaction.signature into signature, validator and hook data function decodeSignature( bytes calldata txSignature ) internal pure returns (bytes memory signature, address validator, bytes memory validatorData) { (signature, validator, validatorData) = abi.decode(txSignature, (bytes, address, bytes)); } // Decode signature into signature and validator function decodeSignatureNoHookData( bytes memory signatureAndValidator ) internal pure returns (bytes memory signature, address validator) { (signature, validator) = abi.decode(signatureAndValidator, (bytes, address)); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; library SsoStorage { // @custom:storage-location erc7201:zksync-sso.contracts.SsoStorage bytes32 private constant SSO_STORAGE_SLOT = 0x76d91304710525fd07f6da5fffdfa69dbbabd80bc84f808f10d120a9bbff4600; struct Layout { // Ownership Data EnumerableSet.AddressSet k1Owners; // Validation EnumerableSet.AddressSet moduleValidators; // Hooks EnumerableSet.AddressSet validationHooks; EnumerableSet.AddressSet executionHooks; // Storage slots reserved for future upgrades uint256[256] __RESERVED; } function layout() internal pure returns (Layout storage l) { bytes32 slot = SSO_STORAGE_SLOT; assembly { l.slot := slot } } function validators() internal view returns (EnumerableSet.AddressSet storage) { return layout().moduleValidators; } function validationHooks() internal view returns (EnumerableSet.AddressSet storage) { return layout().validationHooks; } function executionHooks() internal view returns (EnumerableSet.AddressSet storage) { return layout().executionHooks; } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { ExcessivelySafeCall } from "@nomad-xyz/excessively-safe-call/src/ExcessivelySafeCall.sol"; import { SelfAuth } from "../auth/SelfAuth.sol"; import { SsoStorage } from "../libraries/SsoStorage.sol"; import { Errors } from "../libraries/Errors.sol"; import { IExecutionHook, IValidationHook } from "../interfaces/IHook.sol"; import { IHookManager } from "../interfaces/IHookManager.sol"; import { IModule } from "../interfaces/IModule.sol"; /** * @title Manager contract for hooks * @notice Abstract contract for managing the enabled hooks of the account * @dev Hook addresses are stored in an enumerable set * @author Initially https://getclave.io, then updated by Matter Labs */ abstract contract HookManager is IHookManager, SelfAuth { using EnumerableSet for EnumerableSet.AddressSet; // Interface helper library using ERC165Checker for address; // Low level calls helper library using ExcessivelySafeCall for address; /// @inheritdoc IHookManager function addHook(address hook, bool isValidation, bytes calldata initData) external override onlySelf { _addHook(hook, isValidation, initData); } /// @inheritdoc IHookManager function removeHook(address hook, bool isValidation, bytes calldata deinitData) external override onlySelf { _removeHook(hook, isValidation); IModule(hook).onUninstall(deinitData); } /// @inheritdoc IHookManager function unlinkHook(address hook, bool isValidation, bytes calldata deinitData) external onlySelf { _removeHook(hook, isValidation); // Allow-listing slither finding as we don´t want reverting calls to prevent unlinking // slither-disable-next-line unused-return hook.excessivelySafeCall(gasleft(), 0, abi.encodeCall(IModule.onUninstall, (deinitData))); } /// @inheritdoc IHookManager function isHook(address addr) external view override returns (bool) { return _isHook(addr); } /// @inheritdoc IHookManager function listHooks(bool isValidation) external view override returns (address[] memory hookList) { if (isValidation) { hookList = SsoStorage.validationHooks().values(); } else { hookList = SsoStorage.executionHooks().values(); } } // Runs the validation hooks that are enabled by the account and returns true if none reverts function runValidationHooks(bytes32 signedHash, Transaction calldata transaction) internal returns (bool) { EnumerableSet.AddressSet storage hookList = SsoStorage.validationHooks(); uint256 totalHooks = hookList.length(); for (uint256 i = 0; i < totalHooks; i++) { bool success = _call(hookList.at(i), abi.encodeCall(IValidationHook.validationHook, (signedHash, transaction))); if (!success) { return false; } } return true; } // Runs the execution hooks that are enabled by the account before and after _executeTransaction modifier runExecutionHooks(Transaction calldata transaction) { address[] memory hookList = SsoStorage.executionHooks().values(); uint256 totalHooks = hookList.length; bytes[] memory context = new bytes[](totalHooks); for (uint256 i = 0; i < totalHooks; i++) { context[i] = IExecutionHook(hookList[i]).preExecutionHook(transaction); } _; EnumerableSet.AddressSet storage newHookList = SsoStorage.executionHooks(); for (uint256 i = 0; i < totalHooks; i++) { // Only execute hooks which are both in the old `hookList` and the `newHookList`, // and we don't want to execute hooks that were removed and/or added during this transaction. if (newHookList.contains(hookList[i])) { IExecutionHook(hookList[i]).postExecutionHook(context[i]); } } } function _addHook(address hook, bool isValidation, bytes calldata initData) private { if (!_supportsHook(hook, isValidation)) { revert Errors.HOOK_ERC165_FAIL(hook, isValidation); } // Regardless of whether or not it is a validation or an execution hook, // if the module is already installed, it cannot be installed again (even as another type). if (SsoStorage.validationHooks().contains(hook)) { revert Errors.HOOK_ALREADY_EXISTS(hook, true); } if (SsoStorage.executionHooks().contains(hook)) { revert Errors.HOOK_ALREADY_EXISTS(hook, false); } if (SsoStorage.validators().contains(hook)) { revert Errors.VALIDATOR_ALREADY_EXISTS(hook); } // No need to check the return value of .add() as we just checked that it is not already present. if (isValidation) { bool _result = SsoStorage.validationHooks().add(hook); } else { bool _result = SsoStorage.executionHooks().add(hook); } IModule(hook).onInstall(initData); emit HookAdded(hook); } function _removeHook(address hook, bool isValidation) private { if (isValidation) { if (!SsoStorage.validationHooks().remove(hook)) { revert Errors.HOOK_NOT_FOUND(hook, isValidation); } } else { if (!SsoStorage.executionHooks().remove(hook)) { revert Errors.HOOK_NOT_FOUND(hook, isValidation); } } emit HookRemoved(hook); } function _isHook(address addr) internal view returns (bool) { return SsoStorage.validationHooks().contains(addr) || SsoStorage.executionHooks().contains(addr); } function _call(address target, bytes memory data) private returns (bool success) { assembly ("memory-safe") { success := call(gas(), target, 0, add(data, 0x20), mload(data), 0, 0) } } function _supportsHook(address hook, bool isValidation) private view returns (bool) { bytes4[] memory interfaces = new bytes4[](2); interfaces[0] = isValidation ? type(IValidationHook).interfaceId : type(IExecutionHook).interfaceId; interfaces[1] = type(IModule).interfaceId; return hook.supportsAllInterfaces(interfaces); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { SsoStorage } from "../libraries/SsoStorage.sol"; import { Errors } from "../libraries/Errors.sol"; import { SelfAuth } from "../auth/SelfAuth.sol"; import { IOwnerManager } from "../interfaces/IOwnerManager.sol"; /** * @title Manager contract for owners * @notice Abstract contract for managing the owners of the account * @dev K1 Owners are secp256k1 addresses * @dev Owners are stored in an enumerable set * @author https://getclave.io */ abstract contract OwnerManager is IOwnerManager, SelfAuth { using EnumerableSet for EnumerableSet.AddressSet; /// @inheritdoc IOwnerManager function addK1Owner(address addr) external override onlySelf { _addK1Owner(addr); } /// @inheritdoc IOwnerManager function removeK1Owner(address addr) external override onlySelf { _removeK1Owner(addr); } /// @inheritdoc IOwnerManager function isK1Owner(address addr) external view override returns (bool) { return _isK1Owner(addr); } /// @inheritdoc IOwnerManager function listK1Owners() external view override returns (address[] memory k1OwnerList) { k1OwnerList = _k1Owners().values(); } // Should not be set to private as it is called from SsoAccount's initialize function _addK1Owner(address addr) internal { if (!_k1Owners().add(addr)) { revert Errors.OWNER_ALREADY_EXISTS(addr); } emit K1OwnerAdded(addr); } function _removeK1Owner(address addr) private { if (!_k1Owners().remove(addr)) { revert Errors.OWNER_NOT_FOUND(addr); } emit K1OwnerRemoved(addr); } function _isK1Owner(address addr) internal view returns (bool) { return _k1Owners().contains(addr); } function _k1Owners() private view returns (EnumerableSet.AddressSet storage k1Owners) { k1Owners = SsoStorage.layout().k1Owners; } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { ExcessivelySafeCall } from "@nomad-xyz/excessively-safe-call/src/ExcessivelySafeCall.sol"; import { SelfAuth } from "../auth/SelfAuth.sol"; import { Errors } from "../libraries/Errors.sol"; import { SsoStorage } from "../libraries/SsoStorage.sol"; import { IValidatorManager } from "../interfaces/IValidatorManager.sol"; import { IModuleValidator } from "../interfaces/IModuleValidator.sol"; import { IModule } from "../interfaces/IModule.sol"; /** * @title Manager contract for validators * @notice Abstract contract for managing the validators of the account * @dev Validators are stored in an enumerable set * @author https://getclave.io */ abstract contract ValidatorManager is IValidatorManager, SelfAuth { using EnumerableSet for EnumerableSet.AddressSet; // Interface helper library using ERC165Checker for address; // Low level calls helper library using ExcessivelySafeCall for address; ///@inheritdoc IValidatorManager function addModuleValidator(address validator, bytes calldata initData) external onlySelf { _addModuleValidator(validator, initData); } ///@inheritdoc IValidatorManager function removeModuleValidator(address validator, bytes calldata deinitData) external onlySelf { _removeModuleValidator(validator); IModule(validator).onUninstall(deinitData); } ///@inheritdoc IValidatorManager function unlinkModuleValidator(address validator, bytes calldata deinitData) external onlySelf { _removeModuleValidator(validator); // Allow-listing slither finding as we don´t want reverting calls to prevent unlinking // slither-disable-next-line unused-return validator.excessivelySafeCall(gasleft(), 0, abi.encodeCall(IModule.onUninstall, (deinitData))); } /// @inheritdoc IValidatorManager function isModuleValidator(address validator) external view override returns (bool) { return _isModuleValidator(validator); } /// @inheritdoc IValidatorManager function listModuleValidators() external view override returns (address[] memory validatorList) { validatorList = SsoStorage.validators().values(); } // Should not be set to private as it is called from SsoAccount's initialize function _addModuleValidator(address validator, bytes memory initData) internal { if (!_supportsModuleValidator(validator)) { revert Errors.VALIDATOR_ERC165_FAIL(validator); } // If the module is already installed, it cannot be installed again (even as another type). if (SsoStorage.validationHooks().contains(validator)) { revert Errors.HOOK_ALREADY_EXISTS(validator, true); } if (SsoStorage.executionHooks().contains(validator)) { revert Errors.HOOK_ALREADY_EXISTS(validator, false); } if (!SsoStorage.validators().add(validator)) { revert Errors.VALIDATOR_ALREADY_EXISTS(validator); } IModule(validator).onInstall(initData); emit ValidatorAdded(validator); } function _removeModuleValidator(address validator) private { if (!SsoStorage.validators().remove(validator)) { revert Errors.VALIDATOR_NOT_FOUND(validator); } emit ValidatorRemoved(validator); } function _isModuleValidator(address validator) internal view returns (bool) { return SsoStorage.validators().contains(validator); } function _supportsModuleValidator(address validator) private view returns (bool) { // Ah yes. Array literals are too hard for solidity to handle. bytes4[] memory interfaces = new bytes4[](2); interfaces[0] = type(IModuleValidator).interfaceId; interfaces[1] = type(IModule).interfaceId; return validator.supportsAllInterfaces(interfaces); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { ACCOUNT_VALIDATION_SUCCESS_MAGIC } from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IAccount.sol"; import { Transaction, TransactionHelper } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { EfficientCall } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/EfficientCall.sol"; import { NONCE_HOLDER_SYSTEM_CONTRACT, DEPLOYER_SYSTEM_CONTRACT } from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; import { INonceHolder } from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/INonceHolder.sol"; import { SystemContractsCaller } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractsCaller.sol"; import { Utils } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/Utils.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { HookManager } from "./managers/HookManager.sol"; import { Utils as SsoUtils } from "./helpers/Utils.sol"; import { TokenCallbackHandler } from "./helpers/TokenCallbackHandler.sol"; import { Errors } from "./libraries/Errors.sol"; import { SignatureDecoder } from "./libraries/SignatureDecoder.sol"; import { ERC1271Handler } from "./handlers/ERC1271Handler.sol"; import { BatchCaller } from "./batch/BatchCaller.sol"; import { BootloaderAuth } from "./auth/BootloaderAuth.sol"; import { ISsoAccount } from "./interfaces/ISsoAccount.sol"; import { IModuleValidator } from "./interfaces/IModuleValidator.sol"; /// @title SSO Account /// @author Matter Labs /// @custom:security-contact [email protected] /// @notice The implementation is inspired by Clave wallet. /// @notice This contract is a modular and extensible account implementation with support of /// multi-ownership, custom modules, validation/execution hooks and different signature validation formats. /// @dev Contract is expected to be used as Beacon proxy implementation. contract SsoAccount is Initializable, HookManager, ERC1271Handler, TokenCallbackHandler, BatchCaller, ISsoAccount, BootloaderAuth { // Helper library for the Transaction struct using TransactionHelper for Transaction; constructor() { _disableInitializers(); } /// @notice Initializer function that sets account initial configuration. Expected to be used in the proxy. /// @dev Sets passkey and passkey validator within account storage /// @param initialValidators An array of module validator addresses and initial validation keys /// in an ABI encoded format of `abi.encode(validatorAddr,validationKey)`. /// @param initialK1Owners An array of addresses with full control over the account. function initialize(bytes[] calldata initialValidators, address[] calldata initialK1Owners) external initializer { if (initialValidators.length == 0 && initialK1Owners.length == 0) { revert Errors.INVALID_ACCOUNT_KEYS(); } address validatorAddr; bytes memory initData; for (uint256 i = 0; i < initialValidators.length; ++i) { (validatorAddr, initData) = abi.decode(initialValidators[i], (address, bytes)); _addModuleValidator(validatorAddr, initData); } for (uint256 i = 0; i < initialK1Owners.length; ++i) { _addK1Owner(initialK1Owners[i]); } } /// @dev Account might receive/hold base tokens. receive() external payable {} /// @notice Called by the bootloader to validate that an account agrees to process the transaction /// (and potentially pay for it). /// @dev The developer should strive to preserve as many steps as possible both for valid /// and invalid transactions as this very method is also used during the gas fee estimation /// (without some of the necessary data, e.g. signature). /// @param _suggestedSignedHash The suggested hash of the transaction that is signed by the signer. /// @param _transaction The transaction data. /// @return magic The magic value that should be equal to the signature of this function. /// if the user agrees to proceed with the transaction. function validateTransaction( bytes32, bytes32 _suggestedSignedHash, Transaction calldata _transaction ) external payable override onlyBootloader returns (bytes4 magic) { // TODO: session txs have their own nonce managers, so they have to not alter this nonce _incrementNonce(_transaction.nonce); // If there is not enough balance for the transaction, the account should reject it // on the validation step to prevent paying fees for revertable transactions. uint256 requiredBalance = _transaction.totalRequiredBalance(); if (requiredBalance > address(this).balance) { revert Errors.INSUFFICIENT_FUNDS(requiredBalance, address(this).balance); } // While the suggested signed hash is usually provided, it is generally // not recommended to rely on it to be present, since in the future // there may be tx types with no suggested signed hash. bytes32 signedHash = _suggestedSignedHash == bytes32(0) ? _transaction.encodeHash() : _suggestedSignedHash; magic = _validateTransaction(signedHash, _transaction); } /// @notice Called by the bootloader to make the account execute the transaction. /// @dev The transaction is considered successful if this function does not revert /// @param _transaction The transaction data. function executeTransaction( bytes32, bytes32, Transaction calldata _transaction ) external payable override onlyBootloader runExecutionHooks(_transaction) { address to = SsoUtils.safeCastToAddress(_transaction.to); uint128 value = Utils.safeCastToU128(_transaction.value); _executeCall(to, value, _transaction.data); } /// @notice Executes a call to a given address with a specified value and calldata. /// @param _to The address to which the call is made. /// @param _value The value to send along with the call. /// @param _data The calldata to pass along with the call. function _executeCall(address _to, uint128 _value, bytes calldata _data) private { uint32 gas = Utils.safeCastToU32(gasleft()); bool success; if (_to == address(DEPLOYER_SYSTEM_CONTRACT) && _data.length >= 4) { bytes4 selector = bytes4(_data[:4]); // Check that called function is the deployment method, // the other deployer methods are not supposed to be called from the account. // NOTE: DefaultAccount has the same behavior. bool isSystemCall = selector == DEPLOYER_SYSTEM_CONTRACT.create.selector || selector == DEPLOYER_SYSTEM_CONTRACT.create2.selector || selector == DEPLOYER_SYSTEM_CONTRACT.createAccount.selector || selector == DEPLOYER_SYSTEM_CONTRACT.create2Account.selector; // Note, that the deployer contract can only be called with a "isSystemCall" flag. success = EfficientCall.rawCall({ _gas: gas, _address: _to, _value: _value, _data: _data, _isSystem: isSystemCall }); } else { success = EfficientCall.rawCall(gas, _to, _value, _data, false); } if (!success) { EfficientCall.propagateRevert(); } } /// @notice This function allows an EOA to start a transaction for the account. The main purpose of which is /// to have and entry point for escaping funds when L2 transactions are censored by the chain, and only /// forced transactions are accepted by the network. /// @dev It is not implemented yet. function executeTransactionFromOutside(Transaction calldata) external payable override { revert Errors.METHOD_NOT_IMPLEMENTED(); } /// @notice This function allows the account to pay for its own gas and used when there is no paymaster. /// @param _transaction The transaction data. /// @dev This method must send at least `tx.gasprice * tx.gasLimit` ETH to the bootloader address. function payForTransaction( bytes32, bytes32, Transaction calldata _transaction ) external payable override onlyBootloader { bool success = _transaction.payToTheBootloader(); if (!success) { revert Errors.FEE_PAYMENT_FAILED(); } } /// @notice This function is called by the system if the transaction has a paymaster /// and prepares the interaction with the paymaster. /// @param _transaction The transaction data. function prepareForPaymaster( bytes32, bytes32, Transaction calldata _transaction ) external payable override onlyBootloader { _transaction.processPaymasterInput(); } /// @dev type(ISsoAccount).interfaceId indicates SSO accounts function supportsInterface(bytes4 interfaceId) public view override(IERC165, TokenCallbackHandler) returns (bool) { return interfaceId == type(ISsoAccount).interfaceId || super.supportsInterface(interfaceId); } /// @notice Validates the provided transaction by validating signature of ECDSA k1 owner. /// or running validation hooks and signature validation in the provided validator address. /// @param _signedHash The signed hash of the transaction. /// @param _transaction The transaction data. /// @return The magic value if the validation was successful and bytes4(0) otherwise. function _validateTransaction(bytes32 _signedHash, Transaction calldata _transaction) private returns (bytes4) { // Run validation hooks bool hookSuccess = runValidationHooks(_signedHash, _transaction); if (!hookSuccess) { return bytes4(0); } if (_transaction.signature.length == 65) { (address signer, ECDSA.RecoverError err) = ECDSA.tryRecover(_signedHash, _transaction.signature); return signer == address(0) || err != ECDSA.RecoverError.NoError || !_isK1Owner(signer) ? bytes4(0) : ACCOUNT_VALIDATION_SUCCESS_MAGIC; } // Extract the signature, validator address and hook data from the _transaction.signature // the signature value is not necessary, omitting it // slither-disable-next-line unused-return (, address validator) = SignatureDecoder.decodeSignatureNoHookData(_transaction.signature); bool validationSuccess = _isModuleValidator(validator) && IModuleValidator(validator).validateTransaction(_signedHash, _transaction); if (!validationSuccess) { return bytes4(0); } return ACCOUNT_VALIDATION_SUCCESS_MAGIC; } /// @dev Increments the nonce value in Nonce Holder system contract to ensure replay attack protection. /// @dev Reverts if the Nonce Holder stores different `_nonce` value from the expected one. /// @param _expectedNonce The nonce value expected for the account to be stored in the Nonce Holder. function _incrementNonce(uint256 _expectedNonce) private { // Allow-listing slither finding as the call's success is checked+revert within the fn // slither-disable-next-line unused-return SystemContractsCaller.systemCallWithPropagatedRevert( uint32(gasleft()), address(NONCE_HOLDER_SYSTEM_CONTRACT), 0, abi.encodeCall(INonceHolder.incrementMinNonceIfEquals, (_expectedNonce)) ); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; /// @title SsoBeacon /// @author Matter Labs /// @custom:security-contact [email protected] /// @dev This beacon stores the implementation address of SsoAccount contract, /// which every SSO account delegates to. This beacon's address is immutably stored /// in AAFactory contract, as it is required for deploying new SSO accounts. contract SsoBeacon is UpgradeableBeacon { constructor(address _implementation) UpgradeableBeacon(_implementation) {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Dummy { string public constant dummy = "dummy"; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract TestERC20 is ERC20 { constructor(address mintTo) ERC20("Test ERC20", "TEST") { _mint(mintTo, 10 ** 18); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IPaymaster, ExecutionResult, PAYMASTER_VALIDATION_SUCCESS_MAGIC } from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IPaymaster.sol"; import { IPaymasterFlow } from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IPaymasterFlow.sol"; import { TransactionHelper, Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { BOOTLOADER_FORMAL_ADDRESS } from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { AAFactory } from "../AAFactory.sol"; import { SessionKeyValidator } from "../validators/SessionKeyValidator.sol"; import { GuardianRecoveryValidator } from "../validators/GuardianRecoveryValidator.sol"; import { WebAuthValidator } from "../validators/WebAuthValidator.sol"; /// @author Matter Labs /// @notice This contract does not include any validations other than using the paymaster general flow. contract ExampleAuthServerPaymaster is IPaymaster, Ownable { address public immutable AA_FACTORY_CONTRACT_ADDRESS; address public immutable SESSION_KEY_VALIDATOR_CONTRACT_ADDRESS; address public immutable ACCOUNT_RECOVERY_VALIDATOR_CONTRACT_ADDRESS; address public immutable WEB_AUTH_VALIDATOR_CONTRACT_ADDRESS; bytes4 constant DEPLOY_ACCOUNT_SELECTOR = AAFactory.deployProxySsoAccount.selector; bytes4 constant SESSION_CREATE_SELECTOR = SessionKeyValidator.createSession.selector; bytes4 constant SESSION_REVOKE_KEY_SELECTOR = SessionKeyValidator.revokeKey.selector; bytes4 constant SESSION_REVOKE_KEYS_SELECTOR = SessionKeyValidator.revokeKeys.selector; bytes4 constant GUARDIAN_RECOVERY_ADD_GUARDIAN_SELECTOR = GuardianRecoveryValidator.addGuardian.selector; bytes4 constant GUARDIAN_RECOVERY_PROPOSE_GUARDIAN_SELECTOR = GuardianRecoveryValidator.proposeGuardian.selector; bytes4 constant GUARDIAN_RECOVERY_DISCARD_RECOVERY_SELECTOR = GuardianRecoveryValidator.discardRecovery.selector; bytes4 constant GUARDIAN_RECOVERY_REMOVE_GUARDIAN_SELECTOR = GuardianRecoveryValidator.removeGuardian.selector; bytes4 constant GUARDIAN_RECOVERY_INIT_RECOVERY_SELECTOR = GuardianRecoveryValidator.initRecovery.selector; bytes4 constant WEB_AUTH_VALIDATOR_REMOVE_KEY_SELECTOR = WebAuthValidator.removeValidationKey.selector; bytes4 constant WEB_AUTH_VALIDATOR_ADD_KEY_SELECTOR = WebAuthValidator.addValidationKey.selector; modifier onlyBootloader() { require(msg.sender == BOOTLOADER_FORMAL_ADDRESS, "Only bootloader can call this method"); // Continue execution if called from the bootloader. _; } constructor( address aaFactoryAddress, address sessionKeyValidatorAddress, address accountRecoveryValidatorAddress, address webAuthValidatorAddress ) { AA_FACTORY_CONTRACT_ADDRESS = aaFactoryAddress; SESSION_KEY_VALIDATOR_CONTRACT_ADDRESS = sessionKeyValidatorAddress; ACCOUNT_RECOVERY_VALIDATOR_CONTRACT_ADDRESS = accountRecoveryValidatorAddress; WEB_AUTH_VALIDATOR_CONTRACT_ADDRESS = webAuthValidatorAddress; } function validateAndPayForPaymasterTransaction( bytes32, bytes32, Transaction calldata _transaction ) external payable onlyBootloader returns (bytes4 magic, bytes memory) { // By default we consider the transaction as accepted. magic = PAYMASTER_VALIDATION_SUCCESS_MAGIC; require(_transaction.paymasterInput.length >= 4, "The standard paymaster input must be at least 4 bytes long"); // Ensure the transaction is calling one of our allowed contracts address to = address(uint160(_transaction.to)); require( to == AA_FACTORY_CONTRACT_ADDRESS || to == SESSION_KEY_VALIDATOR_CONTRACT_ADDRESS || to == ACCOUNT_RECOVERY_VALIDATOR_CONTRACT_ADDRESS || to == WEB_AUTH_VALIDATOR_CONTRACT_ADDRESS, "Unsupported contract address" ); // Ensure the transaction is calling either the deployProxySsoAccount or createSession functions require(_transaction.data.length >= 4, "Transaction data is too short"); bytes4 methodSelector = bytes4(_transaction.data[0:4]); if (to == AA_FACTORY_CONTRACT_ADDRESS) { require(methodSelector == DEPLOY_ACCOUNT_SELECTOR, "Unsupported method"); } if (to == SESSION_KEY_VALIDATOR_CONTRACT_ADDRESS) { require( methodSelector == SESSION_CREATE_SELECTOR || methodSelector == SESSION_REVOKE_KEY_SELECTOR || methodSelector == SESSION_REVOKE_KEYS_SELECTOR, "Unsupported method" ); } if (to == ACCOUNT_RECOVERY_VALIDATOR_CONTRACT_ADDRESS) { require( methodSelector == GUARDIAN_RECOVERY_ADD_GUARDIAN_SELECTOR || methodSelector == GUARDIAN_RECOVERY_PROPOSE_GUARDIAN_SELECTOR || methodSelector == GUARDIAN_RECOVERY_DISCARD_RECOVERY_SELECTOR || methodSelector == GUARDIAN_RECOVERY_REMOVE_GUARDIAN_SELECTOR || methodSelector == GUARDIAN_RECOVERY_INIT_RECOVERY_SELECTOR, "Unsupported method" ); } if (to == WEB_AUTH_VALIDATOR_CONTRACT_ADDRESS) { require( methodSelector == WEB_AUTH_VALIDATOR_ADD_KEY_SELECTOR || methodSelector == WEB_AUTH_VALIDATOR_REMOVE_KEY_SELECTOR, "Unsupported method" ); } bytes4 paymasterInputSelector = bytes4(_transaction.paymasterInput[0:4]); require(paymasterInputSelector == IPaymasterFlow.general.selector, "Unsupported paymaster flow"); // Note, that while the minimal amount of ETH needed is tx.gasPrice * tx.gasLimit, // neither paymaster nor account are allowed to access this context variable. uint256 requiredETH = _transaction.gasLimit * _transaction.maxFeePerGas; // The bootloader never returns any data, so it can safely be ignored here. (bool success, ) = payable(BOOTLOADER_FORMAL_ADDRESS).call{ value: requiredETH }(""); require(success, "Failed to transfer tx fee to the Bootloader. Paymaster balance might not be enough."); } function postTransaction( bytes calldata _context, Transaction calldata _transaction, bytes32, bytes32, ExecutionResult _txResult, uint256 _maxRefundedGas ) external payable override onlyBootloader {} function withdraw(address payable _to) external onlyOwner { uint256 balance = address(this).balance; (bool success, ) = _to.call{ value: balance }(""); require(success, "Failed to withdraw funds from paymaster."); } receive() external payable {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { JSONParserLib } from "solady/src/utils/JSONParserLib.sol"; contract JSONParserLibTest { function parse(string calldata json) external pure returns (JSONParserLib.Item memory) { return JSONParserLib.parse(json); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import { Transaction, TransactionHelper } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { IExecutionHook } from "../interfaces/IHook.sol"; import { IModule } from "../interfaces/IModule.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; contract TestExecutionHook is IExecutionHook { using TransactionHelper for Transaction; event ExecutionHookInstalled(address indexed account); event ExecutionHookUninstalled(address indexed account); event PreExecution(address indexed account, address indexed target); event PostExecution(address indexed account, address indexed target); mapping(address => address) public lastTarget; function onInstall(bytes calldata data) external { bool shouldRevert = abi.decode(data, (bool)); if (shouldRevert) { revert("Install hook failed"); } emit ExecutionHookInstalled(msg.sender); } function onUninstall(bytes calldata data) external { bool shouldRevert = abi.decode(data, (bool)); if (shouldRevert) { revert("Uninstall hook failed"); } emit ExecutionHookUninstalled(msg.sender); } function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { return interfaceId == type(IExecutionHook).interfaceId || interfaceId == type(IModule).interfaceId || interfaceId == type(IERC165).interfaceId; } function preExecutionHook(Transaction calldata transaction) external returns (bytes memory context) { // arbitrary revert condition if (transaction.to == 0) { revert("PreExecution hook failed"); } // store some data in transient storage uint256 slot = uint256(uint160(msg.sender)); uint256 storedTarget = transaction.to; assembly { tstore(slot, storedTarget) } // store some data in regular storage lastTarget[msg.sender] = address(uint160(transaction.to)); // emit event emit PreExecution(msg.sender, address(uint160(transaction.to))); // pass some data via context return abi.encode(transaction); } function postExecutionHook(bytes calldata context) external { // decode context data Transaction memory transaction = abi.decode(context, (Transaction)); // arbitrary revert condition if (transaction.to == uint256(uint160(msg.sender))) { revert("PostExecution hook failed"); } // load data from transient storage uint256 storedTarget; uint256 slot = uint256(uint160(msg.sender)); assembly { storedTarget := tload(slot) } require(storedTarget != 0, "No data in transient storage"); require(transaction.to == storedTarget, "Targets do not match (tload)"); require(transaction.to == uint256(uint160(lastTarget[msg.sender])), "Targets do not match (sload)"); // emit event emit PostExecution(msg.sender, address(uint160(transaction.to))); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IPaymaster, ExecutionResult, PAYMASTER_VALIDATION_SUCCESS_MAGIC } from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IPaymaster.sol"; import { IPaymasterFlow } from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IPaymasterFlow.sol"; import { TransactionHelper, Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { BOOTLOADER_FORMAL_ADDRESS } from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol"; contract TestPaymaster is IPaymaster { modifier onlyBootloader() { require(msg.sender == BOOTLOADER_FORMAL_ADDRESS, "Only bootloader can call this method"); // Continue execution if called from the bootloader. _; } function validateAndPayForPaymasterTransaction( bytes32, bytes32, Transaction calldata transaction ) external payable onlyBootloader returns (bytes4 magic, bytes memory) { magic = PAYMASTER_VALIDATION_SUCCESS_MAGIC; bytes4 paymasterInputSelector = bytes4(transaction.paymasterInput[:4]); if (paymasterInputSelector == IPaymasterFlow.approvalBased.selector) { (address token, uint256 amount, bytes memory data) = abi.decode( transaction.paymasterInput[4:], (address, uint256, bytes) ); uint256 providedAllowance = IERC20(token).allowance(address(uint160(transaction.from)), address(this)); // For testing purposes any non-zero allowance of any token is enough require(providedAllowance > 0, "Min allowance too low"); IERC20(token).transferFrom(address(uint160(transaction.from)), address(this), amount); } else if (paymasterInputSelector == IPaymasterFlow.general.selector) { // For testing purposes any transaction is valid } else { revert("Unsupported paymaster flow"); } uint256 requiredETH = transaction.gasLimit * transaction.maxFeePerGas; (bool success, ) = payable(BOOTLOADER_FORMAL_ADDRESS).call{ value: requiredETH }(""); require(success, "Paymaster out of funds"); } function postTransaction( bytes calldata _context, Transaction calldata transaction, bytes32, bytes32, ExecutionResult _txResult, uint256 _maxRefundedGas ) external payable override onlyBootloader {} receive() external payable {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import { Transaction, TransactionHelper } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { IValidationHook } from "../interfaces/IHook.sol"; import { IModule } from "../interfaces/IModule.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; contract TestValidationHook is IValidationHook { using TransactionHelper for Transaction; event ValidationHookInstalled(address indexed account); event ValidationHookUninstalled(address indexed account); event ValidationHookTriggered(address indexed account, address indexed target); mapping(address => address) public lastTarget; function onInstall(bytes calldata data) external { bool shouldRevert = abi.decode(data, (bool)); if (shouldRevert) { revert("Install hook failed"); } emit ValidationHookInstalled(msg.sender); } function onUninstall(bytes calldata data) external { bool shouldRevert = abi.decode(data, (bool)); if (shouldRevert) { revert("Uninstall hook failed"); } emit ValidationHookUninstalled(msg.sender); } function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { return interfaceId == type(IValidationHook).interfaceId || interfaceId == type(IModule).interfaceId || interfaceId == type(IERC165).interfaceId; } // FIXME: if a validation hook were to always revert, the account would be bricked function validationHook(bytes32 signedHash, Transaction calldata transaction) external { if (transaction.data.length == 0) { revert("Empty calldata not allowed"); } address target = address(uint160(transaction.to)); // emit event emit ValidationHookTriggered(msg.sender, target); // store some data in storage lastTarget[msg.sender] = target; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import { EfficientProxy } from "./EfficientProxy.sol"; import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol"; import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; /// @title TransparentProxy /// @author Matter Labs /// @custom:security-contact [email protected] /// @notice This contract is modification of OpenZeppelin `TransparentUpgradeableProxy` with optimisation for /// cheap delegate calls on ZKsync. /// @dev This proxy is placed in front of `AAFactory` and all modules (`WebAuthValidator`, `SessionKeyValidator`). contract TransparentProxy is TransparentUpgradeableProxy, EfficientProxy { constructor( address implementation, bytes memory data ) TransparentUpgradeableProxy(implementation, msg.sender, data) {} function _delegate(address implementation) internal override(EfficientProxy, Proxy) { EfficientProxy._delegate(implementation); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import { WebAuthValidator } from "./WebAuthValidator.sol"; import { IGuardianRecoveryValidator } from "../interfaces/IGuardianRecoveryValidator.sol"; import { IModuleValidator } from "../interfaces/IModuleValidator.sol"; import { IModule } from "../interfaces/IModule.sol"; import { IValidatorManager } from "../interfaces/IValidatorManager.sol"; import { TimestampAsserterLocator } from "../helpers/TimestampAsserterLocator.sol"; import { Utils } from "../helpers/Utils.sol"; /// @title GuardianRecoveryValidator /// @author Matter Labs /// @custom:security-contact [email protected] /// @dev This contract allows account recovery using trusted guardians. contract GuardianRecoveryValidator is Initializable, IGuardianRecoveryValidator { using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.Bytes32Set; uint256 public constant REQUEST_VALIDITY_TIME = 72 * 60 * 60; // 72 hours uint256 public constant REQUEST_DELAY_TIME = 24 * 60 * 60; // 24 hours bytes30 private _gap; // Gap to claim 30 bytes remaining in slot 0 after fields layout of Initializable contract WebAuthValidator public webAuthValidator; // Enforced slot 1 in order to be able to access it during validateTransaction step mapping(bytes32 hashedOriginDomain => mapping(address account => EnumerableSetUpgradeable.AddressSet guardians)) private accountGuardians; mapping(bytes32 hashedOriginDomain => mapping(address guardian => EnumerableSetUpgradeable.AddressSet accounts)) private guardedAccounts; mapping(address account => EnumerableSetUpgradeable.Bytes32Set hashedOriginDomains) private accountHashedOriginDomains; mapping(bytes32 hashedOriginDomain => mapping(address account => RecoveryRequest recoveryData)) private pendingRecoveryData; mapping(bytes32 hashedOriginDomain => mapping(address account => mapping(address guardian => Guardian guardianData))) public accountGuardianData; /// @notice This modifier allows execution only by active guardian of account /// @param hashedOriginDomain Hash of origin domain /// @param account Address of account for which we verify guardian existence modifier onlyGuardianOf(bytes32 hashedOriginDomain, address account) { bool isGuardian = accountGuardians[hashedOriginDomain][account].contains(msg.sender) && accountGuardianData[hashedOriginDomain][account][msg.sender].isReady; if (!isGuardian) revert GuardianNotFound(msg.sender); // Continue execution if called by guardian _; } constructor() { _disableInitializers(); } function initialize(WebAuthValidator _webAuthValidator) external initializer { if (address(_webAuthValidator) == address(0)) revert InvalidWebAuthValidatorAddress(); webAuthValidator = _webAuthValidator; } /// @notice Validator initiator for given sso account. /// @dev This module does not support initialization on creation, /// but ensures that the WebAuthValidator is enabled for calling SsoAccount. /// @inheritdoc IModule function onInstall(bytes calldata) external { if (!IValidatorManager(msg.sender).isModuleValidator(address(webAuthValidator))) { revert WebAuthValidatorNotEnabled(); } } /// @notice Removes all past guardians when this module is disabled in a account /// @inheritdoc IModule function onUninstall(bytes calldata) external { bytes32[] memory hashedOriginDomains = accountHashedOriginDomains[msg.sender].values(); for (uint256 j = 0; j < hashedOriginDomains.length; ++j) { bytes32 hashedOriginDomain = hashedOriginDomains[j]; address[] memory guardians = accountGuardians[hashedOriginDomain][msg.sender].values(); for (uint256 i = 0; i < guardians.length; ++i) { address guardian = guardians[i]; bool wasActiveGuardian = accountGuardianData[hashedOriginDomain][msg.sender][guardian].isReady; if (wasActiveGuardian) { EnumerableSetUpgradeable.AddressSet storage accounts = guardedAccounts[hashedOriginDomain][guardian]; bool guardedAccountsRemovalSuccessful = accounts.remove(msg.sender); if (!guardedAccountsRemovalSuccessful) { revert AccountNotGuardedByAddress(msg.sender, guardian); } } delete accountGuardianData[hashedOriginDomain][msg.sender][guardian]; bool removalSuccessful = accountGuardians[hashedOriginDomain][msg.sender].remove(guardian); if (!removalSuccessful) { revert GuardianNotFound(guardian); } emit GuardianRemoved(msg.sender, hashedOriginDomain, guardian); } // Allow-listing slither finding as the element removal's success is granted due to the element being // loaded from the accountHashedOriginDomains EnumerableSet on line 74 // slither-disable-next-line unused-return accountHashedOriginDomains[msg.sender].remove(hashedOriginDomain); // Remove pending recovery data if exist if (pendingRecoveryData[hashedOriginDomain][msg.sender].timestamp != 0) { discardRecovery(hashedOriginDomain); } } } function proposeGuardian(bytes32 hashedOriginDomain, address newGuardian) external { if (msg.sender == newGuardian) revert GuardianCannotBeSelf(); if (newGuardian == address(0)) revert InvalidGuardianAddress(); bool additionSuccessful = accountGuardians[hashedOriginDomain][msg.sender].add(newGuardian); if (!additionSuccessful) { return; } accountGuardianData[hashedOriginDomain][msg.sender][newGuardian] = Guardian( newGuardian, false, uint64(block.timestamp) ); if (accountHashedOriginDomains[msg.sender].add(hashedOriginDomain)) { emit HashedOriginDomainEnabledForAccount(msg.sender, hashedOriginDomain); } emit GuardianProposed(msg.sender, hashedOriginDomain, newGuardian); } function removeGuardian(bytes32 hashedOriginDomain, address guardianToRemove) external { if (guardianToRemove == address(0)) revert InvalidGuardianAddress(); bool removalSuccessful = accountGuardians[hashedOriginDomain][msg.sender].remove(guardianToRemove); if (!removalSuccessful) { revert GuardianNotFound(guardianToRemove); } bool wasActiveGuardian = accountGuardianData[hashedOriginDomain][msg.sender][guardianToRemove].isReady; delete accountGuardianData[hashedOriginDomain][msg.sender][guardianToRemove]; if (wasActiveGuardian) { EnumerableSetUpgradeable.AddressSet storage accounts = guardedAccounts[hashedOriginDomain][guardianToRemove]; bool accountsRemovalSuccessful = accounts.remove(msg.sender); if (!accountsRemovalSuccessful) { revert AccountNotGuardedByAddress(msg.sender, guardianToRemove); } } if (accountGuardians[hashedOriginDomain][msg.sender].length() == 0) { if (!accountHashedOriginDomains[msg.sender].remove(hashedOriginDomain)) { revert UnknownHashedOriginDomain(hashedOriginDomain); } else { emit HashedOriginDomainDisabledForAccount(msg.sender, hashedOriginDomain); } } emit GuardianRemoved(msg.sender, hashedOriginDomain, guardianToRemove); return; } function addGuardian(bytes32 hashedOriginDomain, address accountToGuard) external returns (bool) { if (accountToGuard == address(0)) revert InvalidAccountToGuardAddress(); bool guardianProposed = accountGuardians[hashedOriginDomain][accountToGuard].contains(msg.sender); if (!guardianProposed) { revert GuardianNotProposed(msg.sender); } // We return true if the guardian was not confirmed before. if (accountGuardianData[hashedOriginDomain][accountToGuard][msg.sender].isReady) return false; accountGuardianData[hashedOriginDomain][accountToGuard][msg.sender].isReady = true; bool addSuccessful = guardedAccounts[hashedOriginDomain][msg.sender].add(accountToGuard); if (!addSuccessful) { revert AccountAlreadyGuardedByGuardian(accountToGuard, msg.sender); } emit GuardianAdded(accountToGuard, hashedOriginDomain, msg.sender); return true; } function initRecovery( address accountToRecover, bytes32 hashedCredentialId, bytes32[2] calldata rawPublicKey, bytes32 hashedOriginDomain ) external onlyGuardianOf(hashedOriginDomain, accountToRecover) { if (accountToRecover == address(0)) revert InvalidAccountToRecoverAddress(); if (pendingRecoveryData[hashedOriginDomain][accountToRecover].timestamp + REQUEST_VALIDITY_TIME > block.timestamp) { revert AccountRecoveryInProgress(); } pendingRecoveryData[hashedOriginDomain][accountToRecover] = RecoveryRequest( hashedCredentialId, rawPublicKey, uint64(block.timestamp) ); emit RecoveryInitiated(accountToRecover, hashedOriginDomain, hashedCredentialId, msg.sender); } function discardRecovery(bytes32 hashedOriginDomain) public { emit RecoveryDiscarded( msg.sender, hashedOriginDomain, pendingRecoveryData[hashedOriginDomain][msg.sender].hashedCredentialId ); _discardRecovery(hashedOriginDomain); } /// @inheritdoc IModuleValidator function validateTransaction(bytes32, Transaction calldata transaction) external returns (bool) { // Finishing Recovery Process. If the user has a recovery in progress then: // 1. The method will check if the transaction is attempting to modify passkeys // 2. Verify the new passkey matches the one stored in `initRecovery` // 3. Allows anyone to call this method, as the recovery was already verified in `initRecovery` // 4. Verifies that the required timelock period has passed since `initRecovery` was called // 5. If all the above are true, the recovery is finished if (transaction.data.length < 4) { revert NonFunctionCallTransaction(); } // Verify the transaction is a call to WebAuthValidator contract address target = Utils.safeCastToAddress(transaction.to); if (target != address(webAuthValidator)) { return false; } // Verify the transaction is a call to `addValidationKey` bytes4 selector = bytes4(transaction.data[:4]); if (selector != WebAuthValidator.addValidationKey.selector) { return false; } // Verify the current request matches pending one bytes calldata transactionData = transaction.data[4:]; (bytes memory credentialId, bytes32[2] memory rawPublicKey, string memory originDomain) = abi.decode( transactionData, (bytes, bytes32[2], string) ); bytes32 hashedOriginDomain = keccak256(abi.encodePacked(originDomain)); RecoveryRequest storage storedData = pendingRecoveryData[hashedOriginDomain][msg.sender]; bytes32 hashedCredentialIdFromTx = keccak256(credentialId); if (hashedCredentialIdFromTx != storedData.hashedCredentialId) { return false; } if (rawPublicKey[0] != storedData.rawPublicKey[0] || rawPublicKey[1] != storedData.rawPublicKey[1]) { return false; } // Verify request is in valid time range TimestampAsserterLocator.locate().assertTimestampInRange( storedData.timestamp + REQUEST_DELAY_TIME, storedData.timestamp + REQUEST_VALIDITY_TIME ); _finishRecovery(hashedOriginDomain); return true; } /// @inheritdoc IModuleValidator function validateSignature(bytes32, bytes calldata) external pure returns (bool) { return false; } /// @inheritdoc IERC165 function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { return interfaceId == type(IERC165).interfaceId || interfaceId == type(IModuleValidator).interfaceId || interfaceId == type(IModule).interfaceId || interfaceId == type(IGuardianRecoveryValidator).interfaceId; } function guardiansFor(bytes32 hashedOriginDomain, address addr) external view returns (Guardian[] memory) { address[] memory guardians = accountGuardians[hashedOriginDomain][addr].values(); Guardian[] memory result = new Guardian[](guardians.length); for (uint256 i = 0; i < guardians.length; ++i) { result[i] = accountGuardianData[hashedOriginDomain][addr][guardians[i]]; } return result; } function guardianOf(bytes32 hashedOriginDomain, address guardian) external view returns (address[] memory) { return guardedAccounts[hashedOriginDomain][guardian].values(); } function getPendingRecoveryData( bytes32 hashedOriginDomain, address account ) external view returns (RecoveryRequest memory) { return pendingRecoveryData[hashedOriginDomain][account]; } /// @notice This method allows to finish currently pending recovery /// @param hashedOriginDomain Hash of origin domain function _finishRecovery(bytes32 hashedOriginDomain) internal { emit RecoveryFinished( msg.sender, hashedOriginDomain, pendingRecoveryData[hashedOriginDomain][msg.sender].hashedCredentialId ); _discardRecovery(hashedOriginDomain); } /// @notice This method allows to discard currently pending recovery /// @param hashedOriginDomain Hash of origin domain function _discardRecovery(bytes32 hashedOriginDomain) internal { delete pendingRecoveryData[hashedOriginDomain][msg.sender]; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { IModuleValidator } from "../interfaces/IModuleValidator.sol"; import { IModule } from "../interfaces/IModule.sol"; import { IValidatorManager } from "../interfaces/IValidatorManager.sol"; import { SessionLib } from "../libraries/SessionLib.sol"; import { Errors } from "../libraries/Errors.sol"; import { SignatureDecoder } from "../libraries/SignatureDecoder.sol"; import { TimestampAsserterLocator } from "../helpers/TimestampAsserterLocator.sol"; /// @title SessionKeyValidator /// @author Matter Labs /// @custom:security-contact [email protected] /// @notice This contract is used to manage sessions for a smart account. contract SessionKeyValidator is IModuleValidator { using SessionLib for SessionLib.SessionStorage; event SessionCreated(address indexed account, bytes32 indexed sessionHash, SessionLib.SessionSpec sessionSpec); event SessionRevoked(address indexed account, bytes32 indexed sessionHash); // NOTE: expired sessions are still counted if not explicitly revoked mapping(address account => uint256 openSessions) private sessionCounter; mapping(bytes32 sessionHash => SessionLib.SessionStorage sessionState) private sessions; /// @notice Get the session state for an account /// @param account The account to fetch the session state for /// @param spec The session specification to get the state of /// @return The session state: status, remaining fee limit, transfer limits, call value and call parameter limits function sessionState( address account, SessionLib.SessionSpec calldata spec ) external view returns (SessionLib.SessionState memory) { return sessions[keccak256(abi.encode(spec))].getState(account, spec); } /// @notice Get the status of a session /// @param account The account to fetch the session status for /// @param sessionHash The session hash to fetch the status of /// @return The status of the session: NotInitialized, Active or Closed function sessionStatus(address account, bytes32 sessionHash) external view returns (SessionLib.Status) { return sessions[sessionHash].status[account]; } /// @notice Runs on module install /// @param data ABI-encoded session specification to immediately create a session, or empty if not needed function onInstall(bytes calldata data) external override { if (data.length > 0) { // This always either succeeds with `true` or reverts within, // so we don't need to check the return value. _addValidationKey(data); } } /// @notice Runs on module uninstall /// @param data ABI-encoded array of session hashes to revoke /// @dev Revokes provided sessions before uninstalling, /// reverts if any session is still active after that. function onUninstall(bytes calldata data) external override { // Revoke keys before uninstalling bytes32[] memory sessionHashes = abi.decode(data, (bytes32[])); for (uint256 i = 0; i < sessionHashes.length; i++) { revokeKey(sessionHashes[i]); } // Here we have make sure that all keys are revoked, so that if the module // is installed again later, there will be no active sessions from the past. uint256 openSessions = sessionCounter[msg.sender]; if (openSessions != 0) { revert Errors.UNINSTALL_WITH_OPEN_SESSIONS(openSessions); } } /// @notice This module should not be used to validate signatures (including EIP-1271), /// as a signature by itself does not have enough information to validate it against a session. /// @return false function validateSignature(bytes32, bytes memory) external pure returns (bool) { return false; } /// @notice Create a new session for an account /// @param sessionSpec The session specification to create a session with function createSession(SessionLib.SessionSpec memory sessionSpec) public { bytes32 sessionHash = keccak256(abi.encode(sessionSpec)); if (!isInitialized(msg.sender)) { revert Errors.NOT_FROM_INITIALIZED_ACCOUNT(msg.sender); } if (sessionSpec.signer == address(0)) { revert Errors.SESSION_ZERO_SIGNER(); } if (sessionSpec.feeLimit.limitType == SessionLib.LimitType.Unlimited) { revert Errors.SESSION_UNLIMITED_FEES(); } if (sessions[sessionHash].status[msg.sender] != SessionLib.Status.NotInitialized) { revert Errors.SESSION_ALREADY_EXISTS(sessionHash); } // Sessions should expire in no less than 60 seconds. if (sessionSpec.expiresAt <= block.timestamp + 60) { revert Errors.SESSION_EXPIRES_TOO_SOON(sessionSpec.expiresAt); } sessionCounter[msg.sender]++; sessions[sessionHash].status[msg.sender] = SessionLib.Status.Active; emit SessionCreated(msg.sender, sessionHash, sessionSpec); } /// @notice creates a new session for an account, called by onInstall /// @param sessionData ABI-encoded session specification function _addValidationKey(bytes calldata sessionData) private returns (bool) { SessionLib.SessionSpec memory sessionSpec = abi.decode(sessionData, (SessionLib.SessionSpec)); createSession(sessionSpec); return true; } /// @inheritdoc IERC165 function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { return interfaceId == type(IERC165).interfaceId || interfaceId == type(IModuleValidator).interfaceId || interfaceId == type(IModule).interfaceId; } /// @notice Revoke a session for an account /// @param sessionHash The hash of a session to revoke /// @dev Decreases the session counter for the account function revokeKey(bytes32 sessionHash) public { if (sessions[sessionHash].status[msg.sender] != SessionLib.Status.Active) { revert Errors.SESSION_NOT_ACTIVE(); } sessions[sessionHash].status[msg.sender] = SessionLib.Status.Closed; sessionCounter[msg.sender]--; emit SessionRevoked(msg.sender, sessionHash); } /// @notice Revoke multiple sessions for an account /// @param sessionHashes An array of session hashes to revoke function revokeKeys(bytes32[] calldata sessionHashes) external { for (uint256 i = 0; i < sessionHashes.length; i++) { revokeKey(sessionHashes[i]); } } /// @notice Check if the validator is registered for the smart account /// @param smartAccount The smart account to check /// @return true if validator is registered for the account, false otherwise function isInitialized(address smartAccount) public view returns (bool) { return IValidatorManager(smartAccount).isModuleValidator(address(this)); } /// @notice Validate a session transaction for an account /// @param signedHash The hash of the transaction /// @param transaction The transaction to validate /// @return true if the transaction is valid /// @dev Session spec and period IDs must be provided as validator data function validateTransaction(bytes32 signedHash, Transaction calldata transaction) external returns (bool) { (bytes memory transactionSignature, address _validator, bytes memory validatorData) = SignatureDecoder .decodeSignature(transaction.signature); (SessionLib.SessionSpec memory spec, uint64[] memory periodIds) = abi.decode( validatorData, // this is passed by the signature builder (SessionLib.SessionSpec, uint64[]) ); if (spec.signer == address(0)) { revert Errors.SESSION_ZERO_SIGNER(); } bytes32 sessionHash = keccak256(abi.encode(spec)); // this generally throws instead of returning false sessions[sessionHash].validate(transaction, spec, periodIds); (address recoveredAddress, ECDSA.RecoverError recoverError) = ECDSA.tryRecover(signedHash, transactionSignature); if (recoverError != ECDSA.RecoverError.NoError || recoveredAddress == address(0)) { return false; } if (recoveredAddress != spec.signer) { revert Errors.SESSION_INVALID_SIGNER(recoveredAddress, spec.signer); } // This check is separate and performed last to prevent gas estimation failures sessions[sessionHash].validateFeeLimit(transaction, spec, periodIds[0]); return true; } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.24; import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { IModuleValidator } from "../interfaces/IModuleValidator.sol"; import { IModule } from "../interfaces/IModule.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { Base64 } from "solady/src/utils/Base64.sol"; import { JSONParserLib } from "solady/src/utils/JSONParserLib.sol"; /// @title WebAuthValidator /// @author Matter Labs /// @custom:security-contact [email protected] /// @dev This contract allows secure user authentication using WebAuthn public keys. contract WebAuthValidator is IModuleValidator { using JSONParserLib for JSONParserLib.Item; using JSONParserLib for string; // Order of Layout: Types struct PasskeyId { string domain; bytes credentialId; } // Order of Layout: State variables /// @dev Mapping of public keys to the account address that owns them mapping(string originDomain => mapping(bytes credentialId => mapping(address accountAddress => bytes32[2] publicKey))) private publicKeys; /// @dev Mapping of domain-bound credential IDs to the account address that owns them mapping(string originDomain => mapping(bytes credentialId => address accountAddress)) public registeredAddress; /// @dev P256Verify precompile implementation, as defined in RIP-7212, is found at /// https://github.com/matter-labs/era-contracts/blob/main/system-contracts/contracts/precompiles/P256Verify.yul address private constant P256_VERIFIER = address(0x100); /// @dev check for secure validation: bit 0 = 1 (user present), bit 2 = 1 (user verified) bytes1 private constant AUTH_DATA_MASK = 0x05; bytes32 private constant LOW_S_MAX = 0x7fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a8; bytes32 private constant HIGH_R_MAX = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551; bytes32 private constant WEBAUTHN_GET_HASH = keccak256("webauthn.get"); bytes32 private constant FALSE_HASH = keccak256("false"); // Order of Layout: Events event PasskeyCreated(address indexed keyOwner, string originDomain, bytes credentialId); event PasskeyRemoved(address indexed keyOwner, string originDomain, bytes credentialId); // Order of Layout: Errors error NOT_KEY_OWNER(address account); error KEY_EXISTS(); error ACCOUNT_EXISTS(); error EMPTY_KEY(); error BAD_DOMAIN_LENGTH(); error BAD_CREDENTIAL_ID_LENGTH(); /// @notice This is helper function that returns the whole public key, as of solidity 0.8.24 the auto-generated getters only return half of the key /// @param originDomain the domain this key is associated with (the auth-server) /// @param credentialId the passkey unique identifier /// @param accountAddress the address of the account that owns the key /// @return publicKeys the public key function getAccountKey( string calldata originDomain, bytes calldata credentialId, address accountAddress ) external view returns (bytes32[2] memory) { return publicKeys[originDomain][credentialId][accountAddress]; } /// @notice Runs on module install /// @param data ABI-encoded WebAuthn passkey to add immediately, or empty if not needed function onInstall(bytes calldata data) external override { if (data.length > 0) { (bytes memory credentialId, bytes32[2] memory rawPublicKey, string memory originDomain) = abi.decode( data, (bytes, bytes32[2], string) ); addValidationKey(credentialId, rawPublicKey, originDomain); } } /// @notice Runs on module uninstall, does not manage any dependant modules /// @param data ABI-encoded array of origin domains to remove keys for function onUninstall(bytes calldata data) external override { PasskeyId[] memory passkeyIds = abi.decode(data, (PasskeyId[])); for (uint256 i = 0; i < passkeyIds.length; ++i) { PasskeyId memory passkeyId = passkeyIds[i]; removeValidationKey(passkeyId.credentialId, passkeyId.domain); } } function removeValidationKey(bytes memory credentialId, string memory domain) public { address registered = registeredAddress[domain][credentialId]; if (registered != msg.sender) { revert NOT_KEY_OWNER(registered); } registeredAddress[domain][credentialId] = address(0); publicKeys[domain][credentialId][msg.sender] = [bytes32(0), bytes32(0)]; emit PasskeyRemoved(msg.sender, domain, credentialId); } /// @notice Adds a WebAuthn passkey for the caller, reverts otherwise /// @param credentialId unique public identifier for the key /// @param rawPublicKey ABI-encoded WebAuthn public key to add /// @param originDomain the domain this associated with function addValidationKey( bytes memory credentialId, bytes32[2] memory rawPublicKey, string memory originDomain ) public { bytes32[2] memory initialAccountKey = publicKeys[originDomain][credentialId][msg.sender]; if (uint256(initialAccountKey[0]) != 0 || uint256(initialAccountKey[1]) != 0) { // only allow adding new keys, no overwrites/updates revert KEY_EXISTS(); } if (registeredAddress[originDomain][credentialId] != address(0)) { // this key already exists on the domain for an existing account revert ACCOUNT_EXISTS(); } if (rawPublicKey[0] == 0 && rawPublicKey[1] == 0) { // empty keys aren't valid revert EMPTY_KEY(); } uint256 domainLength = bytes(originDomain).length; if (domainLength < 1 || domainLength > 253) { // RFC 1035 sets domains between 1-253 characters revert BAD_DOMAIN_LENGTH(); } if (credentialId.length < 16) { // min length from: https://www.w3.org/TR/webauthn-2/#credential-id revert BAD_CREDENTIAL_ID_LENGTH(); } publicKeys[originDomain][credentialId][msg.sender] = rawPublicKey; registeredAddress[originDomain][credentialId] = msg.sender; emit PasskeyCreated(msg.sender, originDomain, credentialId); } /// @notice Validates a WebAuthn signature /// @param signedHash The hash of the signed message /// @param signature The signature to validate /// @return true if the signature is valid function validateSignature(bytes32 signedHash, bytes calldata signature) external view returns (bool) { return webAuthVerify(signedHash, signature); } /// @notice Validates a transaction signed with a passkey /// @dev Does not validate the transaction signature field, which is expected to be different due to the modular format /// @param signedHash The hash of the signed transaction /// @param transaction The transaction to validate /// @return true if the signature is valid function validateTransaction(bytes32 signedHash, Transaction calldata transaction) external view returns (bool) { (bytes memory signature, , ) = abi.decode(transaction.signature, (bytes, address, bytes)); return webAuthVerify(signedHash, signature); } /// @notice Validates a WebAuthn signature /// @dev Performs r & s range validation to prevent signature malleability /// @dev Checks passkey authenticator data flags (valid number of credentials) /// @dev Requires that the transaction signature hash was the signed challenge /// @dev Verifies that the signature was performed by a 'get' request /// @param transactionHash The hash of the signed message /// @param fatSignature The signature to validate (authenticator data, client data, [r, s]) /// @return true if the signature is valid function webAuthVerify(bytes32 transactionHash, bytes memory fatSignature) internal view returns (bool) { ( bytes memory authenticatorData, string memory clientDataJSON, bytes32[2] memory rs, bytes memory credentialId ) = _decodeFatSignature(fatSignature); // prevent signature replay https://yondon.blog/2019/01/01/how-not-to-use-ecdsa/ if (uint256(rs[0]) == 0 || rs[0] > HIGH_R_MAX || uint256(rs[1]) == 0 || rs[1] > LOW_S_MAX) { return false; } // https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API/Authenticator_data#attestedcredentialdata if (authenticatorData[32] & AUTH_DATA_MASK != AUTH_DATA_MASK) { return false; } // parse out the required fields (type, challenge, crossOrigin): https://goo.gl/yabPex JSONParserLib.Item memory root = JSONParserLib.parse(clientDataJSON); // challenge should contain the transaction hash, ensuring that the transaction is signed string memory challenge = root.at('"challenge"').value().decodeString(); bytes memory challengeData = Base64.decode(challenge); if (challengeData.length != 32) { return false; // wrong hash size } if (bytes32(challengeData) != transactionHash) { return false; } // type ensures the signature was created from a validation request string memory webauthn_type = root.at('"type"').value().decodeString(); if (WEBAUTHN_GET_HASH != keccak256(bytes(webauthn_type))) { return false; } // the origin determines which key to validate against // as passkeys are linked to domains, so the storage mapping reflects that string memory origin = root.at('"origin"').value().decodeString(); bytes32[2] memory publicKey = publicKeys[origin][credentialId][msg.sender]; if (uint256(publicKey[0]) == 0 && uint256(publicKey[1]) == 0) { // no key found! return false; } // cross-origin validation is optional, but explicitly not supported. // cross-origin requests would be from embedding the auth request // from another domain. The current SSO setup uses a pop-up instead of // an i-frame, so we're rejecting these until the implemention supports it JSONParserLib.Item memory crossOriginItem = root.at('"crossOrigin"'); if (!crossOriginItem.isUndefined()) { string memory crossOrigin = crossOriginItem.value(); if (FALSE_HASH != keccak256(bytes(crossOrigin))) { return false; } } bytes32 message = _createMessage(authenticatorData, bytes(clientDataJSON)); return callVerifier(P256_VERIFIER, message, rs, publicKey); } /// @inheritdoc IERC165 function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { return interfaceId == type(IERC165).interfaceId || interfaceId == type(IModuleValidator).interfaceId || interfaceId == type(IModule).interfaceId; } function _createMessage( bytes memory authenticatorData, bytes memory clientData ) private pure returns (bytes32 message) { bytes32 clientDataHash = sha256(clientData); message = sha256(bytes.concat(authenticatorData, clientDataHash)); } function _decodeFatSignature( bytes memory fatSignature ) private pure returns ( bytes memory authenticatorData, string memory clientDataSuffix, bytes32[2] memory rs, bytes memory credentialId ) { (authenticatorData, clientDataSuffix, rs, credentialId) = abi.decode( fatSignature, (bytes, string, bytes32[2], bytes) ); } /** * @notice Calls the verifier function with given params * @param verifier address - Address of the verifier contract * @param hash bytes32 - Signed data hash * @param rs bytes32[2] - Signature array for the r and s values * @param pubKey bytes32[2] - Public key coordinates array for the x and y values * @return - bool - Return the success of the verification */ function callVerifier( address verifier, bytes32 hash, bytes32[2] memory rs, bytes32[2] memory pubKey ) internal view returns (bool) { /** * Prepare the input format * input[ 0: 32] = signed data hash * input[ 32: 64] = signature r * input[ 64: 96] = signature s * input[ 96:128] = public key x * input[128:160] = public key y */ bytes memory input = abi.encodePacked(hash, rs[0], rs[1], pubKey[0], pubKey[1]); // Make a call to verify the signature (bool success, bytes memory data) = verifier.staticcall(input); uint256 returnValue; // Return true if the call was successful and the return value is 1 if (success && data.length > 0) { assembly { returnValue := mload(add(data, 0x20)) } return returnValue == 1; } // Otherwise return false for the unsuccessful calls and invalid signatures return false; } }
{ "evmVersion": "cancun", "codegen": "yul", "optimizer": { "enabled": true, "mode": "3" }, "outputSelection": { "*": { "*": [ "abi" ] } }, "detectMissingLibraries": false, "forceEVMLA": false, "enableEraVMExtensions": true, "libraries": {} }
Contract ABI
API[{"inputs":[{"internalType":"address","name":"beacon","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"},{"stateMutability":"payable","type":"receive"}]
Deployed Bytecode
0x001200000000000200060000000000020000006004100270000000d703400197000100000031035500020000003103550003000000310355000400000031035500050000003103550006000000310355000700000031035500080000003103550009000000310355000a000000310355000b000000310355000c000000310355000d000000310355000e000000310355000f00000031035500100000003103550011000000010355000000d70040019d0000008009000039000000400090043f0000000100200190000000820000c13d000000e101000041000000000101041a000000de02000041000000800020043f000000da02100197000000000003004b000000ae0000c13d0000000001000414000000d70010009c000000d701008041000000c001100210000000f2011001c7035803490000040f0000006003100270000000d703300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000080057001bf000000360000613d0000008008000039000000000901034f000000009a09043c0000000008a80436000000000058004b000000320000c13d000000000006004b000000430000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f00000000006504350000000100200190000000dd0000613d0000001f01400039000000600410018f00000080014001bf000000400010043f000000200030008c000000db0000413d000000800200043d000000da0020009c000000db0000213d000000000300041400000011050003670001000000500406000000f30030009c000001360000813d0000000001500400000000c003300210000000f403300197000000f5033001c700010000003103b500000000013103af035803530000040f0000006003100270000000d7033001970000000100200190000001880000613d0000001f02300039000000d8052001970000003f02500039000000e804200197000000400200043d0000000004420019000000000024004b00000000060000390000000106004039000000e00040009c000000a80000213d0000000100600190000000a80000c13d000000400040043f0000000004320436000000000005004b000000770000613d0000000005540019000000000600003100000011066003670000000007040019000000006806043c0000000007870436000000000057004b000000730000c13d0000001f0530018f000000d9063001980000000003640019000001720000613d000000000701034f0000000008040019000000007907043c0000000008980436000000000038004b0000007d0000c13d000001720000013d0000000002000416000000000002004b000000db0000c13d0000001f02300039000000d8022001970000008002200039000000400020043f0000001f0430018f000000d9053001980000008002500039000000930000613d0000008006000039000000000701034f000000007807043c0000000006860436000000000026004b0000008f0000c13d000000000004004b000000a00000613d000000000151034f0000000304400210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f0000000000120435000000200030008c000000db0000413d000000800300043d000000da0030009c000000db0000213d000000400100043d000000db0010009c000001070000a13d000000f701000041000000000010043f0000004101000039000000040010043f000000f8010000410000035a000104300000000001000414000000d70010009c000000d701008041000000c001100210000000f2011001c7035803490000040f0000006003100270000000d703300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000080057001bf000000c30000613d0000008008000039000000000901034f000000009a09043c0000000008a80436000000000058004b000000bf0000c13d000000000006004b000000d00000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f00000000006504350000000100200190000000e90000613d0000001f01400039000000600410018f00000080014001bf000000400010043f000000200030008c000000db0000413d000000800200043d000000da0020009c000001310000a13d00000000010000190000035a000104300000001f0530018f000000d906300198000000400200043d0000000004620019000000f40000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000000e40000c13d000000f40000013d0000001f0530018f000000d906300198000000400200043d0000000004620019000000f40000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000000f00000c13d000000000005004b000001010000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001404350000006001300210000000d70020009c000000d7020080410000004002200210000000000112019f0000035a00010430000300000009001d0000002002100039000400000002001d000000400020043f000500000001001d0000000000010435000000dc010000410000000000100443000600000003001d00000004003004430000000001000414000000d70010009c000000d701008041000000c001100210000000dd011001c70000800202000039035803490000040f0000000100200190000002c00000613d000000400300043d000000000101043b000000000001004b000001a00000c13d0000006401300039000000f00200004100000000002104350000004401300039000000f1020000410000000000210435000000240130003900000025020000390000000000210435000000e9010000410000000000130435000000040130003900000020020000390000000000210435000000d70030009c000000d7030080410000004001300210000000ef011001c70000035a00010430000000000300041400000011050003670001000000500406000000f30030009c000001440000413d000000e902000041000000000021043500000084024001bf00000020030000390000000000320435000000c402400039000000f6030000410000000000320435000000a402400039000000080300003900000000003204350000004001100210000000eb011001c70000035a000104300000000001500400000000c003300210000000f403300197000000f5033001c700010000003103b500000000013103af035803530000040f0000006003100270000000d7033001970000000100200190000001f70000613d0000001f02300039000000d8052001970000003f02500039000000e804200197000000400200043d0000000004420019000000000024004b00000000060000390000000106004039000000e00040009c000000a80000213d0000000100600190000000a80000c13d000000400040043f0000000004320436000000000005004b000001680000613d0000000005540019000000000600003100000011066003670000000007040019000000006806043c0000000007870436000000000057004b000001640000c13d0000001f0530018f000000d9063001980000000003640019000001720000613d000000000701034f0000000008040019000000007907043c0000000008980436000000000038004b0000016e0000c13d000000000005004b0000017f0000613d000000000161034f0000000305500210000000000603043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f0000000000130435000000d70040009c000000d70400804100000040014002100000000002020433000000d70020009c000000d7020080410000006002200210000000000112019f000003590001042e0000001f0430018f000000d902300198000001910000613d000000000501034f0000000006000019000000005705043c0000000006760436000000000026004b0000018d0000c13d000000000004004b0000019e0000613d000000000121034f0000000304400210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f000000000012043500000060013002100000035a00010430000000de010000410000000000130435000000d70030009c000000d701000041000000000103401900000040011002100000000002000414000000d70020009c000000d702008041000000c002200210000000000112019f000000df011001c70000000602000029000200000003001d035803490000040f000000020b0000290000006003100270000000d703300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000000057b0019000001bf0000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b000001bb0000c13d000000000006004b000001cc0000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f000000000065043500000001002001900000020f0000613d0000001f01400039000000600210018f0000000001b20019000000000021004b00000000020000390000000102004039000000e00010009c000000a80000213d0000000100200190000000a80000c13d000000400010043f000000200030008c000000db0000413d00000000010b0433000000da0010009c000000db0000213d000000dc02000041000000000020044300000004001004430000000001000414000000d70010009c000000d701008041000000c001100210000000dd011001c70000800202000039035803490000040f0000000100200190000002c00000613d000000400300043d000000000101043b000000000001004b0000021b0000c13d0000006401300039000000ed0200004100000000002104350000004401300039000000ee02000041000000000021043500000024013000390000003002000039000001260000013d0000001f0430018f000000d902300198000002000000613d000000000501034f0000000006000019000000005705043c0000000006760436000000000026004b000001fc0000c13d000000000004004b0000020d0000613d000000000121034f0000000304400210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f000000000012043500000060013002100000035a000104300000001f0530018f000000d906300198000000400200043d0000000004620019000000f40000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000002160000c13d000000f40000013d000200000003001d000000e101000041000000000201041a000000e2022001970000000605000029000000000252019f000000000021041b0000000001000414000000d70010009c000000d701008041000000c001100210000000e3011001c70000800d020000390000000203000039000000e404000041035803440000040f00000001002001900000000501000029000000db0000613d0000000001010433000000000001004b000002360000c13d000000200100003900000100001004430000012000000443000000ec01000041000003590001042e000000de0100004100000002020000290000000000120435000000d70020009c000000d701000041000000000102401900000040011002100000000002000414000000d70020009c000000d702008041000000c002200210000000000112019f000000df011001c70000000602000029035803490000040f0000006003100270000000d703300197000000200030008c000000200400003900000000040340190000001f0640018f00000020074001900000000205700029000002540000613d000000000801034f0000000209000029000000008a08043c0000000009a90436000000000059004b000002500000c13d000000000006004b000002610000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f00000000006504350000000100200190000002c10000613d0000001f01400039000000600110018f0000000202100029000000000012004b00000000010000390000000101004039000600000002001d000000e00020009c0000000504000029000000a80000213d0000000100100190000000a80000c13d0000000601000029000000400010043f000000200030008c000000db0000413d00000002010000290000000001010433000200000001001d000000da0010009c000000db0000213d0000000601000029000000e50010009c000000a80000213d00000006030000290000006001300039000000400010043f0000004001300039000000e602000041000000000021043500000027010000390000000002130436000000e701000041000100000002001d00000000001204350000000401000029000000d70010009c000000d70100804100000040011002100000000002040433000000d70020009c000000d7020080410000006002200210000000000112019f0000000002000414000000d70020009c000000d702008041000000c002200210000000000112019f00000002020000290358034e0000040f0000006003100270000000d704300198000002cd0000c13d000000600300003900000000010304330000000100200190000002f50000613d000000000001004b000002310000c13d000000dc010000410000000000100443000000020100002900000004001004430000000001000414000000d70010009c000000d701008041000000c001100210000000dd011001c70000800202000039035803490000040f0000000100200190000002c00000613d000000000101043b000000000001004b000002310000c13d000000400100043d0000004402100039000000ea03000041000000000032043500000024021000390000001d030000390000000000320435000000e9020000410000000000210435000000040210003900000020030000390000000000320435000000d70010009c000000d7010080410000004001100210000000eb011001c70000035a00010430000000000001042f0000001f0530018f000000d906300198000000400200043d0000000004620019000000f40000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000002c80000c13d000000f40000013d0000001f03400039000000d8033001970000003f03300039000000e805300197000000400300043d0000000005530019000000000035004b00000000060000390000000106004039000000e00050009c000000a80000213d0000000100600190000000a80000c13d000000400050043f0000001f0540018f0000000007430436000000d906400198000300000007001d0000000004670019000002e70000613d000000000701034f0000000308000029000000007907043c0000000008980436000000000048004b000002e30000c13d000000000005004b0000029a0000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001404350000029a0000013d000000000001004b000003160000c13d000000400100043d000000e90200004100000000002104350000000402100039000000200300003900000000003204350000000602000029000000000202043300000024031000390000000000230435000000f9062001970000001f0520018f0000004404100039000000010040006b0000031f0000813d000000000006004b000003120000613d00000001085000290000000007540019000000200770008a000000200880008a0000000009670019000000000a680019000000000a0a04330000000000a90435000000200660008c0000030c0000c13d000000000005004b000003360000613d00000000070400190000032b0000013d0000000302000029000000d70020009c000000d7020080410000004002200210000000d70010009c000000d7010080410000006001100210000000000121019f0000035a000104300000000007640019000000000006004b000003280000613d00000001080000290000000009040019000000008a0804340000000009a90436000000000079004b000003240000c13d000000000005004b000003360000613d000100010060002d0000000305500210000000000607043300000000065601cf000000000656022f000000010800002900000000080804330000010005500089000000000858022f00000000055801cf000000000565019f00000000005704350000001f05200039000000f903500197000000000242001900000000000204350000004402300039000000d70020009c000000d7020080410000006002200210000000d70010009c000000d7010080410000004001100210000000000112019f0000035a00010430000000000001042f00000347002104210000000102000039000000000001042d0000000002000019000000000001042d0000034c002104230000000102000039000000000001042d0000000002000019000000000001042d00000351002104250000000102000039000000000001042d0000000002000019000000000001042d00000356002104250000000102000039000000000001042d0000000002000019000000000001042d0000035800000432000003590001042e0000035a00010430000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000000000000000000000000000000000000000000000000001ffffffe000000000000000000000000000000000000000000000000000000000ffffffe0000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000ffffffffffffffdf1806aa1896bbf26568e884a7374b41e002500962caba6a15023a8d90e8508b8302000002000000000000000000000000000000240000000000000000000000005c60da1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50ffffffffffffffffffffffff000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e000000000000000000000000000000000000000000000000ffffffffffffff9f206661696c656400000000000000000000000000000000000000000000000000416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c00000000000000000000000000000000000000000000000000000003ffffffe008c379a000000000000000000000000000000000000000000000000000000000416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000000000000000000000000000000000000000000064000000000000000000000000000000020000000000000000000000000000004000000100000000000000000073206e6f74206120636f6e747261637400000000000000000000000000000000455243313936373a20626561636f6e20696d706c656d656e746174696f6e206900000000000000000000000000000000000000840000000000000000000000007472616374000000000000000000000000000000000000000000000000000000455243313936373a206e657720626561636f6e206973206e6f74206120636f6e0000000000000000000000000000000000000004000000800000000000000000000000000000000000000000000000000000000000000000000000010000000000000000ffffffff00000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000004f766572666c6f770000000000000000000000000000000000000000000000004e487b71000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0f6572fa15ff89a06a487ac8ea89a890e33582f91d750adf1589a7ed4f6852c50
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.