Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 25 from a total of 44 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Rescue Funds | 20141814 | 610 days ago | IN | 0 ETH | 0.00025592 | ||||
| Claim | 20060651 | 622 days ago | IN | 0 ETH | 0.00026066 | ||||
| Claim | 20031440 | 626 days ago | IN | 0 ETH | 0.00133402 | ||||
| Claim | 19841287 | 652 days ago | IN | 0 ETH | 0.00105144 | ||||
| Claim | 19781245 | 661 days ago | IN | 0 ETH | 0.00063239 | ||||
| Claim | 19781032 | 661 days ago | IN | 0 ETH | 0.00032075 | ||||
| Claim | 19781031 | 661 days ago | IN | 0 ETH | 0.00031775 | ||||
| Claim | 19781030 | 661 days ago | IN | 0 ETH | 0.00041727 | ||||
| Claim | 19752761 | 665 days ago | IN | 0 ETH | 0.00044291 | ||||
| Claim | 19748680 | 665 days ago | IN | 0 ETH | 0.00025478 | ||||
| Claim | 19746939 | 665 days ago | IN | 0 ETH | 0.00031553 | ||||
| Claim | 19746937 | 665 days ago | IN | 0 ETH | 0.00033776 | ||||
| Claim | 19744704 | 666 days ago | IN | 0 ETH | 0.00030461 | ||||
| Claim | 19734570 | 667 days ago | IN | 0 ETH | 0.00046579 | ||||
| Claim | 19734554 | 667 days ago | IN | 0 ETH | 0.00044733 | ||||
| Claim | 19734546 | 667 days ago | IN | 0 ETH | 0.00068304 | ||||
| Claim | 19734350 | 667 days ago | IN | 0 ETH | 0.00074552 | ||||
| Claim | 19734349 | 667 days ago | IN | 0 ETH | 0.00076453 | ||||
| Claim | 19734348 | 667 days ago | IN | 0 ETH | 0.00072772 | ||||
| Claim | 19734346 | 667 days ago | IN | 0 ETH | 0.00074118 | ||||
| Claim | 19734345 | 667 days ago | IN | 0 ETH | 0.00067498 | ||||
| Claim | 19734329 | 667 days ago | IN | 0 ETH | 0.00072747 | ||||
| Claim | 19734324 | 667 days ago | IN | 0 ETH | 0.00063828 | ||||
| Claim | 19734318 | 667 days ago | IN | 0 ETH | 0.00077728 | ||||
| Claim | 19734316 | 667 days ago | IN | 0 ETH | 0.00087492 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
SignatureMerkleDrop128
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { SafeERC20, IERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
import { ECDSA } from "@1inch/solidity-utils/contracts/libraries/ECDSA.sol";
import { ISignatureMerkleDrop128 } from "./interfaces/ISignatureMerkleDrop128.sol";
contract SignatureMerkleDrop128 is ISignatureMerkleDrop128, Ownable {
using Address for address payable;
using SafeERC20 for IERC20;
/* solhint-disable immutable-vars-naming */
address public immutable override token;
bytes16 public immutable override merkleRoot;
uint256 public immutable override depth;
/* solhint-enable immutable-vars-naming */
// This is a packed array of booleans.
mapping(uint256 => uint256) private _claimedBitMap;
uint256 private constant _CLAIM_GAS_COST = 60000;
receive() external payable {} // solhint-disable-line no-empty-blocks
constructor(address token_, bytes16 merkleRoot_, uint256 depth_) Ownable(msg.sender) {
token = token_;
merkleRoot = merkleRoot_;
depth = depth_;
}
function claim(address receiver, uint256 amount, bytes calldata merkleProof, bytes calldata signature) external override {
bytes32 signedHash = ECDSA.toEthSignedMessageHash(keccak256(abi.encodePacked(receiver)));
address account = ECDSA.recover(signedHash, signature);
// Verify the merkle proof.
bytes16 node = bytes16(keccak256(abi.encodePacked(account, amount)));
(bool valid, uint256 index) = _verifyAsm(merkleProof, merkleRoot, node);
if (!valid) revert InvalidProof();
_invalidate(index);
IERC20(token).safeTransfer(receiver, amount);
_cashback();
}
function verify(bytes calldata proof, bytes16 root, bytes16 leaf) external view returns (bool valid, uint256 index) {
return _verifyAsm(proof, root, leaf);
}
function verify(bytes calldata proof, bytes16 leaf) external view returns (bool valid, uint256 index) {
return _verifyAsm(proof, merkleRoot, leaf);
}
function isClaimed(uint256 index) external view override returns (bool) {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
uint256 claimedWord = _claimedBitMap[claimedWordIndex];
uint256 mask = (1 << claimedBitIndex);
return claimedWord & mask == mask;
}
function _cashback() private {
uint256 balance = address(this).balance;
if (balance > 0) {
// solhint-disable-next-line avoid-tx-origin
payable(tx.origin).sendValue(Math.min(block.basefee * _CLAIM_GAS_COST, balance));
}
}
function _invalidate(uint256 index) private {
uint256 claimedWordIndex = index >> 8;
uint256 claimedBitIndex = index & 0xff;
uint256 claimedWord = _claimedBitMap[claimedWordIndex];
uint256 newClaimedWord = claimedWord | (1 << claimedBitIndex);
if (claimedWord == newClaimedWord) revert DropAlreadyClaimed();
_claimedBitMap[claimedWordIndex] = newClaimedWord;
}
function _verifyAsm(bytes calldata proof, bytes16 root, bytes16 leaf) private view returns (bool valid, uint256 index) {
/// @solidity memory-safe-assembly
assembly { // solhint-disable-line no-inline-assembly
let ptr := proof.offset
let mask := 1
for { let end := add(ptr, proof.length) } lt(ptr, end) { ptr := add(ptr, 0x10) } {
let node := calldataload(ptr)
switch lt(leaf, node)
case 1 {
mstore(0x00, leaf)
mstore(0x10, node)
}
default {
mstore(0x00, node)
mstore(0x10, leaf)
index := or(mask, index)
}
leaf := keccak256(0x00, 0x20)
mask := shl(1, mask)
}
valid := iszero(shr(128, xor(root, leaf)))
}
unchecked {
index <<= depth - proof.length / 16;
}
}
function rescueFunds(address token_, uint256 amount) external onlyOwner {
if (token_ == address(0)) {
payable(msg.sender).sendValue(amount);
} else {
IERC20(token_).safeTransfer(msg.sender, amount);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title IDaiLikePermit
* @dev Interface for Dai-like permit function allowing token spending via signatures.
*/
interface IDaiLikePermit {
/**
* @notice Approves spending of tokens via off-chain signatures.
* @param holder Token holder's address.
* @param spender Spender's address.
* @param nonce Current nonce of the holder.
* @param expiry Time when the permit expires.
* @param allowed True to allow, false to disallow spending.
* @param v, r, s Signature components.
*/
function permit(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title IERC7597Permit
* @dev A new extension for ERC-2612 permit, which has already been added to USDC v2.2.
*/
interface IERC7597Permit {
/**
* @notice Update allowance with a signed permit.
* @dev Signature bytes can be used for both EOA wallets and contract wallets.
* @param owner Token owner's address (Authorizer).
* @param spender Spender's address.
* @param value Amount of allowance.
* @param deadline The time at which the signature expires (unixtime).
* @param signature Unstructured bytes signature signed by an EOA wallet or a contract wallet.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
bytes memory signature
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title IPermit2
* @dev Interface for a flexible permit system that extends ERC20 tokens to support permits in tokens lacking native permit functionality.
*/
interface IPermit2 {
/**
* @dev Struct for holding permit details.
* @param token ERC20 token address for which the permit is issued.
* @param amount The maximum amount allowed to spend.
* @param expiration Timestamp until which the permit is valid.
* @param nonce An incrementing value for each signature, unique per owner, token, and spender.
*/
struct PermitDetails {
address token;
uint160 amount;
uint48 expiration;
uint48 nonce;
}
/**
* @dev Struct for a single token allowance permit.
* @param details Permit details including token, amount, expiration, and nonce.
* @param spender Address authorized to spend the tokens.
* @param sigDeadline Deadline for the permit signature, ensuring timeliness of the permit.
*/
struct PermitSingle {
PermitDetails details;
address spender;
uint256 sigDeadline;
}
/**
* @dev Struct for packed allowance data to optimize storage.
* @param amount Amount allowed.
* @param expiration Permission expiry timestamp.
* @param nonce Unique incrementing value for tracking allowances.
*/
struct PackedAllowance {
uint160 amount;
uint48 expiration;
uint48 nonce;
}
/**
* @notice Executes a token transfer from one address to another.
* @param user The token owner's address.
* @param spender The address authorized to spend the tokens.
* @param amount The amount of tokens to transfer.
* @param token The address of the token being transferred.
*/
function transferFrom(address user, address spender, uint160 amount, address token) external;
/**
* @notice Issues a permit for spending tokens via a signed authorization.
* @param owner The token owner's address.
* @param permitSingle Struct containing the permit details.
* @param signature The signature proving the owner authorized the permit.
*/
function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
/**
* @notice Retrieves the allowance details between a token owner and spender.
* @param user The token owner's address.
* @param token The token address.
* @param spender The spender's address.
* @return The packed allowance details.
*/
function allowance(address user, address token, address spender) external view returns (PackedAllowance memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title IWETH
* @dev Interface for wrapper as WETH-like token.
*/
interface IWETH is IERC20 {
/**
* @notice Emitted when Ether is deposited to get wrapper tokens.
*/
event Deposit(address indexed dst, uint256 wad);
/**
* @notice Emitted when wrapper tokens is withdrawn as Ether.
*/
event Withdrawal(address indexed src, uint256 wad);
/**
* @notice Deposit Ether to get wrapper tokens.
*/
function deposit() external payable;
/**
* @notice Withdraw wrapped tokens as Ether.
* @param amount Amount of wrapped tokens to withdraw.
*/
function withdraw(uint256 amount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/interfaces/IERC1271.sol";
/**
* @title ECDSA signature operations
* @notice Provides functions for recovering addresses from signatures and verifying signatures, including support for EIP-2098 compact signatures.
*/
library ECDSA {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
uint256 private constant _S_BOUNDARY = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 + 1;
uint256 private constant _COMPACT_S_MASK = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
uint256 private constant _COMPACT_V_SHIFT = 255;
/**
* @notice Recovers the signer's address from the signature.
* @dev Recovers the address that has signed a hash with `(v, r, s)` signature.
* @param hash The keccak256 hash of the data signed.
* @param v The recovery byte of the signature.
* @param r The first 32 bytes of the signature.
* @param s The second 32 bytes of the signature.
* @return signer The address of the signer.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal view returns (address signer) {
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
if lt(s, _S_BOUNDARY) {
let ptr := mload(0x40)
mstore(ptr, hash)
mstore(add(ptr, 0x20), v)
mstore(add(ptr, 0x40), r)
mstore(add(ptr, 0x60), s)
mstore(0, 0)
pop(staticcall(gas(), 0x1, ptr, 0x80, 0, 0x20))
signer := mload(0)
}
}
}
/**
* @notice Recovers the signer's address from the signature using `r` and `vs` components.
* @dev Recovers the address that has signed a hash with `r` and `vs`, where `vs` combines `v` and `s`.
* @param hash The keccak256 hash of the data signed.
* @param r The first 32 bytes of the signature.
* @param vs The combined `v` and `s` values of the signature.
* @return signer The address of the signer.
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal view returns (address signer) {
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let s := and(vs, _COMPACT_S_MASK)
if lt(s, _S_BOUNDARY) {
let ptr := mload(0x40)
mstore(ptr, hash)
mstore(add(ptr, 0x20), add(27, shr(_COMPACT_V_SHIFT, vs)))
mstore(add(ptr, 0x40), r)
mstore(add(ptr, 0x60), s)
mstore(0, 0)
pop(staticcall(gas(), 0x1, ptr, 0x80, 0, 0x20))
signer := mload(0)
}
}
}
/**
* @notice Recovers the signer's address from a hash and a signature.
* @param hash The keccak256 hash of the signed data.
* @param signature The full signature from which the signer will be recovered.
* @return signer The address of the signer.
*/
/// @dev WARNING!!!
/// There is a known signature malleability issue with two representations of signatures!
/// Even though this function is able to verify both standard 65-byte and compact 64-byte EIP-2098 signatures
/// one should never use raw signatures for any kind of invalidation logic in their code.
/// As the standard and compact representations are interchangeable any invalidation logic that relies on
/// signature uniqueness will get rekt.
/// More info: https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-4h98-2769-gh6h
function recover(bytes32 hash, bytes calldata signature) internal view returns (address signer) {
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
// memory[ptr:ptr+0x80] = (hash, v, r, s)
switch signature.length
case 65 {
// memory[ptr+0x20:ptr+0x80] = (v, r, s)
mstore(add(ptr, 0x20), byte(0, calldataload(add(signature.offset, 0x40))))
calldatacopy(add(ptr, 0x40), signature.offset, 0x40)
}
case 64 {
// memory[ptr+0x20:ptr+0x80] = (v, r, s)
let vs := calldataload(add(signature.offset, 0x20))
mstore(add(ptr, 0x20), add(27, shr(_COMPACT_V_SHIFT, vs)))
calldatacopy(add(ptr, 0x40), signature.offset, 0x20)
mstore(add(ptr, 0x60), and(vs, _COMPACT_S_MASK))
}
default {
ptr := 0
}
if ptr {
if lt(mload(add(ptr, 0x60)), _S_BOUNDARY) {
// memory[ptr:ptr+0x20] = (hash)
mstore(ptr, hash)
mstore(0, 0)
pop(staticcall(gas(), 0x1, ptr, 0x80, 0, 0x20))
signer := mload(0)
}
}
}
}
/**
* @notice Verifies the signature for a hash, either by recovering the signer or using EIP-1271's `isValidSignature` function.
* @dev Attempts to recover the signer's address from the signature; if the address is non-zero, checks if it's valid according to EIP-1271.
* @param signer The address to validate the signature against.
* @param hash The hash of the signed data.
* @param signature The signature to verify.
* @return success True if the signature is verified, false otherwise.
*/
function recoverOrIsValidSignature(
address signer,
bytes32 hash,
bytes calldata signature
) internal view returns (bool success) {
if (signer == address(0)) return false;
if ((signature.length == 64 || signature.length == 65) && recover(hash, signature) == signer) {
return true;
}
return isValidSignature(signer, hash, signature);
}
/**
* @notice Verifies the signature for a hash, either by recovering the signer or using EIP-1271's `isValidSignature` function.
* @dev Attempts to recover the signer's address from the signature; if the address is non-zero, checks if it's valid according to EIP-1271.
* @param signer The address to validate the signature against.
* @param hash The hash of the signed data.
* @param v The recovery byte of the signature.
* @param r The first 32 bytes of the signature.
* @param s The second 32 bytes of the signature.
* @return success True if the signature is verified, false otherwise.
*/
function recoverOrIsValidSignature(
address signer,
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal view returns (bool success) {
if (signer == address(0)) return false;
if (recover(hash, v, r, s) == signer) {
return true;
}
return isValidSignature(signer, hash, v, r, s);
}
/**
* @notice Verifies the signature for a hash, either by recovering the signer or using EIP-1271's `isValidSignature` function.
* @dev Attempts to recover the signer's address from the signature; if the address is non-zero, checks if it's valid according to EIP-1271.
* @param signer The address to validate the signature against.
* @param hash The hash of the signed data.
* @param r The first 32 bytes of the signature.
* @param vs The combined `v` and `s` values of the signature.
* @return success True if the signature is verified, false otherwise.
*/
function recoverOrIsValidSignature(
address signer,
bytes32 hash,
bytes32 r,
bytes32 vs
) internal view returns (bool success) {
if (signer == address(0)) return false;
if (recover(hash, r, vs) == signer) {
return true;
}
return isValidSignature(signer, hash, r, vs);
}
/**
* @notice Verifies the signature for a given hash, attempting to recover the signer's address or validates it using EIP-1271 for 65-byte signatures.
* @dev Attempts to recover the signer's address from the signature. If the address is a contract, checks if the signature is valid according to EIP-1271.
* @param signer The expected signer's address.
* @param hash The keccak256 hash of the signed data.
* @param r The first 32 bytes of the signature.
* @param vs The last 32 bytes of the signature, with the last byte being the recovery id.
* @return success True if the signature is valid, false otherwise.
*/
function recoverOrIsValidSignature65(
address signer,
bytes32 hash,
bytes32 r,
bytes32 vs
) internal view returns (bool success) {
if (signer == address(0)) return false;
if (recover(hash, r, vs) == signer) {
return true;
}
return isValidSignature65(signer, hash, r, vs);
}
/**
* @notice Validates a signature for a hash using EIP-1271, if `signer` is a contract.
* @dev Makes a static call to `signer` with `isValidSignature` function selector from EIP-1271.
* @param signer The address of the signer to validate against, which could be an EOA or a contract.
* @param hash The hash of the signed data.
* @param signature The signature to validate.
* @return success True if the signature is valid according to EIP-1271, false otherwise.
*/
function isValidSignature(
address signer,
bytes32 hash,
bytes calldata signature
) internal view returns (bool success) {
// (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature));
// return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
bytes4 selector = IERC1271.isValidSignature.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
mstore(ptr, selector)
mstore(add(ptr, 0x04), hash)
mstore(add(ptr, 0x24), 0x40)
mstore(add(ptr, 0x44), signature.length)
calldatacopy(add(ptr, 0x64), signature.offset, signature.length)
if staticcall(gas(), signer, ptr, add(0x64, signature.length), 0, 0x20) {
success := and(eq(selector, mload(0)), eq(returndatasize(), 0x20))
}
}
}
/**
* @notice Validates a signature for a hash using EIP-1271, if `signer` is a contract.
* @dev Makes a static call to `signer` with `isValidSignature` function selector from EIP-1271.
* @param signer The address of the signer to validate against, which could be an EOA or a contract.
* @param hash The hash of the signed data.
* @param v The recovery byte of the signature.
* @param r The first 32 bytes of the signature.
* @param s The second 32 bytes of the signature.
* @return success True if the signature is valid according to EIP-1271, false otherwise.
*/
function isValidSignature(
address signer,
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal view returns (bool success) {
bytes4 selector = IERC1271.isValidSignature.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
mstore(ptr, selector)
mstore(add(ptr, 0x04), hash)
mstore(add(ptr, 0x24), 0x40)
mstore(add(ptr, 0x44), 65)
mstore(add(ptr, 0x64), r)
mstore(add(ptr, 0x84), s)
mstore8(add(ptr, 0xa4), v)
if staticcall(gas(), signer, ptr, 0xa5, 0, 0x20) {
success := and(eq(selector, mload(0)), eq(returndatasize(), 0x20))
}
}
}
/**
* @notice Validates a signature for a hash using EIP-1271, if `signer` is a contract.
* @dev Makes a static call to `signer` with `isValidSignature` function selector from EIP-1271.
* @param signer The address of the signer to validate against, which could be an EOA or a contract.
* @param hash The hash of the signed data.
* @param r The first 32 bytes of the signature.
* @param vs The last 32 bytes of the signature, with the last byte being the recovery id.
* @return success True if the signature is valid according to EIP-1271, false otherwise.
*/
function isValidSignature(
address signer,
bytes32 hash,
bytes32 r,
bytes32 vs
) internal view returns (bool success) {
// (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, abi.encodePacked(r, vs)));
// return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
bytes4 selector = IERC1271.isValidSignature.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
mstore(ptr, selector)
mstore(add(ptr, 0x04), hash)
mstore(add(ptr, 0x24), 0x40)
mstore(add(ptr, 0x44), 64)
mstore(add(ptr, 0x64), r)
mstore(add(ptr, 0x84), vs)
if staticcall(gas(), signer, ptr, 0xa4, 0, 0x20) {
success := and(eq(selector, mload(0)), eq(returndatasize(), 0x20))
}
}
}
/**
* @notice Verifies if a 65-byte signature is valid for a given hash, according to EIP-1271.
* @param signer The address of the signer to validate against, which could be an EOA or a contract.
* @param hash The hash of the signed data.
* @param r The first 32 bytes of the signature.
* @param vs The combined `v` (recovery id) and `s` component of the signature, packed into the last 32 bytes.
* @return success True if the signature is valid according to EIP-1271, false otherwise.
*/
function isValidSignature65(
address signer,
bytes32 hash,
bytes32 r,
bytes32 vs
) internal view returns (bool success) {
// (bool success, bytes memory data) = signer.staticcall(abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, abi.encodePacked(r, vs & ~uint256(1 << 255), uint8(vs >> 255))));
// return success && data.length >= 4 && abi.decode(data, (bytes4)) == IERC1271.isValidSignature.selector;
bytes4 selector = IERC1271.isValidSignature.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
mstore(ptr, selector)
mstore(add(ptr, 0x04), hash)
mstore(add(ptr, 0x24), 0x40)
mstore(add(ptr, 0x44), 65)
mstore(add(ptr, 0x64), r)
mstore(add(ptr, 0x84), and(vs, _COMPACT_S_MASK))
mstore8(add(ptr, 0xa4), add(27, shr(_COMPACT_V_SHIFT, vs)))
if staticcall(gas(), signer, ptr, 0xa5, 0, 0x20) {
success := and(eq(selector, mload(0)), eq(returndatasize(), 0x20))
}
}
}
/**
* @notice Generates a hash compatible with Ethereum's signed message format.
* @dev Prepends the hash with Ethereum's message prefix before hashing it.
* @param hash The hash of the data to sign.
* @return res The Ethereum signed message hash.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 res) {
// 32 is the length in bytes of hash, enforced by the type signature above
// return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
mstore(0, 0x19457468657265756d205369676e6564204d6573736167653a0a333200000000) // "\x19Ethereum Signed Message:\n32"
mstore(28, hash)
res := keccak256(0, 60)
}
}
/**
* @notice Generates an EIP-712 compliant hash.
* @dev Encodes the domain separator and the struct hash according to EIP-712.
* @param domainSeparator The EIP-712 domain separator.
* @param structHash The EIP-712 struct hash.
* @return res The EIP-712 compliant hash.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 res) {
// return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
mstore(ptr, 0x1901000000000000000000000000000000000000000000000000000000000000) // "\x19\x01"
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
res := keccak256(ptr, 66)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title RevertReasonForwarder
* @notice Provides utilities for forwarding and retrieving revert reasons from failed external calls.
*/
library RevertReasonForwarder {
/**
* @dev Forwards the revert reason from the latest external call.
* This method allows propagating the revert reason of a failed external call to the caller.
*/
function reRevert() internal pure {
// bubble up revert reason from latest external call
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
/**
* @dev Retrieves the revert reason from the latest external call.
* This method enables capturing the revert reason of a failed external call for inspection or processing.
* @return reason The latest external call revert reason.
*/
function reReason() internal pure returns (bytes memory reason) {
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
reason := mload(0x40)
let length := returndatasize()
mstore(reason, length)
returndatacopy(add(reason, 0x20), 0, length)
mstore(0x40, add(reason, add(0x20, length)))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import "../interfaces/IDaiLikePermit.sol";
import "../interfaces/IPermit2.sol";
import "../interfaces/IERC7597Permit.sol";
import "../interfaces/IWETH.sol";
import "../libraries/RevertReasonForwarder.sol";
/**
* @title Implements efficient safe methods for ERC20 interface.
* @notice Compared to the standard ERC20, this implementation offers several enhancements:
* 1. more gas-efficient, providing significant savings in transaction costs.
* 2. support for different permit implementations
* 3. forceApprove functionality
* 4. support for WETH deposit and withdraw
*/
library SafeERC20 {
error SafeTransferFailed();
error SafeTransferFromFailed();
error ForceApproveFailed();
error SafeIncreaseAllowanceFailed();
error SafeDecreaseAllowanceFailed();
error SafePermitBadLength();
error Permit2TransferAmountTooHigh();
// Uniswap Permit2 address
address private constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
bytes4 private constant _PERMIT_LENGTH_ERROR = 0x68275857; // SafePermitBadLength.selector
uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;
/**
* @notice Fetches the balance of a specific ERC20 token held by an account.
* Consumes less gas then regular `ERC20.balanceOf`.
* @dev Note that the implementation does not perform dirty bits cleaning, so it is the
* responsibility of the caller to make sure that the higher 96 bits of the `account` parameter are clean.
* @param token The IERC20 token contract for which the balance will be fetched.
* @param account The address of the account whose token balance will be fetched.
* @return tokenBalance The balance of the specified ERC20 token held by the account.
*/
function safeBalanceOf(
IERC20 token,
address account
) internal view returns(uint256 tokenBalance) {
bytes4 selector = IERC20.balanceOf.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
mstore(0x00, selector)
mstore(0x04, account)
let success := staticcall(gas(), token, 0x00, 0x24, 0x00, 0x20)
tokenBalance := mload(0)
if or(iszero(success), lt(returndatasize(), 0x20)) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
/**
* @notice Attempts to safely transfer tokens from one address to another.
* @dev If permit2 is true, uses the Permit2 standard; otherwise uses the standard ERC20 transferFrom.
* Either requires `true` in return data, or requires target to be smart-contract and empty return data.
* Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
* the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean.
* @param token The IERC20 token contract from which the tokens will be transferred.
* @param from The address from which the tokens will be transferred.
* @param to The address to which the tokens will be transferred.
* @param amount The amount of tokens to transfer.
* @param permit2 If true, uses the Permit2 standard for the transfer; otherwise uses the standard ERC20 transferFrom.
*/
function safeTransferFromUniversal(
IERC20 token,
address from,
address to,
uint256 amount,
bool permit2
) internal {
if (permit2) {
safeTransferFromPermit2(token, from, to, amount);
} else {
safeTransferFrom(token, from, to, amount);
}
}
/**
* @notice Attempts to safely transfer tokens from one address to another using the ERC20 standard.
* @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
* Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
* the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean.
* @param token The IERC20 token contract from which the tokens will be transferred.
* @param from The address from which the tokens will be transferred.
* @param to The address to which the tokens will be transferred.
* @param amount The amount of tokens to transfer.
*/
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
bytes4 selector = token.transferFrom.selector;
bool success;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), from)
mstore(add(data, 0x24), to)
mstore(add(data, 0x44), amount)
success := call(gas(), token, 0, data, 100, 0x0, 0x20)
if success {
switch returndatasize()
case 0 {
success := gt(extcodesize(token), 0)
}
default {
success := and(gt(returndatasize(), 31), eq(mload(0), 1))
}
}
}
if (!success) revert SafeTransferFromFailed();
}
/**
* @notice Attempts to safely transfer tokens from one address to another using the Permit2 standard.
* @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
* Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
* the caller to make sure that the higher 96 bits of the `from` and `to` parameters are clean.
* @param token The IERC20 token contract from which the tokens will be transferred.
* @param from The address from which the tokens will be transferred.
* @param to The address to which the tokens will be transferred.
* @param amount The amount of tokens to transfer.
*/
function safeTransferFromPermit2(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
if (amount > type(uint160).max) revert Permit2TransferAmountTooHigh();
bytes4 selector = IPermit2.transferFrom.selector;
bool success;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), from)
mstore(add(data, 0x24), to)
mstore(add(data, 0x44), amount)
mstore(add(data, 0x64), token)
success := call(gas(), _PERMIT2, 0, data, 0x84, 0x0, 0x0)
if success {
success := gt(extcodesize(_PERMIT2), 0)
}
}
if (!success) revert SafeTransferFromFailed();
}
/**
* @notice Attempts to safely transfer tokens to another address.
* @dev Either requires `true` in return data, or requires target to be smart-contract and empty return data.
* Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
* the caller to make sure that the higher 96 bits of the `to` parameter are clean.
* @param token The IERC20 token contract from which the tokens will be transferred.
* @param to The address to which the tokens will be transferred.
* @param value The amount of tokens to transfer.
*/
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
if (!_makeCall(token, token.transfer.selector, to, value)) {
revert SafeTransferFailed();
}
}
/**
* @notice Attempts to approve a spender to spend a certain amount of tokens.
* @dev If `approve(from, to, amount)` fails, it tries to set the allowance to zero, and retries the `approve` call.
* Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
* the caller to make sure that the higher 96 bits of the `spender` parameter are clean.
* @param token The IERC20 token contract on which the call will be made.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
*/
function forceApprove(
IERC20 token,
address spender,
uint256 value
) internal {
if (!_makeCall(token, token.approve.selector, spender, value)) {
if (
!_makeCall(token, token.approve.selector, spender, 0) ||
!_makeCall(token, token.approve.selector, spender, value)
) {
revert ForceApproveFailed();
}
}
}
/**
* @notice Safely increases the allowance of a spender.
* @dev Increases with safe math check. Checks if the increased allowance will overflow, if yes, then it reverts the transaction.
* Then uses `forceApprove` to increase the allowance.
* Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
* the caller to make sure that the higher 96 bits of the `spender` parameter are clean.
* @param token The IERC20 token contract on which the call will be made.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to increase the allowance by.
*/
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 allowance = token.allowance(address(this), spender);
if (value > type(uint256).max - allowance) revert SafeIncreaseAllowanceFailed();
forceApprove(token, spender, allowance + value);
}
/**
* @notice Safely decreases the allowance of a spender.
* @dev Decreases with safe math check. Checks if the decreased allowance will underflow, if yes, then it reverts the transaction.
* Then uses `forceApprove` to increase the allowance.
* Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
* the caller to make sure that the higher 96 bits of the `spender` parameter are clean.
* @param token The IERC20 token contract on which the call will be made.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to decrease the allowance by.
*/
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 allowance = token.allowance(address(this), spender);
if (value > allowance) revert SafeDecreaseAllowanceFailed();
forceApprove(token, spender, allowance - value);
}
/**
* @notice Attempts to execute the `permit` function on the provided token with the sender and contract as parameters.
* Permit type is determined automatically based on permit calldata (IERC20Permit, IDaiLikePermit, and IPermit2).
* @dev Wraps `tryPermit` function and forwards revert reason if permit fails.
* @param token The IERC20 token to execute the permit function on.
* @param permit The permit data to be used in the function call.
*/
function safePermit(IERC20 token, bytes calldata permit) internal {
if (!tryPermit(token, msg.sender, address(this), permit)) RevertReasonForwarder.reRevert();
}
/**
* @notice Attempts to execute the `permit` function on the provided token with custom owner and spender parameters.
* Permit type is determined automatically based on permit calldata (IERC20Permit, IDaiLikePermit, and IPermit2).
* @dev Wraps `tryPermit` function and forwards revert reason if permit fails.
* Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
* the caller to make sure that the higher 96 bits of the `owner` and `spender` parameters are clean.
* @param token The IERC20 token to execute the permit function on.
* @param owner The owner of the tokens for which the permit is made.
* @param spender The spender allowed to spend the tokens by the permit.
* @param permit The permit data to be used in the function call.
*/
function safePermit(IERC20 token, address owner, address spender, bytes calldata permit) internal {
if (!tryPermit(token, owner, spender, permit)) RevertReasonForwarder.reRevert();
}
/**
* @notice Attempts to execute the `permit` function on the provided token with the sender and contract as parameters.
* @dev Invokes `tryPermit` with sender as owner and contract as spender.
* @param token The IERC20 token to execute the permit function on.
* @param permit The permit data to be used in the function call.
* @return success Returns true if the permit function was successfully executed, false otherwise.
*/
function tryPermit(IERC20 token, bytes calldata permit) internal returns(bool success) {
return tryPermit(token, msg.sender, address(this), permit);
}
/**
* @notice The function attempts to call the permit function on a given ERC20 token.
* @dev The function is designed to support a variety of permit functions, namely: IERC20Permit, IDaiLikePermit, IERC7597Permit and IPermit2.
* It accommodates both Compact and Full formats of these permit types.
* Please note, it is expected that the `expiration` parameter for the compact Permit2 and the `deadline` parameter
* for the compact Permit are to be incremented by one before invoking this function. This approach is motivated by
* gas efficiency considerations; as the unlimited expiration period is likely to be the most common scenario, and
* zeros are cheaper to pass in terms of gas cost. Thus, callers should increment the expiration or deadline by one
* before invocation for optimized performance.
* Note that the implementation does not perform dirty bits cleaning, so it is the responsibility of
* the caller to make sure that the higher 96 bits of the `owner` and `spender` parameters are clean.
* @param token The address of the ERC20 token on which to call the permit function.
* @param owner The owner of the tokens. This address should have signed the off-chain permit.
* @param spender The address which will be approved for transfer of tokens.
* @param permit The off-chain permit data, containing different fields depending on the type of permit function.
* @return success A boolean indicating whether the permit call was successful.
*/
function tryPermit(IERC20 token, address owner, address spender, bytes calldata permit) internal returns(bool success) {
// load function selectors for different permit standards
bytes4 permitSelector = IERC20Permit.permit.selector;
bytes4 daiPermitSelector = IDaiLikePermit.permit.selector;
bytes4 permit2Selector = IPermit2.permit.selector;
bytes4 erc7597PermitSelector = IERC7597Permit.permit.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
// Switch case for different permit lengths, indicating different permit standards
switch permit.length
// Compact IERC20Permit
case 100 {
mstore(ptr, permitSelector) // store selector
mstore(add(ptr, 0x04), owner) // store owner
mstore(add(ptr, 0x24), spender) // store spender
// Compact IERC20Permit.permit(uint256 value, uint32 deadline, uint256 r, uint256 vs)
{ // stack too deep
let deadline := shr(224, calldataload(add(permit.offset, 0x20))) // loads permit.offset 0x20..0x23
let vs := calldataload(add(permit.offset, 0x44)) // loads permit.offset 0x44..0x63
calldatacopy(add(ptr, 0x44), permit.offset, 0x20) // store value = copy permit.offset 0x00..0x19
mstore(add(ptr, 0x64), sub(deadline, 1)) // store deadline = deadline - 1
mstore(add(ptr, 0x84), add(27, shr(255, vs))) // store v = most significant bit of vs + 27 (27 or 28)
calldatacopy(add(ptr, 0xa4), add(permit.offset, 0x24), 0x20) // store r = copy permit.offset 0x24..0x43
mstore(add(ptr, 0xc4), shr(1, shl(1, vs))) // store s = vs without most significant bit
}
// IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
}
// Compact IDaiLikePermit
case 72 {
mstore(ptr, daiPermitSelector) // store selector
mstore(add(ptr, 0x04), owner) // store owner
mstore(add(ptr, 0x24), spender) // store spender
// Compact IDaiLikePermit.permit(uint32 nonce, uint32 expiry, uint256 r, uint256 vs)
{ // stack too deep
let expiry := shr(224, calldataload(add(permit.offset, 0x04))) // loads permit.offset 0x04..0x07
let vs := calldataload(add(permit.offset, 0x28)) // loads permit.offset 0x28..0x47
mstore(add(ptr, 0x44), shr(224, calldataload(permit.offset))) // store nonce = copy permit.offset 0x00..0x03
mstore(add(ptr, 0x64), sub(expiry, 1)) // store expiry = expiry - 1
mstore(add(ptr, 0x84), true) // store allowed = true
mstore(add(ptr, 0xa4), add(27, shr(255, vs))) // store v = most significant bit of vs + 27 (27 or 28)
calldatacopy(add(ptr, 0xc4), add(permit.offset, 0x08), 0x20) // store r = copy permit.offset 0x08..0x27
mstore(add(ptr, 0xe4), shr(1, shl(1, vs))) // store s = vs without most significant bit
}
// IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0x104, 0, 0)
}
// IERC20Permit
case 224 {
mstore(ptr, permitSelector)
calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
// IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
}
// IDaiLikePermit
case 256 {
mstore(ptr, daiPermitSelector)
calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
// IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0x104, 0, 0)
}
// Compact IPermit2
case 96 {
// Compact IPermit2.permit(uint160 amount, uint32 expiration, uint32 nonce, uint32 sigDeadline, uint256 r, uint256 vs)
mstore(ptr, permit2Selector) // store selector
mstore(add(ptr, 0x04), owner) // store owner
mstore(add(ptr, 0x24), token) // store token
calldatacopy(add(ptr, 0x50), permit.offset, 0x14) // store amount = copy permit.offset 0x00..0x13
// and(0xffffffffffff, ...) - conversion to uint48
mstore(add(ptr, 0x64), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x14))), 1))) // store expiration = ((permit.offset 0x14..0x17 - 1) & 0xffffffffffff)
mstore(add(ptr, 0x84), shr(224, calldataload(add(permit.offset, 0x18)))) // store nonce = copy permit.offset 0x18..0x1b
mstore(add(ptr, 0xa4), spender) // store spender
// and(0xffffffffffff, ...) - conversion to uint48
mstore(add(ptr, 0xc4), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x1c))), 1))) // store sigDeadline = ((permit.offset 0x1c..0x1f - 1) & 0xffffffffffff)
mstore(add(ptr, 0xe4), 0x100) // store offset = 256
mstore(add(ptr, 0x104), 0x40) // store length = 64
calldatacopy(add(ptr, 0x124), add(permit.offset, 0x20), 0x20) // store r = copy permit.offset 0x20..0x3f
calldatacopy(add(ptr, 0x144), add(permit.offset, 0x40), 0x20) // store vs = copy permit.offset 0x40..0x5f
// IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0)
}
// IPermit2
case 352 {
mstore(ptr, permit2Selector)
calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
// IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0)
}
// Dynamic length
default {
mstore(ptr, erc7597PermitSelector)
calldatacopy(add(ptr, 0x04), permit.offset, permit.length) // copy permit calldata
// IERC7597Permit.permit(address owner, address spender, uint256 value, uint256 deadline, bytes memory signature)
success := call(gas(), token, 0, ptr, add(permit.length, 4), 0, 0)
}
}
}
/**
* @dev Executes a low level call to a token contract, making it resistant to reversion and erroneous boolean returns.
* @param token The IERC20 token contract on which the call will be made.
* @param selector The function signature that is to be called on the token contract.
* @param to The address to which the token amount will be transferred.
* @param amount The token amount to be transferred.
* @return success A boolean indicating if the call was successful. Returns 'true' on success and 'false' on failure.
* In case of success but no returned data, validates that the contract code exists.
* In case of returned data, ensures that it's a boolean `true`.
*/
function _makeCall(
IERC20 token,
bytes4 selector,
address to,
uint256 amount
) private returns (bool success) {
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), to)
mstore(add(data, 0x24), amount)
success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
if success {
switch returndatasize()
case 0 {
success := gt(extcodesize(token), 0)
}
default {
success := and(gt(returndatasize(), 31), eq(mload(0), 1))
}
}
}
}
/**
* @notice Safely deposits a specified amount of Ether into the IWETH contract. Consumes less gas then regular `IWETH.deposit`.
* @param weth The IWETH token contract.
* @param amount The amount of Ether to deposit into the IWETH contract.
*/
function safeDeposit(IWETH weth, uint256 amount) internal {
if (amount > 0) {
bytes4 selector = IWETH.deposit.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
mstore(0, selector)
if iszero(call(gas(), weth, amount, 0, 4, 0, 0)) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
}
/**
* @notice Safely withdraws a specified amount of wrapped Ether from the IWETH contract. Consumes less gas then regular `IWETH.withdraw`.
* @dev Uses inline assembly to interact with the IWETH contract.
* @param weth The IWETH token contract.
* @param amount The amount of wrapped Ether to withdraw from the IWETH contract.
*/
function safeWithdraw(IWETH weth, uint256 amount) internal {
bytes4 selector = IWETH.withdraw.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
mstore(0, selector)
mstore(4, amount)
if iszero(call(gas(), weth, 0, 0, 0x24, 0, 0)) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
/**
* @notice Safely withdraws a specified amount of wrapped Ether from the IWETH contract to a specified recipient.
* Consumes less gas then regular `IWETH.withdraw`.
* @param weth The IWETH token contract.
* @param amount The amount of wrapped Ether to withdraw from the IWETH contract.
* @param to The recipient of the withdrawn Ether.
*/
function safeWithdrawTo(IWETH weth, uint256 amount, address to) internal {
safeWithdraw(weth, amount);
if (to != address(this)) {
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
if iszero(call(_RAW_CALL_GAS_LIMIT, to, amount, 0, 0, 0, 0)) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma abicoder v1;
// Allows anyone to claim a token if they exist in a merkle root.
interface ISignatureMerkleDrop128 {
error InvalidProof();
error DropAlreadyClaimed();
// Returns the address of the token distributed by this contract.
function token() external view returns (address);
// Returns the merkle root of the merkle tree containing account balances available to claim.
function merkleRoot() external view returns (bytes16);
// Returns the tree depth of the merkle tree containing account balances available to claim.
function depth() external view returns (uint256);
// Claim the given amount of the token to the given address. Reverts if the inputs are invalid.
function claim(address receiver, uint256 amount, bytes calldata merkleProof, bytes calldata signature) external;
// Verifies that given leaf and merkle proof matches given merkle root and returns leaf index.
function verify(bytes calldata proof, bytes16 root, bytes16 leaf) external view returns (bool valid, uint256 index);
// Returns true if the index has been marked claimed.
function isClaimed(uint256 index) external view returns (bool);
}{
"optimizer": {
"enabled": true,
"runs": 1000000
},
"evmVersion": "shanghai",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"bytes16","name":"merkleRoot_","type":"bytes16"},{"internalType":"uint256","name":"depth_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"DropAlreadyClaimed","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"SafeTransferFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"merkleProof","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"isClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes16","name":"","type":"bytes16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"proof","type":"bytes"},{"internalType":"bytes16","name":"root","type":"bytes16"},{"internalType":"bytes16","name":"leaf","type":"bytes16"}],"name":"verify","outputs":[{"internalType":"bool","name":"valid","type":"bool"},{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"proof","type":"bytes"},{"internalType":"bytes16","name":"leaf","type":"bytes16"}],"name":"verify","outputs":[{"internalType":"bool","name":"valid","type":"bool"},{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60e060405234801561000f575f80fd5b50604051610e96380380610e9683398101604081905261002e916100cf565b338061005357604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b61005c81610080565b506001600160a01b039092166080526001600160801b03191660a05260c052610126565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f805f606084860312156100e1575f80fd5b83516001600160a01b03811681146100f7575f80fd5b60208501519093506001600160801b031981168114610114575f80fd5b80925050604084015190509250925092565b60805160a05160c051610d2b61016b5f395f8181610176015261065301525f818160d70152818161036d01526104c901525f81816102c2015261054c0152610d2b5ff3fe6080604052600436106100bb575f3560e01c80638da5cb5b11610071578063b33302c71161004c578063b33302c714610273578063f2fde38b14610292578063fc0c546a146102b1575f80fd5b80638da5cb5b146101db5780638ebb005d146102255780639e34070f14610244575f80fd5b8063631c56ef116100a1578063631c56ef14610165578063715018a6146101a657806378e3214f146101bc575f80fd5b80632eb4a7ab146100c65780633956851b1461012f575f80fd5b366100c257005b5f80fd5b3480156100d1575f80fd5b506100f97f000000000000000000000000000000000000000000000000000000000000000081565b6040517fffffffffffffffffffffffffffffffff0000000000000000000000000000000090911681526020015b60405180910390f35b34801561013a575f80fd5b5061014e610149366004610ab6565b6102e4565b604080519215158352602083019190915201610126565b348015610170575f80fd5b506101987f000000000000000000000000000000000000000000000000000000000000000081565b604051908152602001610126565b3480156101b1575f80fd5b506101ba6102ff565b005b3480156101c7575f80fd5b506101ba6101d6366004610b3a565b610312565b3480156101e6575f80fd5b505f5473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610126565b348015610230575f80fd5b5061014e61023f366004610b62565b610364565b34801561024f575f80fd5b5061026361025e366004610bb2565b61039e565b6040519015158152602001610126565b34801561027e575f80fd5b506101ba61028d366004610bc9565b6103dc565b34801561029d575f80fd5b506101ba6102ac366004610c4d565b610588565b3480156102bc575f80fd5b506102007f000000000000000000000000000000000000000000000000000000000000000081565b5f806102f2868686866105f0565b9150915094509492505050565b610307610679565b6103105f6106cb565b565b61031a610679565b73ffffffffffffffffffffffffffffffffffffffff82166103435761033f338261073f565b5050565b61033f73ffffffffffffffffffffffffffffffffffffffff83163383610816565b5f8061039285857f0000000000000000000000000000000000000000000000000000000000000000866105f0565b91509150935093915050565b5f806103ac61010084610c93565b90505f6103bb61010085610ca6565b5f9283526001602081905260409093205492901b9182169091149392505050565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088901b1660208201525f9061045c90603401604051602081830303815290604052805190602001207f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f908152601c91909152603c902090565b90505f61046a828585610878565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b166020820152603481018990529091505f906054016040516020818303038152906040528051906020012090505f806104ee89897f0000000000000000000000000000000000000000000000000000000000000000866105f0565b9150915081610529576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61053281610940565b61057373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168c8c610816565b61057b6109af565b5050505050505050505050565b610590610679565b73ffffffffffffffffffffffffffffffffffffffff81166105e4576040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081525f60048201526024015b60405180910390fd5b6105ed816106cb565b50565b5f808560018682015b808310156106405782358087106001811461061f57815f52876010528584179550610627565b875f52816010525b505060205f2095508160011b91506010830192506105f9565b5050509190921860801c159460109093047f0000000000000000000000000000000000000000000000000000000000000000031b925050565b5f5473ffffffffffffffffffffffffffffffffffffffff163314610310576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016105db565b5f805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b8047101561077b576040517fcd7860590000000000000000000000000000000000000000000000000000000081523060048201526024016105db565b5f8273ffffffffffffffffffffffffffffffffffffffff16826040515f6040518083038185875af1925050503d805f81146107d1576040519150601f19603f3d011682016040523d82523d5f602084013e6107d6565b606091505b5050905080610811576040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b610842837fa9059cbb0000000000000000000000000000000000000000000000000000000084846109d5565b610811576040517ffb7f507900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f604051826041811461089557604081146108ae575f91506108ef565b60408501355f1a602083015260408560408401376108ef565b60208501358060ff1c601b01602084015260208660408501377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660608301525b508015610938577f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a160608201511015610938578481525f805260205f60808360015afa505f5191505b509392505050565b600881901c5f8181526001602081905260409091205460ff841691821b8117808203610998576040517f1e65ff1f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f9384526001602052604090932092909255505050565b4780156105ed576105ed6109ce6109c861ea6048610cb9565b83610a24565b329061073f565b5f60405184815283600482015282602482015260205f6044835f8a5af19150508015610a1c573d8015610a135760015f5114601f3d11169150610a1a565b5f863b1191505b505b949350505050565b5f818310610a325781610a34565b825b90505b92915050565b5f8083601f840112610a4d575f80fd5b50813567ffffffffffffffff811115610a64575f80fd5b602083019150836020828501011115610a7b575f80fd5b9250929050565b80357fffffffffffffffffffffffffffffffff0000000000000000000000000000000081168114610ab1575f80fd5b919050565b5f805f8060608587031215610ac9575f80fd5b843567ffffffffffffffff811115610adf575f80fd5b610aeb87828801610a3d565b9095509350610afe905060208601610a82565b9150610b0c60408601610a82565b905092959194509250565b803573ffffffffffffffffffffffffffffffffffffffff81168114610ab1575f80fd5b5f8060408385031215610b4b575f80fd5b610b5483610b17565b946020939093013593505050565b5f805f60408486031215610b74575f80fd5b833567ffffffffffffffff811115610b8a575f80fd5b610b9686828701610a3d565b9094509250610ba9905060208501610a82565b90509250925092565b5f60208284031215610bc2575f80fd5b5035919050565b5f805f805f8060808789031215610bde575f80fd5b610be787610b17565b955060208701359450604087013567ffffffffffffffff80821115610c0a575f80fd5b610c168a838b01610a3d565b90965094506060890135915080821115610c2e575f80fd5b50610c3b89828a01610a3d565b979a9699509497509295939492505050565b5f60208284031215610c5d575f80fd5b610a3482610b17565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f82610ca157610ca1610c66565b500490565b5f82610cb457610cb4610c66565b500690565b8082028115828204841417610a37577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffdfea2646970667358221220bcfb7109300f22437a7f2522f92947c9c5628d89f69b694516d9c47bbfc0854f64736f6c63430008170033000000000000000000000000111111111117dc0aa78b770fa6a738034120c3029f06c4c79e63f55d17dc6e07a1dfcf56000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008
Deployed Bytecode
0x6080604052600436106100bb575f3560e01c80638da5cb5b11610071578063b33302c71161004c578063b33302c714610273578063f2fde38b14610292578063fc0c546a146102b1575f80fd5b80638da5cb5b146101db5780638ebb005d146102255780639e34070f14610244575f80fd5b8063631c56ef116100a1578063631c56ef14610165578063715018a6146101a657806378e3214f146101bc575f80fd5b80632eb4a7ab146100c65780633956851b1461012f575f80fd5b366100c257005b5f80fd5b3480156100d1575f80fd5b506100f97f9f06c4c79e63f55d17dc6e07a1dfcf560000000000000000000000000000000081565b6040517fffffffffffffffffffffffffffffffff0000000000000000000000000000000090911681526020015b60405180910390f35b34801561013a575f80fd5b5061014e610149366004610ab6565b6102e4565b604080519215158352602083019190915201610126565b348015610170575f80fd5b506101987f000000000000000000000000000000000000000000000000000000000000000881565b604051908152602001610126565b3480156101b1575f80fd5b506101ba6102ff565b005b3480156101c7575f80fd5b506101ba6101d6366004610b3a565b610312565b3480156101e6575f80fd5b505f5473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610126565b348015610230575f80fd5b5061014e61023f366004610b62565b610364565b34801561024f575f80fd5b5061026361025e366004610bb2565b61039e565b6040519015158152602001610126565b34801561027e575f80fd5b506101ba61028d366004610bc9565b6103dc565b34801561029d575f80fd5b506101ba6102ac366004610c4d565b610588565b3480156102bc575f80fd5b506102007f000000000000000000000000111111111117dc0aa78b770fa6a738034120c30281565b5f806102f2868686866105f0565b9150915094509492505050565b610307610679565b6103105f6106cb565b565b61031a610679565b73ffffffffffffffffffffffffffffffffffffffff82166103435761033f338261073f565b5050565b61033f73ffffffffffffffffffffffffffffffffffffffff83163383610816565b5f8061039285857f9f06c4c79e63f55d17dc6e07a1dfcf5600000000000000000000000000000000866105f0565b91509150935093915050565b5f806103ac61010084610c93565b90505f6103bb61010085610ca6565b5f9283526001602081905260409093205492901b9182169091149392505050565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088901b1660208201525f9061045c90603401604051602081830303815290604052805190602001207f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f908152601c91909152603c902090565b90505f61046a828585610878565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b166020820152603481018990529091505f906054016040516020818303038152906040528051906020012090505f806104ee89897f9f06c4c79e63f55d17dc6e07a1dfcf5600000000000000000000000000000000866105f0565b9150915081610529576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61053281610940565b61057373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000111111111117dc0aa78b770fa6a738034120c302168c8c610816565b61057b6109af565b5050505050505050505050565b610590610679565b73ffffffffffffffffffffffffffffffffffffffff81166105e4576040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081525f60048201526024015b60405180910390fd5b6105ed816106cb565b50565b5f808560018682015b808310156106405782358087106001811461061f57815f52876010528584179550610627565b875f52816010525b505060205f2095508160011b91506010830192506105f9565b5050509190921860801c159460109093047f0000000000000000000000000000000000000000000000000000000000000008031b925050565b5f5473ffffffffffffffffffffffffffffffffffffffff163314610310576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016105db565b5f805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b8047101561077b576040517fcd7860590000000000000000000000000000000000000000000000000000000081523060048201526024016105db565b5f8273ffffffffffffffffffffffffffffffffffffffff16826040515f6040518083038185875af1925050503d805f81146107d1576040519150601f19603f3d011682016040523d82523d5f602084013e6107d6565b606091505b5050905080610811576040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b610842837fa9059cbb0000000000000000000000000000000000000000000000000000000084846109d5565b610811576040517ffb7f507900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f604051826041811461089557604081146108ae575f91506108ef565b60408501355f1a602083015260408560408401376108ef565b60208501358060ff1c601b01602084015260208660408501377f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660608301525b508015610938577f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a160608201511015610938578481525f805260205f60808360015afa505f5191505b509392505050565b600881901c5f8181526001602081905260409091205460ff841691821b8117808203610998576040517f1e65ff1f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f9384526001602052604090932092909255505050565b4780156105ed576105ed6109ce6109c861ea6048610cb9565b83610a24565b329061073f565b5f60405184815283600482015282602482015260205f6044835f8a5af19150508015610a1c573d8015610a135760015f5114601f3d11169150610a1a565b5f863b1191505b505b949350505050565b5f818310610a325781610a34565b825b90505b92915050565b5f8083601f840112610a4d575f80fd5b50813567ffffffffffffffff811115610a64575f80fd5b602083019150836020828501011115610a7b575f80fd5b9250929050565b80357fffffffffffffffffffffffffffffffff0000000000000000000000000000000081168114610ab1575f80fd5b919050565b5f805f8060608587031215610ac9575f80fd5b843567ffffffffffffffff811115610adf575f80fd5b610aeb87828801610a3d565b9095509350610afe905060208601610a82565b9150610b0c60408601610a82565b905092959194509250565b803573ffffffffffffffffffffffffffffffffffffffff81168114610ab1575f80fd5b5f8060408385031215610b4b575f80fd5b610b5483610b17565b946020939093013593505050565b5f805f60408486031215610b74575f80fd5b833567ffffffffffffffff811115610b8a575f80fd5b610b9686828701610a3d565b9094509250610ba9905060208501610a82565b90509250925092565b5f60208284031215610bc2575f80fd5b5035919050565b5f805f805f8060808789031215610bde575f80fd5b610be787610b17565b955060208701359450604087013567ffffffffffffffff80821115610c0a575f80fd5b610c168a838b01610a3d565b90965094506060890135915080821115610c2e575f80fd5b50610c3b89828a01610a3d565b979a9699509497509295939492505050565b5f60208284031215610c5d575f80fd5b610a3482610b17565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f82610ca157610ca1610c66565b500490565b5f82610cb457610cb4610c66565b500690565b8082028115828204841417610a37577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffdfea2646970667358221220bcfb7109300f22437a7f2522f92947c9c5628d89f69b694516d9c47bbfc0854f64736f6c63430008170033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000111111111117dc0aa78b770fa6a738034120c3029f06c4c79e63f55d17dc6e07a1dfcf56000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008
-----Decoded View---------------
Arg [0] : token_ (address): 0x111111111117dC0aa78b770fA6A738034120C302
Arg [1] : merkleRoot_ (bytes16): 0x9f06c4c79e63f55d17dc6e07a1dfcf56
Arg [2] : depth_ (uint256): 8
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000111111111117dc0aa78b770fa6a738034120c302
Arg [1] : 9f06c4c79e63f55d17dc6e07a1dfcf5600000000000000000000000000000000
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000008
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.