Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 9652510 | 147 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Escrow
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import {TokenTransferLib} from "./libraries/TokenTransferLib.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {IEscrow} from "./interfaces/IEscrow.sol";
import {ISettler} from "./interfaces/ISettler.sol";
/// @title Escrow Contract
/// @notice Facilitates secure token escrow with cross-chain settlement capabilities
/// @dev Supports multi-token escrows with configurable refund amounts and settlement deadlines
contract Escrow is IEscrow {
////////////////////////////////////////////////////////////////////////
// Events
////////////////////////////////////////////////////////////////////////
/// @notice Emitted when a new escrow is created
event EscrowCreated(bytes32 escrowId);
/// @notice Emitted when funds are refunded to the depositor
event EscrowRefundedDepositor(bytes32 escrowId);
/// @notice Emitted when funds are refunded to the recipient
event EscrowRefundedRecipient(bytes32 escrowId);
/// @notice Emitted when an escrow is successfully settled
event EscrowSettled(bytes32 escrowId);
////////////////////////////////////////////////////////////////////////
// Errors
////////////////////////////////////////////////////////////////////////
/// @notice Thrown when an operation is attempted on an escrow in an invalid status
error InvalidStatus();
/// @notice Thrown when escrow parameters are invalid (e.g., refund > escrow amount)
error InvalidEscrow();
/// @notice Thrown when refund is attempted before the settlement deadline
error RefundInvalid();
/// @notice Thrown when the settler contract rejects the settlement
error SettlementInvalid();
////////////////////////////////////////////////////////////////////////
// EIP-5267 Support
////////////////////////////////////////////////////////////////////////
/// @dev See: https://eips.ethereum.org/EIPS/eip-5267
/// Returns the fields and values that describe the domain separator used for signing.
/// Note: This is just for labelling and offchain verification purposes.
/// This contract does not use EIP712 signatures anywhere else.
function eip712Domain()
public
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
fields = hex"0f"; // `0b01111` - has name, version, chainId, verifyingContract
name = "Escrow";
version = "0.0.1";
chainId = block.chainid;
verifyingContract = address(this);
salt = bytes32(0);
extensions = new uint256[](0);
}
////////////////////////////////////////////////////////////////////////
// State Variables
////////////////////////////////////////////////////////////////////////
/// @notice Stores escrow details indexed by escrow ID
mapping(bytes32 => Escrow) public escrows;
/// @notice Tracks the current status of each escrow
mapping(bytes32 => EscrowStatus) public statuses;
////////////////////////////////////////////////////////////////////////
// Functions
////////////////////////////////////////////////////////////////////////
/// @notice Creates one or more escrows by transferring tokens from the depositor
/// @dev Generates unique escrow IDs by hashing the escrow struct
function escrow(Escrow[] memory _escrows) public payable {
uint256 totalNativeEscrowAmount;
for (uint256 i = 0; i < _escrows.length; i++) {
if (_escrows[i].refundAmount > _escrows[i].escrowAmount) {
revert InvalidEscrow();
}
bytes32 escrowId = keccak256(abi.encode(_escrows[i]));
// Check if the escrow already exists
if (statuses[escrowId] != EscrowStatus.NULL) {
revert InvalidStatus();
}
statuses[escrowId] = EscrowStatus.CREATED;
escrows[escrowId] = _escrows[i];
if (_escrows[i].escrowAmount > 0) {
if (_escrows[i].token == address(0)) {
totalNativeEscrowAmount += _escrows[i].escrowAmount;
} else {
SafeTransferLib.safeTransferFrom(
_escrows[i].token, msg.sender, address(this), _escrows[i].escrowAmount
);
}
}
emit EscrowCreated(escrowId);
}
if (msg.value != totalNativeEscrowAmount) {
revert InvalidEscrow();
}
}
/// @notice Refunds the specified amount to both depositors and recipients
/// @dev Can only be called after refundTimestamp has passed.
/// @dev If one of the parties is forcefully reverting the tx, then the other party
/// can use the individual refund functions to get their funds back.
function refund(bytes32[] calldata escrowIds) public {
for (uint256 i = 0; i < escrowIds.length; i++) {
Escrow storage _escrow = escrows[escrowIds[i]];
// If refund timestamp hasn't passed yet, then the refund is invalid.
if (block.timestamp <= _escrow.refundTimestamp) {
revert RefundInvalid();
}
_refundDepositor(escrowIds[i], _escrow);
_refundRecipient(escrowIds[i], _escrow);
}
}
/// @notice Refunds the specified amount to depositors after the refund timestamp
/// @dev Can only be called after refundTimestamp has passed
function refundDepositor(bytes32[] calldata escrowIds) public {
for (uint256 i = 0; i < escrowIds.length; i++) {
Escrow storage _escrow = escrows[escrowIds[i]];
// If refund timestamp hasn't passed yet, then the refund is invalid.
if (block.timestamp <= _escrow.refundTimestamp) {
revert RefundInvalid();
}
_refundDepositor(escrowIds[i], _escrow);
}
}
/// @notice Internal function to process depositor refund
/// @dev Updates escrow status based on current state (CREATED -> REFUND_DEPOSIT or REFUND_RECIPIENT -> FINALIZED)
function _refundDepositor(bytes32 escrowId, Escrow storage _escrow) internal {
EscrowStatus status = statuses[escrowId];
if (status == EscrowStatus.CREATED) {
statuses[escrowId] = EscrowStatus.REFUND_DEPOSIT;
} else if (status == EscrowStatus.REFUND_RECIPIENT) {
statuses[escrowId] = EscrowStatus.FINALIZED;
} else {
revert InvalidStatus();
}
if (_escrow.refundAmount > 0) {
TokenTransferLib.safeTransfer(_escrow.token, _escrow.depositor, _escrow.refundAmount);
}
emit EscrowRefundedDepositor(escrowId);
}
/// @notice Refunds the remaining amount (escrowAmount - refundAmount) to recipients after the refund timestamp
/// @dev Can only be called after refundTimestamp has passed
function refundRecipient(bytes32[] calldata escrowIds) public {
for (uint256 i = 0; i < escrowIds.length; i++) {
Escrow storage _escrow = escrows[escrowIds[i]];
// If settlement is still within the deadline, then refund is invalid.
if (block.timestamp <= _escrow.refundTimestamp) {
revert RefundInvalid();
}
_refundRecipient(escrowIds[i], _escrow);
}
}
/// @notice Internal function to process recipient refund
/// @dev Updates escrow status based on current state (CREATED -> REFUND_RECIPIENT or REFUND_DEPOSIT -> FINALIZED)
function _refundRecipient(bytes32 escrowId, Escrow storage _escrow) internal {
EscrowStatus status = statuses[escrowId];
// Status has to be REFUND_DEPOSIT or CREATED
if (status == EscrowStatus.CREATED) {
statuses[escrowId] = EscrowStatus.REFUND_RECIPIENT;
} else if (status == EscrowStatus.REFUND_DEPOSIT) {
statuses[escrowId] = EscrowStatus.FINALIZED;
} else {
revert InvalidStatus();
}
if (_escrow.escrowAmount - _escrow.refundAmount > 0) {
TokenTransferLib.safeTransfer(
_escrow.token, _escrow.recipient, _escrow.escrowAmount - _escrow.refundAmount
);
}
emit EscrowRefundedRecipient(escrowId);
}
/// @notice Settles escrows by transferring the full amount to recipients if validated by the settler
/// @dev Requires validation from the settler contract.
/// @dev Settlement can happen anytime before the refund timestamp. It can also happen after
/// refund timestamp, but only if the escrow hasn't processed any refunds yet.
function settle(bytes32[] calldata escrowIds) public {
for (uint256 i = 0; i < escrowIds.length; i++) {
_settle(escrowIds[i]);
}
}
/// @notice Internal function to process escrow settlement
/// @dev Validates settlement with the settler contract and transfers full escrowAmount to recipient
function _settle(bytes32 escrowId) internal {
Escrow storage _escrow = escrows[escrowId];
// Status has to be CREATED.
if (statuses[escrowId] != EscrowStatus.CREATED) {
revert InvalidStatus();
}
statuses[escrowId] = EscrowStatus.FINALIZED;
// Check with the settler if the message has been sent from the correct sender and chainId.
bool isSettled = ISettler(_escrow.settler).read(
_escrow.settlementId, _escrow.sender, _escrow.senderChainId
);
if (!isSettled) {
revert SettlementInvalid();
}
if (_escrow.escrowAmount > 0) {
TokenTransferLib.safeTransfer(_escrow.token, _escrow.recipient, _escrow.escrowAmount);
}
emit EscrowSettled(escrowId);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
/// @title TokenTransferLib
/// @notice A library to handle token transfers.
library TokenTransferLib {
////////////////////////////////////////////////////////////////////////
// Operations
////////////////////////////////////////////////////////////////////////
/// @dev ERC20 or native token balance query.
/// If `token` is `address(0)`, it is treated as a native token balance query.
function balanceOf(address token, address owner) internal view returns (uint256) {
if (token == address(0)) return owner.balance;
return SafeTransferLib.balanceOf(token, owner);
}
/// @dev ERC20 or native token transfer function.
/// If `token` is `address(0)`, it is treated as a native token transfer.
function safeTransfer(address token, address to, uint256 amount) internal {
if (token == address(0)) {
SafeTransferLib.safeTransferETH(to, amount);
} else {
SafeTransferLib.safeTransfer(token, to, amount);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/// @dev The ERC20 `totalSupply` query has failed.
error TotalSupplyQueryFailed();
/// @dev The Permit2 operation has failed.
error Permit2Failed();
/// @dev The Permit2 amount must be less than `2**160 - 1`.
error Permit2AmountOverflow();
/// @dev The Permit2 approve operation has failed.
error Permit2ApproveFailed();
/// @dev The Permit2 lockdown operation has failed.
error Permit2LockdownFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/// @dev The unique EIP-712 domain separator for the DAI token contract.
bytes32 internal constant DAI_DOMAIN_SEPARATOR =
0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;
/// @dev The address for the WETH9 contract on Ethereum mainnet.
address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/// @dev The canonical Permit2 address.
/// [Github](https://github.com/Uniswap/permit2)
/// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
/// @dev The canonical address of the `SELFDESTRUCT` ETH mover.
/// See: https://gist.github.com/Vectorized/1cb8ad4cf393b1378e08f23f79bd99fa
/// [Etherscan](https://etherscan.io/address/0x00000000000073c48c8055bD43D1A53799176f0D)
address internal constant ETH_MOVER = 0x00000000000073c48c8055bD43D1A53799176f0D;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Force transfers ETH to `to`, without triggering the fallback (if any).
/// This method attempts to use a separate contract to send via `SELFDESTRUCT`,
/// and upon failure, deploys a minimal vault to accrue the ETH.
function safeMoveETH(address to, uint256 amount) internal returns (address vault) {
/// @solidity memory-safe-assembly
assembly {
to := shr(96, shl(96, to)) // Clean upper 96 bits.
for { let mover := ETH_MOVER } iszero(eq(to, address())) {} {
let selfBalanceBefore := selfbalance()
if or(lt(selfBalanceBefore, amount), eq(to, mover)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if extcodesize(mover) {
let balanceBefore := balance(to) // Check via delta, in case `SELFDESTRUCT` is bricked.
mstore(0x00, to)
pop(call(gas(), mover, amount, 0x00, 0x20, codesize(), 0x00))
// If `address(to).balance >= amount + balanceBefore`, skip vault workflow.
if iszero(lt(balance(to), add(amount, balanceBefore))) { break }
// Just in case `SELFDESTRUCT` is changed to not revert and do nothing.
if lt(selfBalanceBefore, selfbalance()) { invalid() }
}
let m := mload(0x40)
// If the mover is missing or bricked, deploy a minimal vault
// that withdraws all ETH to `to` when being called only by `to`.
// forgefmt: disable-next-item
mstore(add(m, 0x20), 0x33146025575b600160005260206000f35b3d3d3d3d47335af1601a5760003dfd)
mstore(m, or(to, shl(160, 0x6035600b3d3960353df3fe73)))
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, keccak256(m, 0x40))
mstore(0x01, shl(96, address())) // Deployer.
mstore(0x15, 0) // Salt.
vault := keccak256(0x00, 0x55)
pop(call(gas(), vault, amount, codesize(), 0x00, codesize(), 0x00))
// The vault returns a single word on success. Failure reverts with empty data.
if iszero(returndatasize()) {
if iszero(create2(0, m, 0x40, 0)) { revert(codesize(), codesize()) } // For gas estimation.
}
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function trySafeTransferFrom(address token, address from, address to, uint256 amount)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have their entire balance approved for the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
if iszero(and(eq(mload(0x00), 1), success)) {
// Check the `extcodesize` again just in case the token selfdestructs lol.
if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=
mul( // The arguments of `mul` are evaluated from right to left.
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
/// @dev Performs a `token.balanceOf(account)` check.
/// `implemented` denotes whether the `token` does not implement `balanceOf`.
/// `amount` is zero if the `token` does not implement `balanceOf`.
function checkBalanceOf(address token, address account)
internal
view
returns (bool implemented, uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
implemented :=
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
amount := mul(mload(0x20), implemented)
}
}
/// @dev Returns the total supply of the `token`.
/// Reverts if the token does not exist or does not implement `totalSupply()`.
function totalSupply(address token) internal view returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x18160ddd) // `totalSupply()`.
if iszero(
and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
) {
mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
revert(0x1c, 0x04)
}
result := mload(0x00)
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// If the initial attempt fails, try to use Permit2 to transfer the token.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
if (!trySafeTransferFrom(token, from, to, amount)) {
permit2TransferFrom(token, from, to, amount);
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
/// Reverts upon failure.
function permit2TransferFrom(address token, address from, address to, uint256 amount)
internal
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(add(m, 0x74), shr(96, shl(96, token)))
mstore(add(m, 0x54), amount)
mstore(add(m, 0x34), to)
mstore(add(m, 0x20), shl(96, from))
// `transferFrom(address,address,uint160,address)`.
mstore(m, 0x36c78516000000000000000000000000)
let p := PERMIT2
let exists := eq(chainid(), 1)
if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
if iszero(
and(
call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00),
lt(iszero(extcodesize(token)), exists) // Token has code and Permit2 exists.
)
) {
mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
}
}
}
/// @dev Permit a user to spend a given amount of
/// another user's tokens via native EIP-2612 permit if possible, falling
/// back to Permit2 if native permit fails or is not implemented on the token.
function permit2(
address token,
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
for {} shl(96, xor(token, WETH9)) {} {
mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
// Gas stipend to limit gas burn for tokens that don't refund gas when
// an non-existing function is called. 5K should be enough for a SLOAD.
staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
)
) { break }
// After here, we can be sure that token is a contract.
let m := mload(0x40)
mstore(add(m, 0x34), spender)
mstore(add(m, 0x20), shl(96, owner))
mstore(add(m, 0x74), deadline)
if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
mstore(0x14, owner)
mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
mstore(
add(m, 0x94),
lt(iszero(amount), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
)
mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
// `nonces` is already at `add(m, 0x54)`.
// `amount != 0` is already stored at `add(m, 0x94)`.
mstore(add(m, 0xb4), and(0xff, v))
mstore(add(m, 0xd4), r)
mstore(add(m, 0xf4), s)
success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
break
}
mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
mstore(add(m, 0x54), amount)
mstore(add(m, 0x94), and(0xff, v))
mstore(add(m, 0xb4), r)
mstore(add(m, 0xd4), s)
success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
break
}
}
if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
}
/// @dev Simple permit on the Permit2 contract.
function simplePermit2(
address token,
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, 0x927da105) // `allowance(address,address,address)`.
{
let addressMask := shr(96, not(0))
mstore(add(m, 0x20), and(addressMask, owner))
mstore(add(m, 0x40), and(addressMask, token))
mstore(add(m, 0x60), and(addressMask, spender))
mstore(add(m, 0xc0), and(addressMask, spender))
}
let p := mul(PERMIT2, iszero(shr(160, amount)))
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
)
) {
mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
revert(add(0x18, shl(2, iszero(p))), 0x04)
}
mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
// `owner` is already `add(m, 0x20)`.
// `token` is already at `add(m, 0x40)`.
mstore(add(m, 0x60), amount)
mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
// `nonce` is already at `add(m, 0xa0)`.
// `spender` is already at `add(m, 0xc0)`.
mstore(add(m, 0xe0), deadline)
mstore(add(m, 0x100), 0x100) // `signature` offset.
mstore(add(m, 0x120), 0x41) // `signature` length.
mstore(add(m, 0x140), r)
mstore(add(m, 0x160), s)
mstore(add(m, 0x180), shl(248, v))
if iszero( // Revert if token does not have code, or if the call fails.
mul(extcodesize(token), call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00))) {
mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Approves `spender` to spend `amount` of `token` for `address(this)`.
function permit2Approve(address token, address spender, uint160 amount, uint48 expiration)
internal
{
/// @solidity memory-safe-assembly
assembly {
let addressMask := shr(96, not(0))
let m := mload(0x40)
mstore(m, 0x87517c45) // `approve(address,address,uint160,uint48)`.
mstore(add(m, 0x20), and(addressMask, token))
mstore(add(m, 0x40), and(addressMask, spender))
mstore(add(m, 0x60), and(addressMask, amount))
mstore(add(m, 0x80), and(0xffffffffffff, expiration))
if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
mstore(0x00, 0x324f14ae) // `Permit2ApproveFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Revokes an approval for `token` and `spender` for `address(this)`.
function permit2Lockdown(address token, address spender) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, 0xcc53287f) // `Permit2.lockdown`.
mstore(add(m, 0x20), 0x20) // Offset of the `approvals`.
mstore(add(m, 0x40), 1) // `approvals.length`.
mstore(add(m, 0x60), shr(96, shl(96, token)))
mstore(add(m, 0x80), shr(96, shl(96, spender)))
if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
mstore(0x00, 0x96b3de23) // `Permit2LockdownFailed()`.
revert(0x1c, 0x04)
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
interface IEscrow {
enum EscrowStatus {
NULL,
CREATED,
REFUND_DEPOSIT,
REFUND_RECIPIENT,
FINALIZED
}
struct Escrow {
// 12 byte field, that can be used to create a unique escrow id.
bytes12 salt;
address depositor;
address recipient;
// The address of the token that is being escrowed.
address token;
// The amount of tokens being deposited to the escrow.
uint256 escrowAmount;
// The amount of tokens that will be refunded to depositor, if settlement doesn't happen.
uint256 refundAmount;
// The timestamp after which permissionless refunds become available.
uint256 refundTimestamp;
// The address of the oracle, which decides if the escrow should be settled.
address settler;
// The settler expects the following parameters as input.
// The address of the entity, that sends the cross chain message.
address sender;
// The settlement id, that is used to identify the settlement.
bytes32 settlementId;
// The chain id of the sender.
uint256 senderChainId;
}
function escrow(Escrow[] memory _escrows) external payable;
function refundDepositor(bytes32[] calldata escrowIds) external;
function refundRecipient(bytes32[] calldata escrowIds) external;
function settle(bytes32[] calldata escrowIds) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
interface ISettler {
/// @dev Allows anyone to attest to any settlementId, on all the input chains.
/// Input chain readers can choose which attestations they want to trust.
/// @param settlementId The ID of the settlement to attest to
/// @param settlerContext Encoded context data that the settler can decode (e.g., array of input chains)
function send(bytes32 settlementId, bytes calldata settlerContext) external payable;
/// @dev Check if an attester from a particular output chain, has attested to the settlementId.
/// For our case, the attester is the orchestrator.
/// And the settlementId, is the root of the merkle tree which is signed by the user.
function read(bytes32 settlementId, address attester, uint256 chainId)
external
view
returns (bool isSettled);
}{
"remappings": [
"forge-std/=lib/forge-std/src/",
"solady/=lib/solady/src/",
"murky/=lib/murky/src/",
"@layerzerolabs/oapp-evm/=lib/devtools/packages/oapp-evm/",
"@layerzerolabs/lz-evm-protocol-v2/=lib/LayerZero-v2/packages/layerzero-v2/evm/protocol/",
"@layerzerolabs/lz-evm-oapp-v2/=lib/LayerZero-v2/packages/layerzero-v2/evm/oapp/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"LayerZero-v2/=lib/LayerZero-v2/",
"devtools/=lib/devtools/packages/toolbox-foundry/src/",
"ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"InvalidEscrow","type":"error"},{"inputs":[],"name":"InvalidStatus","type":"error"},{"inputs":[],"name":"RefundInvalid","type":"error"},{"inputs":[],"name":"SettlementInvalid","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"escrowId","type":"bytes32"}],"name":"EscrowCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"escrowId","type":"bytes32"}],"name":"EscrowRefundedDepositor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"escrowId","type":"bytes32"}],"name":"EscrowRefundedRecipient","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"escrowId","type":"bytes32"}],"name":"EscrowSettled","type":"event"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes12","name":"salt","type":"bytes12"},{"internalType":"address","name":"depositor","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"uint256","name":"refundAmount","type":"uint256"},{"internalType":"uint256","name":"refundTimestamp","type":"uint256"},{"internalType":"address","name":"settler","type":"address"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"bytes32","name":"settlementId","type":"bytes32"},{"internalType":"uint256","name":"senderChainId","type":"uint256"}],"internalType":"struct IEscrow.Escrow[]","name":"_escrows","type":"tuple[]"}],"name":"escrow","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"escrows","outputs":[{"internalType":"bytes12","name":"salt","type":"bytes12"},{"internalType":"address","name":"depositor","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"uint256","name":"refundAmount","type":"uint256"},{"internalType":"uint256","name":"refundTimestamp","type":"uint256"},{"internalType":"address","name":"settler","type":"address"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"bytes32","name":"settlementId","type":"bytes32"},{"internalType":"uint256","name":"senderChainId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"escrowIds","type":"bytes32[]"}],"name":"refund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"escrowIds","type":"bytes32[]"}],"name":"refundDepositor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"escrowIds","type":"bytes32[]"}],"name":"refundRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"escrowIds","type":"bytes32[]"}],"name":"settle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"statuses","outputs":[{"internalType":"enum IEscrow.EscrowStatus","name":"","type":"uint8"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6080604052348015600e575f5ffd5b506111178061001c5f395ff3fe608060405260043610610079575f3560e01c80636023fda51161004c5780636023fda5146101f7578063657061bf1461021657806384b0196e14610229578063e7f921a21461028e575f5ffd5b806310f7cf321461007d5780632d83549c146100c1578063305cd830146101b75780635959f498146101d8575b5f5ffd5b348015610088575f5ffd5b506100ab610097366004610c0c565b60016020525f908152604090205460ff1681565b6040516100b89190610c37565b60405180910390f35b3480156100cc575f5ffd5b506101456100db366004610c0c565b5f60208190529081526040902080546001820154600283015460038401546004850154600586015460068701546007880154600889015460099099015460a089901b99600160601b9099046001600160a01b0390811699988116989781169793811693921691908b565b604080516001600160a01b0319909c168c526001600160a01b039a8b1660208d0152988a16988b019890985295881660608a0152608089019490945260a088019290925260c0870152841660e086015292909216610100840152610120830191909152610140820152610160016100b8565b3480156101c2575f5ffd5b506101d66101d1366004610c5d565b6102ad565b005b3480156101e3575f5ffd5b506101d66101f2366004610c5d565b610336565b348015610202575f5ffd5b506101d6610211366004610c5d565b6103ba565b6101d6610224366004610d6f565b610446565b348015610234575f5ffd5b506040805180820182526006815265457363726f7760d01b602080830191909152825180840184526005815264302e302e3160d81b8183015283515f808252928101948590526100b894600f60f81b949346923092610ef9565b348015610299575f5ffd5b506101d66102a8366004610c5d565b610766565b5f5b81811015610331575f5f5f8585858181106102cc576102cc610f8f565b9050602002013581526020019081526020015f209050806005015442116103065760405163ea6f9d8760e01b815260040160405180910390fd5b61032884848481811061031b5761031b610f8f565b9050602002013582610799565b506001016102af565b505050565b5f5b81811015610331575f5f5f85858581811061035557610355610f8f565b9050602002013581526020019081526020015f2090508060050154421161038f5760405163ea6f9d8760e01b815260040160405180910390fd5b6103b18484848181106103a4576103a4610f8f565b90506020020135826108c9565b50600101610338565b5f5b81811015610331575f5f5f8585858181106103d9576103d9610f8f565b9050602002013581526020019081526020015f209050806005015442116104135760405163ea6f9d8760e01b815260040160405180910390fd5b6104288484848181106103a4576103a4610f8f565b61043d84848481811061031b5761031b610f8f565b506001016103bc565b5f805b82518110156107415782818151811061046457610464610f8f565b60200260200101516080015183828151811061048257610482610f8f565b602002602001015160a0015111156104ad5760405163c4c3834b60e01b815260040160405180910390fd5b5f8382815181106104c0576104c0610f8f565b60200260200101516040516020016104d89190610fa3565b60408051601f19818403018152919052805160209091012090505f5f8281526001602052604090205460ff16600481111561051557610515610c23565b14610533576040516307a92f1960e51b815260040160405180910390fd5b5f818152600160208190526040909120805460ff19168280021790555083828151811061056257610562610f8f565b6020908102919091018101515f83815280835260408082208351948401516001600160a01b03908116600160601b0260a096871c178255918401516001820180549184166001600160a01b031992831617905560608501516002830180549185169183169190911790556080850151600383015594840151600482015560c0840151600582015560e0840151600682018054918416918716919091179055610100840151600782018054919093169516949094179055610120820151600884015561014090910151600990920191909155845185908490811061064757610647610f8f565b6020026020010151608001511115610705575f6001600160a01b031684838151811061067557610675610f8f565b6020026020010151606001516001600160a01b0316036106bf578382815181106106a1576106a1610f8f565b602002602001015160800151836106b8919061108f565b9250610705565b6107058483815181106106d4576106d4610f8f565b60200260200101516060015133308786815181106106f4576106f4610f8f565b6020026020010151608001516109bc565b6040518181527f172bd7c7b396e5e48c349fc8e1c845f01eefba35dff18954119870b39fd05e799060200160405180910390a150600101610449565b508034146107625760405163c4c3834b60e01b815260040160405180910390fd5b5050565b5f5b818110156103315761079183838381811061078557610785610f8f565b90506020020135610a15565b600101610768565b5f8281526001602081905260409091205460ff16908160048111156107c0576107c0610c23565b036107ed575f838152600160208190526040909120805460039260ff1990911690835b0217905550610841565b600281600481111561080157610801610c23565b03610828575f838152600160208190526040909120805460049260ff1990911690836107e3565b6040516307a92f1960e51b815260040160405180910390fd5b5f8260040154836003015461085691906110a8565b1115610890576002820154600183015460048401546003850154610890936001600160a01b0390811693169161088b916110a8565b610b86565b6040518381527f86f3f8fa9e3f11e6e5074508b888432d010d9457b158b59a76b769701626ee73906020015b60405180910390a1505050565b5f8281526001602081905260409091205460ff16908160048111156108f0576108f0610c23565b0361091d575f838152600160208190526040909120805460029260ff1990911690835b0217905550610958565b600381600481111561093157610931610c23565b03610828575f838152600160208190526040909120805460049260ff199091169083610913565b60048201541561098c5760028201548254600484015461098c926001600160a01b0390811692600160601b90041690610b86565b6040518381527f85c4b5c469113d432cac91fd4dc5b695be2774d74cfebb67ea93df9fbb872444906020016108bc565b60405181606052826040528360601b602c526323b872dd60601b600c5260205f6064601c5f895af18060015f511416610a0757803d873b151710610a0757637939f4245f526004601cfd5b505f60605260405250505050565b5f8181526020818152604080832060019283905292205460ff166004811115610a4057610a40610c23565b14610a5e576040516307a92f1960e51b815260040160405180910390fd5b5f82815260016020526040808220805460ff191660049081179091556006840154600885015460078601546009870154945163161a982560e21b8152938401919091526001600160a01b0390811660248401526044830193909352919091169063586a609490606401602060405180830381865afa158015610ae2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b0691906110bb565b905080610b2657604051636be6332960e11b815260040160405180910390fd5b600382015415610b5657600282015460018301546003840154610b56926001600160a01b03908116921690610b86565b6040518381527f5d408e94f2926e5e47121f94e0fe936fdbd142cc108f29e6db5b8018bb4099d6906020016108bc565b6001600160a01b038316610b9e576103318282610ba9565b610331838383610bc2565b5f385f3884865af16107625763b12d13eb5f526004601cfd5b816014528060345263a9059cbb60601b5f5260205f604460105f875af18060015f511416610c0257803d853b151710610c02576390b8ec185f526004601cfd5b505f603452505050565b5f60208284031215610c1c575f5ffd5b5035919050565b634e487b7160e01b5f52602160045260245ffd5b6020810160058310610c5757634e487b7160e01b5f52602160045260245ffd5b91905290565b5f5f60208385031215610c6e575f5ffd5b823567ffffffffffffffff811115610c84575f5ffd5b8301601f81018513610c94575f5ffd5b803567ffffffffffffffff811115610caa575f5ffd5b8560208260051b8401011115610cbe575f5ffd5b6020919091019590945092505050565b634e487b7160e01b5f52604160045260245ffd5b604051610160810167ffffffffffffffff81118282101715610d0657610d06610cce565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610d3557610d35610cce565b604052919050565b80356001600160a01b031981168114610d54575f5ffd5b919050565b80356001600160a01b0381168114610d54575f5ffd5b5f60208284031215610d7f575f5ffd5b813567ffffffffffffffff811115610d95575f5ffd5b8201601f81018413610da5575f5ffd5b803567ffffffffffffffff811115610dbf57610dbf610cce565b610dce60208260051b01610d0c565b8082825260208201915060206101608402850101925086831115610df0575f5ffd5b6020840193505b82841015610ec1576101608488031215610e0f575f5ffd5b610e17610ce2565b610e2085610d3d565b8152610e2e60208601610d59565b6020820152610e3f60408601610d59565b6040820152610e5060608601610d59565b60608201526080858101359082015260a0808601359082015260c08086013590820152610e7f60e08601610d59565b60e0820152610e916101008601610d59565b61010082015261012085810135908201526101408086013590820152825261016090930192602090910190610df7565b9695505050505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b60ff60f81b8816815260e060208201525f610f1760e0830189610ecb565b8281036040840152610f298189610ecb565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015610f7e578351835260209384019390920191600101610f60565b50909b9a5050505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b81516001600160a01b031916815261016081016020830151610fd060208401826001600160a01b03169052565b506040830151610feb60408401826001600160a01b03169052565b50606083015161100660608401826001600160a01b03169052565b506080830151608083015260a083015160a083015260c083015160c083015260e083015161103f60e08401826001600160a01b03169052565b5061010083015161105c6101008401826001600160a01b03169052565b5061012083015161012083015261014083015161014083015292915050565b634e487b7160e01b5f52601160045260245ffd5b808201808211156110a2576110a261107b565b92915050565b818103818111156110a2576110a261107b565b5f602082840312156110cb575f5ffd5b815180151581146110da575f5ffd5b939250505056fea264697066735822122053dc12bf2439bfea35d3663b369eadfcfbfe7ae1daa85dbbcf707e47612e0e8464736f6c634300081c0033
Deployed Bytecode
0x608060405260043610610079575f3560e01c80636023fda51161004c5780636023fda5146101f7578063657061bf1461021657806384b0196e14610229578063e7f921a21461028e575f5ffd5b806310f7cf321461007d5780632d83549c146100c1578063305cd830146101b75780635959f498146101d8575b5f5ffd5b348015610088575f5ffd5b506100ab610097366004610c0c565b60016020525f908152604090205460ff1681565b6040516100b89190610c37565b60405180910390f35b3480156100cc575f5ffd5b506101456100db366004610c0c565b5f60208190529081526040902080546001820154600283015460038401546004850154600586015460068701546007880154600889015460099099015460a089901b99600160601b9099046001600160a01b0390811699988116989781169793811693921691908b565b604080516001600160a01b0319909c168c526001600160a01b039a8b1660208d0152988a16988b019890985295881660608a0152608089019490945260a088019290925260c0870152841660e086015292909216610100840152610120830191909152610140820152610160016100b8565b3480156101c2575f5ffd5b506101d66101d1366004610c5d565b6102ad565b005b3480156101e3575f5ffd5b506101d66101f2366004610c5d565b610336565b348015610202575f5ffd5b506101d6610211366004610c5d565b6103ba565b6101d6610224366004610d6f565b610446565b348015610234575f5ffd5b506040805180820182526006815265457363726f7760d01b602080830191909152825180840184526005815264302e302e3160d81b8183015283515f808252928101948590526100b894600f60f81b949346923092610ef9565b348015610299575f5ffd5b506101d66102a8366004610c5d565b610766565b5f5b81811015610331575f5f5f8585858181106102cc576102cc610f8f565b9050602002013581526020019081526020015f209050806005015442116103065760405163ea6f9d8760e01b815260040160405180910390fd5b61032884848481811061031b5761031b610f8f565b9050602002013582610799565b506001016102af565b505050565b5f5b81811015610331575f5f5f85858581811061035557610355610f8f565b9050602002013581526020019081526020015f2090508060050154421161038f5760405163ea6f9d8760e01b815260040160405180910390fd5b6103b18484848181106103a4576103a4610f8f565b90506020020135826108c9565b50600101610338565b5f5b81811015610331575f5f5f8585858181106103d9576103d9610f8f565b9050602002013581526020019081526020015f209050806005015442116104135760405163ea6f9d8760e01b815260040160405180910390fd5b6104288484848181106103a4576103a4610f8f565b61043d84848481811061031b5761031b610f8f565b506001016103bc565b5f805b82518110156107415782818151811061046457610464610f8f565b60200260200101516080015183828151811061048257610482610f8f565b602002602001015160a0015111156104ad5760405163c4c3834b60e01b815260040160405180910390fd5b5f8382815181106104c0576104c0610f8f565b60200260200101516040516020016104d89190610fa3565b60408051601f19818403018152919052805160209091012090505f5f8281526001602052604090205460ff16600481111561051557610515610c23565b14610533576040516307a92f1960e51b815260040160405180910390fd5b5f818152600160208190526040909120805460ff19168280021790555083828151811061056257610562610f8f565b6020908102919091018101515f83815280835260408082208351948401516001600160a01b03908116600160601b0260a096871c178255918401516001820180549184166001600160a01b031992831617905560608501516002830180549185169183169190911790556080850151600383015594840151600482015560c0840151600582015560e0840151600682018054918416918716919091179055610100840151600782018054919093169516949094179055610120820151600884015561014090910151600990920191909155845185908490811061064757610647610f8f565b6020026020010151608001511115610705575f6001600160a01b031684838151811061067557610675610f8f565b6020026020010151606001516001600160a01b0316036106bf578382815181106106a1576106a1610f8f565b602002602001015160800151836106b8919061108f565b9250610705565b6107058483815181106106d4576106d4610f8f565b60200260200101516060015133308786815181106106f4576106f4610f8f565b6020026020010151608001516109bc565b6040518181527f172bd7c7b396e5e48c349fc8e1c845f01eefba35dff18954119870b39fd05e799060200160405180910390a150600101610449565b508034146107625760405163c4c3834b60e01b815260040160405180910390fd5b5050565b5f5b818110156103315761079183838381811061078557610785610f8f565b90506020020135610a15565b600101610768565b5f8281526001602081905260409091205460ff16908160048111156107c0576107c0610c23565b036107ed575f838152600160208190526040909120805460039260ff1990911690835b0217905550610841565b600281600481111561080157610801610c23565b03610828575f838152600160208190526040909120805460049260ff1990911690836107e3565b6040516307a92f1960e51b815260040160405180910390fd5b5f8260040154836003015461085691906110a8565b1115610890576002820154600183015460048401546003850154610890936001600160a01b0390811693169161088b916110a8565b610b86565b6040518381527f86f3f8fa9e3f11e6e5074508b888432d010d9457b158b59a76b769701626ee73906020015b60405180910390a1505050565b5f8281526001602081905260409091205460ff16908160048111156108f0576108f0610c23565b0361091d575f838152600160208190526040909120805460029260ff1990911690835b0217905550610958565b600381600481111561093157610931610c23565b03610828575f838152600160208190526040909120805460049260ff199091169083610913565b60048201541561098c5760028201548254600484015461098c926001600160a01b0390811692600160601b90041690610b86565b6040518381527f85c4b5c469113d432cac91fd4dc5b695be2774d74cfebb67ea93df9fbb872444906020016108bc565b60405181606052826040528360601b602c526323b872dd60601b600c5260205f6064601c5f895af18060015f511416610a0757803d873b151710610a0757637939f4245f526004601cfd5b505f60605260405250505050565b5f8181526020818152604080832060019283905292205460ff166004811115610a4057610a40610c23565b14610a5e576040516307a92f1960e51b815260040160405180910390fd5b5f82815260016020526040808220805460ff191660049081179091556006840154600885015460078601546009870154945163161a982560e21b8152938401919091526001600160a01b0390811660248401526044830193909352919091169063586a609490606401602060405180830381865afa158015610ae2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b0691906110bb565b905080610b2657604051636be6332960e11b815260040160405180910390fd5b600382015415610b5657600282015460018301546003840154610b56926001600160a01b03908116921690610b86565b6040518381527f5d408e94f2926e5e47121f94e0fe936fdbd142cc108f29e6db5b8018bb4099d6906020016108bc565b6001600160a01b038316610b9e576103318282610ba9565b610331838383610bc2565b5f385f3884865af16107625763b12d13eb5f526004601cfd5b816014528060345263a9059cbb60601b5f5260205f604460105f875af18060015f511416610c0257803d853b151710610c02576390b8ec185f526004601cfd5b505f603452505050565b5f60208284031215610c1c575f5ffd5b5035919050565b634e487b7160e01b5f52602160045260245ffd5b6020810160058310610c5757634e487b7160e01b5f52602160045260245ffd5b91905290565b5f5f60208385031215610c6e575f5ffd5b823567ffffffffffffffff811115610c84575f5ffd5b8301601f81018513610c94575f5ffd5b803567ffffffffffffffff811115610caa575f5ffd5b8560208260051b8401011115610cbe575f5ffd5b6020919091019590945092505050565b634e487b7160e01b5f52604160045260245ffd5b604051610160810167ffffffffffffffff81118282101715610d0657610d06610cce565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610d3557610d35610cce565b604052919050565b80356001600160a01b031981168114610d54575f5ffd5b919050565b80356001600160a01b0381168114610d54575f5ffd5b5f60208284031215610d7f575f5ffd5b813567ffffffffffffffff811115610d95575f5ffd5b8201601f81018413610da5575f5ffd5b803567ffffffffffffffff811115610dbf57610dbf610cce565b610dce60208260051b01610d0c565b8082825260208201915060206101608402850101925086831115610df0575f5ffd5b6020840193505b82841015610ec1576101608488031215610e0f575f5ffd5b610e17610ce2565b610e2085610d3d565b8152610e2e60208601610d59565b6020820152610e3f60408601610d59565b6040820152610e5060608601610d59565b60608201526080858101359082015260a0808601359082015260c08086013590820152610e7f60e08601610d59565b60e0820152610e916101008601610d59565b61010082015261012085810135908201526101408086013590820152825261016090930192602090910190610df7565b9695505050505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b60ff60f81b8816815260e060208201525f610f1760e0830189610ecb565b8281036040840152610f298189610ecb565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015610f7e578351835260209384019390920191600101610f60565b50909b9a5050505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b81516001600160a01b031916815261016081016020830151610fd060208401826001600160a01b03169052565b506040830151610feb60408401826001600160a01b03169052565b50606083015161100660608401826001600160a01b03169052565b506080830151608083015260a083015160a083015260c083015160c083015260e083015161103f60e08401826001600160a01b03169052565b5061010083015161105c6101008401826001600160a01b03169052565b5061012083015161012083015261014083015161014083015292915050565b634e487b7160e01b5f52601160045260245ffd5b808201808211156110a2576110a261107b565b92915050565b818103818111156110a2576110a261107b565b5f602082840312156110cb575f5ffd5b815180151581146110da575f5ffd5b939250505056fea264697066735822122053dc12bf2439bfea35d3663b369eadfcfbfe7ae1daa85dbbcf707e47612e0e8464736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.80
Net Worth in ETH
0.00027
Token Allocations
USDT
100.00%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| BASE | 100.00% | $0.998095 | 0.7987 | $0.7971 |
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.