Source Code
Latest 7 from a total of 7 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Transfer Ownersh... | 31748529 | 41 days ago | IN | 0 ETH | 0.00000488 | ||||
| Withdraw | 31748188 | 41 days ago | IN | 0 ETH | 0.00000675 | ||||
| Request Ownershi... | 31122208 | 46 days ago | IN | 0 ETH | 0.0000056 | ||||
| Request Ownershi... | 31121928 | 46 days ago | IN | 0.0001 ETH | 0.00000403 | ||||
| Cancel Ownership... | 31121860 | 46 days ago | IN | 0.00001 ETH | 0.0000045 | ||||
| Cancel Ownership... | 31119987 | 46 days ago | IN | 0 ETH | 0.00000557 | ||||
| Withdraw | 31088652 | 46 days ago | IN | 0 ETH | 0.00000675 |
Cross-Chain Transactions
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.
Contract Name:
OnchainPaymentHandler
Compiler Version
v0.8.28+commit.7893614a
ZkSolc Version
v1.5.11
Optimization Enabled:
Yes with Mode 3
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {EIP712} from "solady-0.1.24/src/utils/EIP712.sol";
import {Ownable} from "solady-0.1.24/src/auth/Ownable.sol";
import {SafeTransferLib} from "solady-0.1.24/src/utils/ext/zksync/SafeTransferLib.sol";
import {SignatureCheckerLib} from "solady-0.1.24/src/utils/SignatureCheckerLib.sol";
import {PurchaseReservation} from "./Structs.sol";
contract OnchainPaymentHandler is EIP712, Ownable {
using SafeTransferLib for address;
bytes32 public constant PURCHASE_TYPEHASH =
keccak256("Purchase(bytes32 reservationId,uint256 maxQuantity,uint256 expiration,uint256 pricePerUnit)");
address public signer;
address public paymentToken;
bool public isPaused;
mapping(bytes32 => PurchaseReservation) public reservations;
error ReservationUsed();
error ReservationExpired();
error ExceedsMaxQuantity();
error Paused();
event Purchase(address indexed buyer, bytes32 indexed reservationId, uint256 quantity, uint256 pricePerUnit);
constructor(address owner, address _paymentToken, address _signer) {
_initializeOwner(owner);
paymentToken = _paymentToken;
signer = _signer;
}
function processPayment(
bytes32 reservationId,
address buyer,
uint256 quantity,
uint256 maxQuantity,
uint256 expiration,
uint256 pricePerUnit,
bytes calldata signature
) public {
if (isPaused) revert Paused();
_validatePurchase(reservationId, quantity, maxQuantity, expiration, pricePerUnit, signature);
uint256 totalAmount = quantity * pricePerUnit;
reservations[reservationId] =
PurchaseReservation({buyer: buyer, quantity: uint16(quantity), amount: uint80(totalAmount)});
paymentToken.safeTransferFrom(msg.sender, address(this), totalAmount);
emit Purchase(buyer, reservationId, quantity, pricePerUnit);
}
function withdraw(address token) public onlyOwner {
token.safeTransferAll(owner());
}
function setSigner(address _signer) public onlyOwner {
signer = _signer;
}
function setPaused(bool _isPaused) public onlyOwner {
isPaused = _isPaused;
}
function _validatePurchase(
bytes32 reservationId,
uint256 quantity,
uint256 maxQuantity,
uint256 expiration,
uint256 pricePerUnit,
bytes calldata signature
) internal virtual {
require(reservations[reservationId].buyer == address(0), ReservationUsed());
require(block.timestamp < expiration, ReservationExpired());
require(quantity <= maxQuantity, ExceedsMaxQuantity());
bytes32 digest = _hashTypedData(
keccak256(abi.encode(PURCHASE_TYPEHASH, reservationId, maxQuantity, expiration, pricePerUnit))
);
require(SignatureCheckerLib.isValidSignatureNowCalldata(signer, digest, signature), "Invalid signature");
}
function _domainNameAndVersion() internal pure override returns (string memory, string memory) {
return ("OnchainPaymentHandler", "1");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Contract for EIP-712 typed structured data hashing and signing.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
///
/// @dev Note, this implementation:
/// - Uses `address(this)` for the `verifyingContract` field.
/// - Does NOT use the optional EIP-712 salt.
/// - Does NOT use any EIP-712 extensions.
/// This is for simplicity and to save gas.
/// If you need to customize, please fork / modify accordingly.
abstract contract EIP712 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS AND IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 internal constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev `keccak256("EIP712Domain(string name,string version,address verifyingContract)")`.
/// This is only used in `_hashTypedDataSansChainId`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID =
0x91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766;
/// @dev `keccak256("EIP712Domain(string name,string version)")`.
/// This is only used in `_hashTypedDataSansChainIdAndVerifyingContract`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT =
0xb03948446334eb9b2196d5eb166f69b9d49403eb4a12f36de8d3f9f3cb8e15c3;
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId)")`.
/// This is only used in `_hashTypedDataSansVerifyingContract`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT =
0xc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e;
uint256 private immutable _cachedThis;
uint256 private immutable _cachedChainId;
bytes32 private immutable _cachedNameHash;
bytes32 private immutable _cachedVersionHash;
bytes32 private immutable _cachedDomainSeparator;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Cache the hashes for cheaper runtime gas costs.
/// In the case of upgradeable contracts (i.e. proxies),
/// or if the chain id changes due to a hard fork,
/// the domain separator will be seamlessly calculated on-the-fly.
constructor() {
_cachedThis = uint256(uint160(address(this)));
_cachedChainId = block.chainid;
string memory name;
string memory version;
if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
bytes32 versionHash =
_domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
_cachedNameHash = nameHash;
_cachedVersionHash = versionHash;
bytes32 separator;
if (!_domainNameAndVersionMayChange()) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
_cachedDomainSeparator = separator;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FUNCTIONS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to return the domain name and version.
/// ```
/// function _domainNameAndVersion()
/// internal
/// pure
/// virtual
/// returns (string memory name, string memory version)
/// {
/// name = "Solady";
/// version = "1";
/// }
/// ```
///
/// Note: If the returned result may change after the contract has been deployed,
/// you must override `_domainNameAndVersionMayChange()` to return true.
function _domainNameAndVersion()
internal
view
virtual
returns (string memory name, string memory version);
/// @dev Returns if `_domainNameAndVersion()` may change
/// after the contract has been deployed (i.e. after the constructor).
/// Default: false.
function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _domainSeparator() internal view virtual returns (bytes32 separator) {
if (_domainNameAndVersionMayChange()) {
separator = _buildDomainSeparator();
} else {
separator = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
}
}
/// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
/// given `structHash`, as defined in
/// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
///
/// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
/// ```
/// bytes32 digest = _hashTypedData(keccak256(abi.encode(
/// keccak256("Mail(address to,string contents)"),
/// mailTo,
/// keccak256(bytes(mailContents))
/// )));
/// address signer = ECDSA.recover(digest, signature);
/// ```
function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
// We will use `digest` to store the domain separator to save a bit of gas.
if (_domainNameAndVersionMayChange()) {
digest = _buildDomainSeparator();
} else {
digest = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
}
/// @solidity memory-safe-assembly
assembly {
// Compute the digest.
mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
mstore(0x1a, digest) // Store the domain separator.
mstore(0x3a, structHash) // Store the struct hash.
digest := keccak256(0x18, 0x42)
// Restore the part of the free memory slot that was overwritten.
mstore(0x3a, 0)
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID.
/// Included for the niche use case of cross-chain workflows.
function _hashTypedDataSansChainId(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
mstore(0x60, address())
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
/// Included for the niche use case of cross-chain and multi-verifier workflows.
function _hashTypedDataSansChainIdAndVerifyingContract(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x60)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
/// Included for the niche use case of multi-verifier workflows.
function _hashTypedDataSansVerifyingContract(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
mstore(0x60, chainid())
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-5267 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev See: https://eips.ethereum.org/EIPS/eip-5267
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
fields = hex"0f"; // `0b01111`.
(name, version) = _domainNameAndVersion();
chainId = block.chainid;
verifyingContract = address(this);
salt = salt; // `bytes32(0)`.
extensions = extensions; // `new uint256[](0)`.
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _buildDomainSeparator() private view returns (bytes32 separator) {
// We will use `separator` to store the name hash to save a bit of gas.
bytes32 versionHash;
if (_domainNameAndVersionMayChange()) {
(string memory name, string memory version) = _domainNameAndVersion();
separator = keccak256(bytes(name));
versionHash = keccak256(bytes(version));
} else {
separator = _cachedNameHash;
versionHash = _cachedVersionHash;
}
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), separator) // Name hash.
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
/// @dev Returns if the cached domain separator has been invalidated.
function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
uint256 cachedChainId = _cachedChainId;
uint256 cachedThis = _cachedThis;
/// @solidity memory-safe-assembly
assembly {
result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {SingleUseETHVault} from "./SingleUseETHVault.sol";
/// @notice Library for force safe transferring ETH and ERC20s in ZKsync.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ext/zksync/SafeTransferLib.sol)
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A single use ETH vault has been created for `to`, with `amount`.
event SingleUseETHVaultCreated(address indexed to, uint256 amount, address vault);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/// @dev The ERC20 `totalSupply` query has failed.
error TotalSupplyQueryFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 1000000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, 0x00, 0x00, 0x00, 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), 0x00, 0x00, 0x00, 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
/// If force transfer is used, returns the vault. Else returns `address(0)`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (address vault)
{
if (amount == uint256(0)) return address(0); // Early return if `amount` is zero.
uint256 selfBalanceBefore = address(this).balance;
/// @solidity memory-safe-assembly
assembly {
if lt(selfBalanceBefore, amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
pop(call(gasStipend, to, amount, 0x00, 0x00, 0x00, 0x00))
}
if (address(this).balance == selfBalanceBefore) {
vault = address(new SingleUseETHVault());
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, shr(96, shl(96, to)))
if iszero(call(gas(), vault, amount, 0x00, 0x20, 0x00, 0x00)) { revert(0x00, 0x00) }
}
emit SingleUseETHVaultCreated(to, amount, vault);
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
/// If force transfer is used, returns the vault. Else returns `address(0)`.
function forceSafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (address vault)
{
vault = forceSafeTransferETH(to, address(this).balance, gasStipend);
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
/// If force transfer is used, returns the vault. Else returns `address(0)`.
function forceSafeTransferETH(address to, uint256 amount) internal returns (address vault) {
vault = forceSafeTransferETH(to, amount, GAS_STIPEND_NO_GRIEF);
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
/// If force transfer is used, returns the vault. Else returns `address(0)`.
function forceSafeTransferAllETH(address to) internal returns (address vault) {
vault = forceSafeTransferETH(to, address(this).balance, GAS_STIPEND_NO_GRIEF);
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, 0x00, 0x00, 0x00, 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), 0x00, 0x00, 0x00, 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function trySafeTransferFrom(address token, address from, address to, uint256 amount)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have their entire balance approved for the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, 0x00, 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
// Check the `extcodesize` again just in case the token selfdestructs lol.
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=
mul( // The arguments of `mul` are evaluated from right to left.
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
/// @dev Returns the total supply of the `token`.
/// Reverts if the token does not exist or does not implement `totalSupply()`.
function totalSupply(address token) internal view returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x18160ddd) // `totalSupply()`.
if iszero(
and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
) {
mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
revert(0x1c, 0x04)
}
result := mload(0x00)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Signature verification helper that supports both ECDSA signatures from EOAs
/// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol)
///
/// @dev Note:
/// - The signature checking functions use the ecrecover precompile (0x1).
/// - The `bytes memory signature` variants use the identity precompile (0x4)
/// to copy memory internally.
/// - Unlike ECDSA signatures, contract signatures are revocable.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
/// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
/// See: https://eips.ethereum.org/EIPS/eip-2098
/// This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT use signatures as unique identifiers:
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
/// EIP-712 also enables readable signing of typed data for better user safety.
/// This implementation does NOT check if a signature is non-malleable.
library SignatureCheckerLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIGNATURE CHECKING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `signature` is valid for `signer` and `hash`.
/// If `signer.code.length == 0`, then validate with `ecrecover`, else
/// it will validate with ERC1271 on `signer`.
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
view
returns (bool isValid)
{
if (signer == address(0)) return isValid;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
for {} 1 {} {
if iszero(extcodesize(signer)) {
switch mload(signature)
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { break }
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
// Copy the `signature` over.
let n := add(0x20, mload(signature))
let copied := staticcall(gas(), 4, signature, n, add(m, 0x44), n)
isValid := staticcall(gas(), signer, m, add(returndatasize(), 0x44), d, 0x20)
isValid := and(eq(mload(d), f), and(isValid, copied))
break
}
}
}
/// @dev Returns whether `signature` is valid for `signer` and `hash`.
/// If `signer.code.length == 0`, then validate with `ecrecover`, else
/// it will validate with ERC1271 on `signer`.
function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature)
internal
view
returns (bool isValid)
{
if (signer == address(0)) return isValid;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
for {} 1 {} {
if iszero(extcodesize(signer)) {
switch signature.length
case 64 {
let vs := calldataload(add(signature.offset, 0x20))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, calldataload(signature.offset)) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`.
}
default { break }
mstore(0x00, hash)
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), signature.length)
// Copy the `signature` over.
calldatacopy(add(m, 0x64), signature.offset, signature.length)
isValid := staticcall(gas(), signer, m, add(signature.length, 0x64), d, 0x20)
isValid := and(eq(mload(d), f), isValid)
break
}
}
}
/// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`.
/// If `signer.code.length == 0`, then validate with `ecrecover`, else
/// it will validate with ERC1271 on `signer`.
function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
internal
view
returns (bool isValid)
{
if (signer == address(0)) return isValid;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
for {} 1 {} {
if iszero(extcodesize(signer)) {
mstore(0x00, hash)
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, r) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
isValid := and(eq(mload(d), f), isValid)
break
}
}
}
/// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`.
/// If `signer.code.length == 0`, then validate with `ecrecover`, else
/// it will validate with ERC1271 on `signer`.
function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (bool isValid)
{
if (signer == address(0)) return isValid;
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
for {} 1 {} {
if iszero(extcodesize(signer)) {
mstore(0x00, hash)
mstore(0x20, and(v, 0xff)) // `v`.
mstore(0x40, r) // `r`.
mstore(0x60, s) // `s`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), s) // `s`.
mstore8(add(m, 0xa4), v) // `v`.
isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
isValid := and(eq(mload(d), f), isValid)
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1271 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: These ERC1271 operations do NOT have an ECDSA fallback.
/// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
// Copy the `signature` over.
let n := add(0x20, mload(signature))
let copied := staticcall(gas(), 4, signature, n, add(m, 0x44), n)
isValid := staticcall(gas(), signer, m, add(returndatasize(), 0x44), d, 0x20)
isValid := and(eq(mload(d), f), and(isValid, copied))
}
}
/// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
function isValidERC1271SignatureNowCalldata(
address signer,
bytes32 hash,
bytes calldata signature
) internal view returns (bool isValid) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), signature.length)
// Copy the `signature` over.
calldatacopy(add(m, 0x64), signature.offset, signature.length)
isValid := staticcall(gas(), signer, m, add(signature.length, 0x64), d, 0x20)
isValid := and(eq(mload(d), f), isValid)
}
}
/// @dev Returns whether the signature (`r`, `vs`) is valid for `hash`
/// for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
isValid := and(eq(mload(d), f), isValid)
}
}
/// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash`
/// for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), s) // `s`.
mstore8(add(m, 0xa4), v) // `v`.
isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
isValid := and(eq(mload(d), f), isValid)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC6492 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: These ERC6492 operations now include an ECDSA fallback at the very end.
// The calldata variants are excluded for brevity.
/// @dev Returns whether `signature` is valid for `hash`.
/// If the signature is postfixed with the ERC6492 magic number, it will attempt to
/// deploy / prepare the `signer` smart account before doing a regular ERC1271 check.
/// Note: This function is NOT reentrancy safe.
/// The verifier must be deployed.
/// Otherwise, the function will return false if `signer` is not yet deployed / prepared.
/// See: https://gist.github.com/Vectorized/011d6becff6e0a73e42fe100f8d7ef04
/// With a dedicated verifier, this function is safe to use in contracts
/// that have been granted special permissions.
function isValidERC6492SignatureNowAllowSideEffects(
address signer,
bytes32 hash,
bytes memory signature
) internal returns (bool isValid) {
/// @solidity memory-safe-assembly
assembly {
function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
let m_ := mload(0x40)
let f_ := shl(224, 0x1626ba7e)
mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m_, 0x04), hash_)
let d_ := add(m_, 0x24)
mstore(d_, 0x40) // The offset of the `signature` in the calldata.
let n_ := add(0x20, mload(signature_))
let copied_ := staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_)
_isValid := staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
_isValid := and(eq(mload(d_), f_), and(_isValid, copied_))
}
let noCode := iszero(extcodesize(signer))
let n := mload(signature)
for {} 1 {} {
if iszero(eq(mload(add(signature, n)), mul(0x6492, div(not(isValid), 0xffff)))) {
if iszero(noCode) { isValid := callIsValidSignature(signer, hash, signature) }
break
}
if iszero(noCode) {
let o := add(signature, 0x20) // Signature bytes.
isValid := callIsValidSignature(signer, hash, add(o, mload(add(o, 0x40))))
if isValid { break }
}
let m := mload(0x40)
mstore(m, signer)
mstore(add(m, 0x20), hash)
pop(
call(
gas(), // Remaining gas.
0x0000bc370E4DC924F427d84e2f4B9Ec81626ba7E, // Non-reverting verifier.
0, // Send zero ETH.
m, // Start of memory.
add(returndatasize(), 0x40), // Length of calldata in memory.
staticcall(gas(), 4, add(signature, 0x20), n, add(m, 0x40), n), // 1.
0x00 // Length of returndata to write.
)
)
isValid := returndatasize()
break
}
// Do `ecrecover` fallback if `noCode && !isValid`.
for {} gt(noCode, isValid) {} {
switch n
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { break }
let m := mload(0x40)
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/// @dev Returns whether `signature` is valid for `hash`.
/// If the signature is postfixed with the ERC6492 magic number, it will attempt
/// to use a reverting verifier to deploy / prepare the `signer` smart account
/// and do a `isValidSignature` check via the reverting verifier.
/// Note: This function is reentrancy safe.
/// The reverting verifier must be deployed.
/// Otherwise, the function will return false if `signer` is not yet deployed / prepared.
/// See: https://gist.github.com/Vectorized/846a474c855eee9e441506676800a9ad
function isValidERC6492SignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
let m_ := mload(0x40)
let f_ := shl(224, 0x1626ba7e)
mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m_, 0x04), hash_)
let d_ := add(m_, 0x24)
mstore(d_, 0x40) // The offset of the `signature` in the calldata.
let n_ := add(0x20, mload(signature_))
let copied_ := staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_)
_isValid := staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
_isValid := and(eq(mload(d_), f_), and(_isValid, copied_))
}
let noCode := iszero(extcodesize(signer))
let n := mload(signature)
for {} 1 {} {
if iszero(eq(mload(add(signature, n)), mul(0x6492, div(not(isValid), 0xffff)))) {
if iszero(noCode) { isValid := callIsValidSignature(signer, hash, signature) }
break
}
if iszero(noCode) {
let o := add(signature, 0x20) // Signature bytes.
isValid := callIsValidSignature(signer, hash, add(o, mload(add(o, 0x40))))
if isValid { break }
}
let m := mload(0x40)
mstore(m, signer)
mstore(add(m, 0x20), hash)
let willBeZeroIfRevertingVerifierExists :=
call(
gas(), // Remaining gas.
0x00007bd799e4A591FeA53f8A8a3E9f931626Ba7e, // Reverting verifier.
0, // Send zero ETH.
m, // Start of memory.
add(returndatasize(), 0x40), // Length of calldata in memory.
staticcall(gas(), 4, add(signature, 0x20), n, add(m, 0x40), n), // 1.
0x00 // Length of returndata to write.
)
isValid := gt(returndatasize(), willBeZeroIfRevertingVerifierExists)
break
}
// Do `ecrecover` fallback if `noCode && !isValid`.
for {} gt(noCode, isValid) {} {
switch n
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { break }
let m := mload(0x40)
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an Ethereum Signed Message, created from a `hash`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
/// JSON-RPC method as part of EIP-191.
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, hash) // Store into scratch space for keccak256.
mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
}
}
/// @dev Returns an Ethereum Signed Message, created from `s`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
/// JSON-RPC method as part of EIP-191.
/// Note: Supports lengths of `s` up to 999999 bytes.
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let sLength := mload(s)
let o := 0x20
mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
mstore(0x00, 0x00)
// Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
for { let temp := sLength } 1 {} {
o := sub(o, 1)
mstore8(o, add(48, mod(temp, 10)))
temp := div(temp, 10)
if iszero(temp) { break }
}
let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
// Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
mstore(s, sLength) // Restore the length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EMPTY CALLDATA HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an empty calldata bytes.
function emptySignature() internal pure returns (bytes calldata signature) {
/// @solidity memory-safe-assembly
assembly {
signature.length := 0
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
struct PackItem {
address token;
uint96 id;
}
struct CubeResult {
BlisterPackResult[] blisterPacks;
ToyResult[] looseToys;
uint256 looseItems;
}
struct BlisterPackResult {
ToyResult[] toys;
uint256 looseItems;
}
struct ToyResult {
uint256 equippedItems;
}
enum PurchaseType {
TOY,
ITEM,
EQUIPPED_ITEM,
TOY_WITH_ITEMS,
BLISTER_PACK
}
struct CheckoutItem {
PurchaseType purchaseType;
bool enabled;
bool quantityLimited;
uint40 activeFrom;
uint40 activeUntil;
uint32 price;
uint32 maxQuantityPerPurchase;
uint32 maxQuantityPerAddress;
uint32 totalAvailable;
uint32 remainingAvailable;
string name;
bytes additionalData;
}
struct CreateCheckoutItem {
PurchaseType purchaseType;
bool quantityLimited;
uint40 activeFrom;
uint40 activeUntil;
uint32 price;
uint32 maxQuantityPerPurchase;
uint32 maxQuantityPerAddress;
uint32 totalAvailable;
string name;
bytes additionalData;
}
struct UpdateCheckoutItem {
bool enabled;
uint40 activeFrom;
uint40 activeUntil;
uint32 price;
uint32 maxQuantityPerPurchase;
uint32 maxQuantityPerAddress;
uint32 totalAvailable;
string name;
bytes additionalData;
}
struct PurchaseReservation {
address buyer;
uint16 quantity;
uint80 amount;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice A single-use vault that allows a designated caller to withdraw all ETH in it.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ext/zksync/SingleUseETHVault.sol)
contract SingleUseETHVault {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unable to withdraw all.
error WithdrawAllFailed();
/// @dev Not authorized.
error Unauthorized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WITHDRAW ALL */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
fallback() external payable virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x40, 0) // Optimization trick to remove free memory pointer initialization.
let owner := sload(0)
// Initialization.
if iszero(owner) {
sstore(0, calldataload(0x00)) // Store the owner.
return(0x00, 0x00) // Early return.
}
// Authorization check.
if iszero(eq(caller(), owner)) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
let to := calldataload(0x00)
// If the calldata is less than 32 bytes, zero-left-pad it to 32 bytes.
// Then use the rightmost 20 bytes of the word as the `to` address.
// This allows for the calldata to be `abi.encode(to)` or `abi.encodePacked(to)`.
to := shr(mul(lt(calldatasize(), 0x20), shl(3, sub(0x20, calldatasize()))), to)
// If `to` is `address(0)`, set it to `msg.sender`.
to := xor(mul(xor(to, caller()), iszero(to)), to)
// Transfers the whole balance to `to`.
if iszero(call(gas(), to, selfbalance(), 0x00, 0x00, 0x00, 0x00)) {
mstore(0x00, 0x651aee10) // `WithdrawAllFailed()`.
revert(0x1c, 0x04)
}
}
}
}{
"viaIR": false,
"codegen": "yul",
"remappings": [
"@/=src/",
"@openzeppelin-contracts-4.9.6/=dependencies/@openzeppelin-contracts-4.9.6/",
"@openzeppelin-contracts-5.3.0/=dependencies/@openzeppelin-contracts-5.3.0/",
"@openzeppelin/contracts-v4/=dependencies/openzeppelin-contracts/contracts/",
"@zksync-contracts-28.0.1/=dependencies/@zksync-contracts-28.0.1/",
"era-contracts/=dependencies/@zksync-contracts-28.0.1/contracts/",
"forge-std-1.9.7/=dependencies/forge-std-1.9.7/",
"solady-0.1.24/=dependencies/solady-0.1.24/",
"@openzeppelin/contracts/=dependencies/@zksync-contracts-28.0.1/lib/openzeppelin-contracts/contracts/",
"ds-test/=dependencies/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=dependencies/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=dependencies/openzeppelin-contracts/lib/forge-std/src/",
"openzeppelin-contracts/=dependencies/openzeppelin-contracts/",
"openzeppelin/=dependencies/openzeppelin-contracts/contracts/"
],
"evmVersion": "prague",
"outputSelection": {
"*": {
"*": [
"abi"
]
}
},
"optimizer": {
"enabled": true,
"mode": "3",
"size_fallback": false,
"disable_system_request_memoization": true
},
"metadata": {},
"libraries": {},
"enableEraVMExtensions": false,
"forceEVMLA": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"_paymentToken","type":"address"},{"internalType":"address","name":"_signer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ExceedsMaxQuantity","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"ReservationExpired","type":"error"},{"inputs":[],"name":"ReservationUsed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"reservationId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"quantity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pricePerUnit","type":"uint256"}],"name":"Purchase","type":"event"},{"inputs":[],"name":"PURCHASE_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"reservationId","type":"bytes32"},{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"maxQuantity","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"pricePerUnit","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"processPayment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"reservations","outputs":[{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint16","name":"quantity","type":"uint16"},{"internalType":"uint80","name":"amount","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_isPaused","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
9c4d535b0000000000000000000000000000000000000000000000000000000000000000010001d9a7a5aad1928ef41848b631575ae238c1ace425be128bcbd25e82cedc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000600000000000000000000000003e18b7cfc3955f5edfeb03ba04d78b4c9c8050bb00000000000000000000000084a71ccd554cc1b02749b35d22f684cc8ec987e1000000000000000000000000f0b272184dab47945ec2ace865db66cd6ba20cb7
Deployed Bytecode
0x0001000000000002000d00000000000200000000000103550000006003100270000001770330019700000001002001900000002c0000c13d0000008002000039000000400020043f000000040030008c000005890000413d000000000201043b000000e002200270000001870020009c0000007a0000a13d000001880020009c000000870000a13d000001890020009c0000011f0000a13d0000018a0020009c0000013e0000613d0000018b0020009c0000016c0000613d0000018c0020009c000005890000c13d000000240030008c000005890000413d0000000002000416000000000002004b000005890000c13d0000000401100370000000000101043b0000017a0010009c000005890000213d0000019f020000410000000c0020043f000000000010043f0000000c01000039000000200200003905d605b70000040f000000000101041a000000800010043f000001a001000041000005d70001042e0000012004000039000000400040043f0000000002000416000000000002004b000005890000c13d0000001f0230003900000178022001970000012002200039000000400020043f0000001f0530018f000001790630019800000120026000390000003e0000613d000000000701034f000000007807043c0000000004840436000000000024004b0000003a0000c13d000000000005004b0000004b0000613d000000000161034f0000000304500210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f0000000000120435000000600030008c000005890000413d000001200300043d0000017a0030009c000005890000213d000001400100043d0000017a0010009c000005890000213d000001600200043d0000017a0020009c000005890000213d000d00000003001d000b00000002001d000c00000001001d0000000001000410000000800010043f0000017b0100004100000000001004430000000001000414000001770010009c0000017701008041000000c0011002100000017c011001c70000800b0200003905d605d10000040f00000001002001900000058b0000613d000000000401043b000000a00040043f000000400100043d0000017d0010009c000000740000213d0000004002100039000000400020043f000000150200003900000000022104360000017e030000410000000000320435000000400500043d0000017d0050009c000002b10000a13d000001ba01000041000000000010043f0000004101000039000000040010043f000001bb01000041000005d800010430000001940020009c000000c40000213d0000019a0020009c000000fd0000213d0000019d0020009c0000017d0000613d0000019e0020009c000005890000c13d0000000001000416000000000001004b000005890000c13d000000000100041a0000013a0000013d0000018f0020009c000000e20000213d000001920020009c0000012e0000613d000001930020009c000005890000c13d0000000001000416000000000001004b000005890000c13d0000001501000039000000800010043f0000017e02000041000000a00020043f0000010002000039000000400020043f0000000102000039000000c00020043f0000017f03000041000000e00030043f000001c803000041000001000030043f000000e003000039000001200030043f000001e00010043f000002000100043d000001c9011001970000017e011001c7000002000010043f000002150000043f0000012001000039000001400010043f000002200020043f000002400100043d000001ca011001970000017f011001c7000002400010043f000002410000043f0000017b0100004100000000001004430000000001000414000001770010009c0000017701008041000000c0011002100000017c011001c70000800b0200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000001600010043f0000000001000410000001800010043f000001a00000043f0000016001000039000001c00010043f000000600100043d000002600010043f000000000001004b000003340000c13d00000180010000390000033e0000013d000001950020009c000001060000213d000001980020009c000001950000613d000001990020009c000005890000c13d0000019f010000410000000c0010043f0000000001000411000000000010043f0000000001000414000001770010009c0000017701008041000000c001100210000001a3011001c7000080100200003905d605d10000040f0000000100200190000005890000613d000000000101043b000000000001041b0000000001000414000001770010009c0000017701008041000000c00110021000000180011001c70000800d020000390000000203000039000001cc04000041000002400000013d000001900020009c000001350000613d000001910020009c000005890000c13d000000240030008c000005890000413d0000000002000416000000000002004b000005890000c13d0000000401100370000000000101043b000000000010043f0000000201000039000000200010043f0000004002000039000000000100001905d605b70000040f000000000101041a0000017a02100197000000800020043f000000a0021002700000ffff0220018f000000a00020043f000000b001100270000000c00010043f000001c701000041000005d70001042e0000019b0020009c0000021a0000613d0000019c0020009c000005890000c13d0000000001000416000000000001004b000005890000c13d0000000101000039000001390000013d000001960020009c000002460000613d000001970020009c000005890000c13d0000018301000041000000000501041a0000000001000411000000000051004b000002ad0000c13d0000000001000414000001770010009c0000017701008041000000c00110021000000180011001c70000800d0200003900000003030000390000018404000041000000000600001905d605cc0000040f0000000100200190000005890000613d0000018301000041000000000001041b0000000001000019000005d70001042e0000018d0020009c000002570000613d0000018e0020009c000005890000c13d0000000001000416000000000001004b000005890000c13d0000000101000039000000000101041a000001a6001001980000000001000039000000010100c039000000800010043f000001a001000041000005d70001042e0000000001000416000000000001004b000005890000c13d000001ad01000041000000800010043f000001a001000041000005d70001042e0000000001000416000000000001004b000005890000c13d0000018301000041000000000101041a0000017a01100197000000800010043f000001a001000041000005d70001042e000000240030008c000005890000413d0000000401100370000000000301043b0000017a0030009c000005890000213d0000018301000041000000000101041a0000000002000411000000000012004b000002ad0000c13d0000019f010000410000000c0010043f000000000030043f0000000001000414000001770010009c0000017701008041000000c001100210000001a3011001c70000801002000039000d00000003001d05d605d10000040f0000000100200190000005890000613d000000000101043b000b00000001001d000000000101041a000c00000001001d000001a40100004100000000001004430000000001000414000001770010009c0000017701008041000000c0011002100000017c011001c70000800b0200003905d605d10000040f00000001002001900000058b0000613d000000000101043b0000000c0010006c000003470000a13d000001a501000041000000000010043f000001a201000041000005d800010430000000240030008c000005890000413d0000000401100370000000000101043b0000017a0010009c000005890000213d0000018302000041000000000202041a0000000003000411000000000023004b000002ad0000c13d000000000001004b0000034a0000c13d000001a101000041000000000010043f000001a201000041000005d800010430000000240030008c000005890000413d0000000002000416000000000002004b000005890000c13d0000000401100370000000000201043b000000000002004b0000000001000039000000010100c039000000000012004b000005890000c13d000d00000002001d05d605960000040f0000000d0000006b0000000001000019000001d50100c0410000000102000039000000000302041a000001d603300197000000000113019f000000000012041b0000000001000019000005d70001042e000000240030008c000005890000413d0000000002000416000000000002004b000005890000c13d0000000401100370000000000201043b0000017a0020009c000005890000213d0000018301000041000000000301041a0000000001000411000000000031004b000002ad0000c13d000c00000003001d000001ce01000041000000000010043f0000000001000410000000200010043f0000000001000414000001770010009c0000017701008041000000c001100210000001cf011001c7000d00000002001d05d605d10000040f00000060031002700000017703300197000000200030008c000000200400003900000000040340190000001f0540018f00000020064001900000003404600039000001be0000613d0000003407000039000000000801034f000000008908043c0000000007970436000000000047004b000001ba0000c13d000000000005004b000001cb0000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001404350000001f0030008c00000000010000390000000101002039000000000112016f0000000100100190000003430000613d0000000c01000029000000140010043f000001d001000041000000000010043f0000000001000414000001770010009c0000017701008041000000c001100210000001d1011001c70000000d0200002905d605cc0000040f00000060031002700000017707300197000000200070008c000000200400003900000000040740190000001f0340018f0000002008400190000001ea0000613d000000000401034f0000000005000019000000004604043c0000000005650436000000000085004b000001e60000c13d000000000003004b000001f70000613d000000000181034f0000000303300210000000000408043300000000043401cf000000000434022f000000000101043b0000010003300089000000000131022f00000000013101cf000000000141019f0000000000180435000000000100043d000000010010008c00000000010000390000000101006039000000000112016f0000000100100190000002170000c13d000b00000002001d000c00000007001d000001b30100004100000000001004430000000d0100002900000004001004430000000001000414000001770010009c0000017701008041000000c001100210000001b4011001c7000080020200003905d605d10000040f00000001002001900000058b0000613d000000010200008a0000000b0220014f000000000101043b000000000001004b000000000100003900000001010060390000000c001001b0000000010220c1bf0000000100200190000003430000c13d000000340000043f0000000001000019000005d70001042e0000019f010000410000000c0010043f0000000001000411000000000010043f000001a40100004100000000001004430000000001000414000001770010009c0000017701008041000000c0011002100000017c011001c70000800b0200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000d00000001001d0000000001000414000001770010009c0000017701008041000000c001100210000001a3011001c7000080100200003905d605d10000040f0000000100200190000005890000613d000000000101043b0000000d02000029000001d30220009a000000000021041b0000000001000414000001770010009c0000017701008041000000c00110021000000180011001c70000800d020000390000000203000039000001d404000041000000000500041105d605cc0000040f0000000100200190000005890000613d0000000001000019000005d70001042e000000240030008c000005890000413d0000000002000416000000000002004b000005890000c13d0000000401100370000000000101043b0000017a0010009c000005890000213d000d00000001001d05d605960000040f000000000100041a00000185011001970000000d011001af000000000010041b0000000001000019000005d70001042e000000e40030008c000005890000413d0000000002000416000000000002004b000005890000c13d0000000402100370000000000402043b0000002402100370000000000702043b0000017a0070009c000005890000213d000000a402100370000000000502043b0000008402100370000000000c02043b0000006402100370000000000b02043b0000004402100370000000000602043b000000c402100370000000000902043b000001a70090009c000005890000213d0000002302900039000000000032004b000005890000813d000000040a9000390000000001a1034f000000000201043b000001a70020009c000005890000213d00000024089000390000000001820019000000000031004b000005890000213d0000000101000039000000000101041a000001a6001001980000034d0000c13d000000000040043f0000000201000039000000200010043f0000000001000414000001770010009c0000017701008041000000c001100210000001aa011001c7000800000002001d0000801002000039000900000004001d000a00000005001d000b00000006001d000400000007001d000700000008001d000600000009001d00050000000a001d000c0000000b001d000d0000000c001d05d605d10000040f0000000100200190000005890000613d000000000101043b000000000101041a0000017a00100198000003510000c13d000001a40100004100000000001004430000000001000414000001770010009c0000017701008041000000c0011002100000017c011001c70000800b0200003905d605d10000040f00000001002001900000058b0000613d000000000101043b0000000d0010006c000003550000813d0000000c020000290000000b0020006b000003590000a13d000001c601000041000000000010043f000001a901000041000005d800010430000001cd01000041000000000010043f000001a201000041000005d800010430000800000004001d0000004003500039000000400030043f0000000103000039000a00000005001d00000000043504360000017f03000041000900000004001d0000000000340435000001770020009c000001770200804100000040022002100000000001010433000001770010009c00000177010080410000006001100210000000000121019f0000000002000414000001770020009c0000017702008041000000c002200210000000000112019f00000180011001c7000080100200003905d605d10000040f0000000100200190000005890000613d0000000902000029000001770020009c000001770200804100000040022002100000000a030000290000000003030433000001770030009c00000177030080410000006003300210000000000223019f000000000101043b000a00000001001d0000000001000414000001770010009c0000017701008041000000c001100210000000000121019f00000180011001c7000080100200003905d605d10000040f0000000100200190000005890000613d000000000101043b0000000a05000029000000c00050043f000000e00010043f000000400200043d000000800320003900000000040004100000000000430435000000600320003900000008040000290000000000430435000000400320003900000000001304350000002001200039000000000051043500000181010000410000000000120435000001770020009c000001770200804100000040012002100000000002000414000001770020009c0000017702008041000000c002200210000000000121019f00000182011001c7000080100200003905d605d10000040f0000000d060000290000000100200190000005890000613d000000000101043b000001000010043f0000018301000041000000000061041b0000000001000414000001770010009c0000017701008041000000c00110021000000180011001c70000800d0200003900000003030000390000018404000041000000000500001905d605cc0000040f0000000b0300002900000001002001900000000c02000029000005890000613d0000000104000039000000000104041a0000018501100197000000000121019f000000000014041b000000000100041a0000018501100197000000000131019f000000000010041b000000800100043d000001400000044300000160001004430000002001000039000000a00200043d0000018000100443000001a0002004430000004002000039000000c00300043d000001c000200443000001e0003004430000006002000039000000e00300043d000002000020044300000220003004430000008002000039000001000300043d000002400020044300000260003004430000010000100443000000050100003900000120001004430000018601000041000005d70001042e0000028003000039000000000200001900000080050000390000000004030019000000005305043400000000033404360000000102200039000000000012004b000003370000413d000000e00140008a000001770010009c00000177010080410000006001100210000001cb011001c7000005d70001042e000001d201000041000000000010043f000001a201000041000005d8000104300000000b01000029000000000001041b0000000d0100002905d605a00000040f0000000001000019000005d70001042e000001a801000041000000000010043f000001a901000041000005d800010430000001ab01000041000000000010043f000001a901000041000005d800010430000001ac01000041000000000010043f000001a901000041000005d800010430000000400100043d000000a0021000390000000a03000029000000000032043500000080021000390000000d03000029000000000032043500000060021000390000000c030000290000000000320435000000400210003900000009030000290000000000320435000000a0020000390000000002210436000001ad030000410000000000320435000001ae0010009c000000740000213d000000c003100039000000400030043f000001770020009c000001770200804100000040022002100000000001010433000001770010009c00000177010080410000006001100210000000000121019f0000000002000414000001770020009c0000017702008041000000c002200210000000000112019f00000180011001c7000080100200003905d605d10000040f00000001002001900000008002000039000005890000613d000000000101043b000d00000001001d000001af0100004100000000001004430000000001000412000000040010044300000024002004430000000001000414000001770010009c0000017701008041000000c001100210000001b0011001c7000080050200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000c00000001001d000001af0100004100000000001004430000000001000412000000040010044300000024000004430000000001000414000001770010009c0000017701008041000000c001100210000001b0011001c7000080050200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000300000001001d000001af01000041000000000010044300000000010004120000000400100443000000200100003900000024001004430000000001000414000001770010009c0000017701008041000000c001100210000001b0011001c7000080050200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000200000001001d0000017b0100004100000000001004430000000001000414000001770010009c0000017701008041000000c0011002100000017c011001c70000800b0200003905d605d10000040f00000001002001900000058b0000613d0000000002000410000000000101043b000100000001001d000000030020006c000003ee0000c13d0000000202000029000000010020006b000003ee0000c13d000001b101000041000000000010043f0000000c010000290000001a0010043f0000000d010000290000003a0010043f0000000001000414000001770010009c0000017701008041000000c001100210000001b2011001c7000080100200003905d605d10000040f0000000100200190000005890000613d000000000101043b000c00000001001d0000003a0000043f000000000100041a000300000001001d000d017a0010019c0000042e0000c13d000000400100043d0000004402100039000001c3030000410000000000320435000000240210003900000011030000390000000000320435000001c4020000410000000000210435000000040210003900000020030000390000000000320435000001770010009c00000177010080410000004001100210000001c5011001c7000005d800010430000000400200043d0000018101000041000300000002001d0000000001120436000c00000001001d000001af01000041000000000010044300000000010004120000000400100443000000400100003900000024001004430000000001000414000001770010009c0000017701008041000000c001100210000001b0011001c7000080050200003905d605d10000040f00000001002001900000058b0000613d000000000101043b0000000c020000290000000000120435000001af01000041000000000010044300000000010004120000000400100443000000600100003900000024001004430000000001000414000001770010009c0000017701008041000000c001100210000001b0011001c7000080050200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000000030400002900000080024000390000000003000410000000000032043500000060024000390000000103000029000000000032043500000040024000390000000000120435000001770040009c000001770400804100000040014002100000000002000414000001770020009c0000017702008041000000c002200210000000000121019f00000182011001c7000080100200003905d605d10000040f0000000100200190000005890000613d000000000101043b000c00000001001d000003c70000013d000000400100043d000200000001001d000001b30100004100000000001004430000000d0100002900000004001004430000000001000414000001770010009c0000017701008041000000c001100210000001b4011001c7000080020200003905d605d10000040f00000001002001900000058b0000613d000000000101043b000000000001004b000004540000c13d0000000801000029000000400010008c000004af0000613d0000000801000029000000410010008c000003dd0000c13d000000060100002900000064011000390000000001100367000000000101043b000000f801100270000000200010043f000000070100002900000000011003670000004002000039000000001301043c0000000002320436000000800020008c0000044f0000c13d000004bc0000013d00000002050000290000006402500039000000440150003900000024065000390000000403500039000001b50400004100000000004504350000000c0400002900000000004304350000004003000039000c00000006001d000000000036043500000008040000290000000000410435000001d7034001980000001f0440018f0000000001320019000000070500002900000000055003670000046d0000613d000000000605034f000000006706043c0000000002720436000000000012004b000004690000c13d000000000004004b0000047a0000613d000000000235034f0000000303400210000000000401043300000000043401cf000000000434022f000000000202043b0000010003300089000000000232022f00000000023201cf000000000242019f00000000002104350000000201000029000001770010009c000001770100804100000040011002100000000802000029000001b60020009c000001b6020080410000006002200210000000000112019f0000000002000414000001770020009c0000017702008041000000c002200210000000000121019f000001b70110009a0000000d0200002905d605d10000040f00000060031002700000017703300197000000200030008c00000020030080390000001f0430018f00000020053001900000000c03500029000004990000613d000000000601034f0000000c07000029000000006806043c0000000007870436000000000037004b000004950000c13d000000000004004b000004a60000613d000000000151034f0000000304400210000000000503043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f00000000001304350000000c010000290000000001010433000001b50010009c00000000010000390000000101006039000000000112016f0000000100100190000003dd0000613d000004ea0000013d000000050400002900000040014000390000000001100367000000000101043b000000ff031002700000001b03300039000000200030043f00000020034000390000000002300367000000000202043b000000400020043f000001b801100197000000600010043f0000000c01000029000000000010043f0000000001000414000001770010009c0000017701008041000000c001100210000001b9011001c7000000010200003905d605d10000040f000000010900003900000060031002700000017703300197000000200030008c000000200400003900000000040340190000001f0540018f000000200640019000000001046001bf000004d40000613d000000000701034f000000007807043c0000000009890436000000000049004b000004d00000c13d000000010220018f000000000005004b000004e20000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001404350000000001020433000000600000043f0000000202000029000000400020043f000000030110014f0000006001100210000000000013004b000003dd0000a13d0000000b02000029000d000a002000bd000000000002004b000004f20000613d0000000d020000290000000b012000fa0000000a0010006b0000058c0000c13d000000400100043d000c00000001001d000001bc0010009c000000740000213d00000004010000290000017a021001970000000c030000290000006001300039000000400010043f0000000d01000029000001bd011001970000004004300039000800000004001d0000000000140435000600000002001d00000000022304360000000b010000290000ffff0110018f000700000002001d00000000001204350000000901000029000000000010043f0000000201000039000000200010043f0000000001000414000001770010009c0000017701008041000000c001100210000001aa011001c7000080100200003905d605d10000040f0000000100200190000005890000613d00000008020000290000000002020433000000b00220021000000007030000290000000003030433000000a003300210000001be03300197000000000223019f0000000c0300002900000000030304330000017a03300197000000000232019f000000000101043b000000000021041b0000000101000039000000000101041a000000400200043d000c00000002001d0000000d02000029000000600020043f0000000002000410000000400020043f000000000200041100000060022002100000002c0020043f000001bf020000410000000c0020043f00000000030004140000017a02100197000001770030009c0000017703008041000000c001300210000001c0011001c7000800000002001d05d605cc0000040f000d00000002001d00000060021002700000017702200197000000200020008c000700000002001d00000020020080390000001f0320018f0000002002200190000005450000613d000000000401034f0000000005000019000000004604043c0000000005650436000000000025004b000005410000c13d000000000003004b000005520000613d000000000121034f0000000303300210000000000402043300000000043401cf000000000434022f000000000101043b0000010003300089000000000131022f00000000013101cf000000000141019f0000000000120435000000000100043d000000010010008c000000000100003900000001010060390000000d0110017f0000000100100190000005700000c13d000001b3010000410000000000100443000000080100002900000004001004430000000001000414000001770010009c0000017701008041000000c001100210000001b4011001c7000080020200003905d605d10000040f00000001002001900000058b0000613d000000010200008a0000000d0220014f000000000101043b000000000001004b0000000001000039000000010100603900000007001001b0000000010220c1bf0000000100200190000005920000c13d000000600000043f0000000c03000029000000400030043f00000020013000390000000a0200002900000000002104350000000b010000290000000000130435000001770030009c000001770300804100000040013002100000000002000414000001770020009c0000017702008041000000c002200210000000000112019f000001aa011001c70000800d020000390000000303000039000001c2040000410000000605000029000000090600002905d605cc0000040f0000000100200190000002440000c13d0000000001000019000005d800010430000000000001042f000001ba01000041000000000010043f0000001101000039000000040010043f000001bb01000041000005d800010430000001c101000041000000000010043f000001a201000041000005d8000104300000018301000041000000000101041a0000000002000411000000000012004b0000059c0000c13d000000000001042d000001cd01000041000000000010043f000001a201000041000005d80001043000010000000000020000018302000041000000000502041a00000000020004140000017a06100197000001770020009c0000017702008041000000c00120021000000180011001c70000800d0200003900000003030000390000018404000041000100000006001d05d605cc0000040f0000000100200190000005b40000613d00000183010000410000000102000029000000000021041b000000000001042d0000000001000019000005d800010430000000000001042f000001770010009c00000177010080410000004001100210000001770020009c00000177020080410000006002200210000000000112019f0000000002000414000001770020009c0000017702008041000000c002200210000000000112019f00000180011001c7000080100200003905d605d10000040f0000000100200190000005ca0000613d000000000101043b000000000001042d0000000001000019000005d800010430000005cf002104210000000102000039000000000001042d0000000002000019000000000001042d000005d4002104230000000102000039000000000001042d0000000002000019000000000001042d000005d600000432000005d70001042e000005d80001043000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000000000000000000000000000000000000000000000000001ffffffe000000000000000000000000000000000000000000000000000000000ffffffe0000000000000000000000000ffffffffffffffffffffffffffffffffffffffff9a8a0592ac89c5ad3bc6df8224c17b485976f597df104ee20d0df415241f670b0200000200000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffbf4f6e636861696e5061796d656e7448616e646c65720000000000000000000000310000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f02000000000000000000000000000000000000a0000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0ffffffffffffffffffffffff00000000000000000000000000000000000000000000000200000000000000000000000000000180000001000000000000000000000000000000000000000000000000000000000000000000000000007eb6f6e000000000000000000000000000000000000000000000000000000000aa56cf0a00000000000000000000000000000000000000000000000000000000f04e283d00000000000000000000000000000000000000000000000000000000f04e283e00000000000000000000000000000000000000000000000000000000f2fde38b00000000000000000000000000000000000000000000000000000000fee81cf400000000000000000000000000000000000000000000000000000000aa56cf0b00000000000000000000000000000000000000000000000000000000b187bd26000000000000000000000000000000000000000000000000000000008da5cb5a000000000000000000000000000000000000000000000000000000008da5cb5b00000000000000000000000000000000000000000000000000000000a1b416f9000000000000000000000000000000000000000000000000000000007eb6f6e10000000000000000000000000000000000000000000000000000000084b0196e0000000000000000000000000000000000000000000000000000000051cff8d8000000000000000000000000000000000000000000000000000000006c19e782000000000000000000000000000000000000000000000000000000006c19e78300000000000000000000000000000000000000000000000000000000715018a60000000000000000000000000000000000000000000000000000000051cff8d90000000000000000000000000000000000000000000000000000000054d1f13d00000000000000000000000000000000000000000000000000000000256929610000000000000000000000000000000000000000000000000000000025692962000000000000000000000000000000000000000000000000000000003013ce290000000000000000000000000000000000000000000000000000000016c38b3c00000000000000000000000000000000000000000000000000000000238ac93300000000000000000000000000000000000000000000000000000000389a75e10000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000007448fbae00000000000000000000000000000000000000040000001c000000000000000002000000000000000000000000000000000000200000000c0000000000000000796b89b91644bc98cd93958e4c9038275d622183e25ac5af08cc6b5d95539132000000000000000000000000000000000000000000000000000000006f5e88180000000000000000000000ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff9e87fac800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000002000000000000000000000000000000000000400000000000000000000000009788df3100000000000000000000000000000000000000000000000000000000443df96f000000000000000000000000000000000000000000000000000000005995564d962001b477987327e72c2ea2ece6e78d0f93891a33628569c90d8274000000000000000000000000000000000000000000000000ffffffffffffff3f310ab089e4439a4c15d089f94afb7896ff553aecb10793d0ab882de59d99a32e0200000200000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000000000000190100000000000002000000000000000000000000000000000000420000001800000000000000001806aa1896bbf26568e884a7374b41e002500962caba6a15023a8d90e8508b8302000002000000000000000000000000000000240000000000000000000000001626ba7e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff9bffffffffffffffffffffffffffffffffffffff9c0000000000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000800000000000000000000000004e487b71000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff9f00000000000000000000000000000000000000000000ffffffffffffffffffff00000000000000000000ffff00000000000000000000000000000000000000000000000000000000000000000000000023b872dd00000000000000000000000000000000000000000000000000000000000000640000001c0000000000000000000000000000000000000000000000000000000000000000000000007939f4249b429a8fe2296e0c55b003fb026b35c55fc68cea615f6fb9d533ec800634dd4b496e76616c6964207369676e617475726500000000000000000000000000000008c379a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000d76288d70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000008000000000000000000f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000001000000000000000000fa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c920000000000000000000000000000000000000000000000000000000082b429000000000000000000000000000000000000000000000000000000000070a0823100000000000000000000000000000000000000240000001c000000000000000000000000000000000000000000000000a9059cbb00000000000000000000000000000000000000000000000000000000000000440000001000000000000000000000000000000000000000000000000000000000000000000000000090b8ec18fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd5d00dbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d0000000000000000000000010000000000000000000000000000000000000000ffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0c0ec6c955ade696f2b6ecc0d74aec887fef8c100781c29c8521eeb58adda92df
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000003e18b7cfc3955f5edfeb03ba04d78b4c9c8050bb00000000000000000000000084a71ccd554cc1b02749b35d22f684cc8ec987e1000000000000000000000000f0b272184dab47945ec2ace865db66cd6ba20cb7
-----Decoded View---------------
Arg [0] : owner (address): 0x3e18b7CFc3955f5edfeB03Ba04D78b4c9C8050bb
Arg [1] : _paymentToken (address): 0x84A71ccD554Cc1b02749b35d22F684CC8ec987e1
Arg [2] : _signer (address): 0xF0b272184dAb47945eC2AcE865db66CD6ba20CB7
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000003e18b7cfc3955f5edfeb03ba04d78b4c9c8050bb
Arg [1] : 00000000000000000000000084a71ccd554cc1b02749b35d22f684cc8ec987e1
Arg [2] : 000000000000000000000000f0b272184dab47945ec2ace865db66cd6ba20cb7
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.26
Net Worth in ETH
0.000111
Token Allocations
ETH
100.00%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ABSTRACT | 100.00% | $2,360.86 | 0.00011 | $0.2596 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.