Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Minimal Proxy Contract for 0xcd70bf33cfe59759851db21c83ea47b6b83bef6a
Contract Name:
EscrowSrc
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 { IERC20 } from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "solidity-utils/contracts/libraries/SafeERC20.sol"; import { AddressLib, Address } from "solidity-utils/contracts/libraries/AddressLib.sol"; import { Timelocks, TimelocksLib } from "./libraries/TimelocksLib.sol"; import { ImmutablesLib } from "./libraries/ImmutablesLib.sol"; import { IEscrowSrc } from "./interfaces/IEscrowSrc.sol"; import { BaseEscrow } from "./BaseEscrow.sol"; import { Escrow } from "./Escrow.sol"; /** * @title Source Escrow contract for cross-chain atomic swap. * @notice Contract to initially lock funds and then unlock them with verification of the secret presented. * @dev Funds are locked in at the time of contract deployment. For this Limit Order Protocol * calls the `EscrowFactory.postInteraction` function. * To perform any action, the caller must provide the same Immutables values used to deploy the clone contract. * @custom:security-contact [email protected] */ contract EscrowSrc is Escrow, IEscrowSrc { using AddressLib for Address; using ImmutablesLib for Immutables; using SafeERC20 for IERC20; using TimelocksLib for Timelocks; constructor(uint32 rescueDelay, IERC20 accessToken) BaseEscrow(rescueDelay, accessToken) {} /** * @notice See {IBaseEscrow-withdraw}. * @dev The function works on the time interval highlighted with capital letters: * ---- contract deployed --/-- finality --/-- PRIVATE WITHDRAWAL --/-- PUBLIC WITHDRAWAL --/-- * --/-- private cancellation --/-- public cancellation ---- */ function withdraw(bytes32 secret, Immutables calldata immutables) external onlyTaker(immutables) onlyAfter(immutables.timelocks.get(TimelocksLib.Stage.SrcWithdrawal)) onlyBefore(immutables.timelocks.get(TimelocksLib.Stage.SrcCancellation)) { _withdrawTo(secret, msg.sender, immutables); } /** * @notice See {IEscrowSrc-withdrawTo}. * @dev The function works on the time interval highlighted with capital letters: * ---- contract deployed --/-- finality --/-- PRIVATE WITHDRAWAL --/-- PUBLIC WITHDRAWAL --/-- * --/-- private cancellation --/-- public cancellation ---- */ function withdrawTo(bytes32 secret, address target, Immutables calldata immutables) external onlyTaker(immutables) onlyAfter(immutables.timelocks.get(TimelocksLib.Stage.SrcWithdrawal)) onlyBefore(immutables.timelocks.get(TimelocksLib.Stage.SrcCancellation)) { _withdrawTo(secret, target, immutables); } /** * @notice See {IEscrowSrc-publicWithdraw}. * @dev The function works on the time interval highlighted with capital letters: * ---- contract deployed --/-- finality --/-- private withdrawal --/-- PUBLIC WITHDRAWAL --/-- * --/-- private cancellation --/-- public cancellation ---- */ function publicWithdraw(bytes32 secret, Immutables calldata immutables) external onlyAccessTokenHolder() onlyAfter(immutables.timelocks.get(TimelocksLib.Stage.SrcPublicWithdrawal)) onlyBefore(immutables.timelocks.get(TimelocksLib.Stage.SrcCancellation)) { _withdrawTo(secret, immutables.taker.get(), immutables); } /** * @notice See {IBaseEscrow-cancel}. * @dev The function works on the time intervals highlighted with capital letters: * ---- contract deployed --/-- finality --/-- private withdrawal --/-- public withdrawal --/-- * --/-- PRIVATE CANCELLATION --/-- PUBLIC CANCELLATION ---- */ function cancel(Immutables calldata immutables) external onlyTaker(immutables) onlyAfter(immutables.timelocks.get(TimelocksLib.Stage.SrcCancellation)) { _cancel(immutables); } /** * @notice See {IEscrowSrc-publicCancel}. * @dev The function works on the time intervals highlighted with capital letters: * ---- contract deployed --/-- finality --/-- private withdrawal --/-- public withdrawal --/-- * --/-- private cancellation --/-- PUBLIC CANCELLATION ---- */ function publicCancel(Immutables calldata immutables) external onlyAccessTokenHolder() onlyAfter(immutables.timelocks.get(TimelocksLib.Stage.SrcPublicCancellation)) { _cancel(immutables); } /** * @dev Transfers ERC20 tokens to the target and native tokens to the caller. * @param secret The secret that unlocks the escrow. * @param target The address to transfer ERC20 tokens to. * @param immutables The immutable values used to deploy the clone contract. */ function _withdrawTo(bytes32 secret, address target, Immutables calldata immutables) internal onlyValidImmutables(immutables) onlyValidSecret(secret, immutables) { IERC20(immutables.token.get()).safeTransfer(target, immutables.amount); _ethTransfer(msg.sender, immutables.safetyDeposit); emit EscrowWithdrawal(secret); } /** * @dev Transfers ERC20 tokens to the maker and native tokens to the caller. * @param immutables The immutable values used to deploy the clone contract. */ function _cancel(Immutables calldata immutables) internal onlyValidImmutables(immutables) { IERC20(immutables.token.get()).safeTransfer(immutables.maker.get(), immutables.amount); _ethTransfer(msg.sender, immutables.safetyDeposit); emit EscrowCancelled(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ 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 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 pragma solidity ^0.8.0; type Address is uint256; /** * @notice AddressLib * @notice Library for working with addresses encoded as uint256 values, which can include flags in the highest bits. */ library AddressLib { uint256 private constant _LOW_160_BIT_MASK = (1 << 160) - 1; /** * @notice Returns the address representation of a uint256. * @param a The uint256 value to convert to an address. * @return The address representation of the provided uint256 value. */ function get(Address a) internal pure returns (address) { return address(uint160(Address.unwrap(a) & _LOW_160_BIT_MASK)); } /** * @notice Checks if a given flag is set for the provided address. * @param a The address to check for the flag. * @param flag The flag to check for in the provided address. * @return True if the provided flag is set in the address, false otherwise. */ function getFlag(Address a, uint256 flag) internal pure returns (bool) { return (Address.unwrap(a) & flag) != 0; } /** * @notice Returns a uint32 value stored at a specific bit offset in the provided address. * @param a The address containing the uint32 value. * @param offset The bit offset at which the uint32 value is stored. * @return The uint32 value stored in the address at the specified bit offset. */ function getUint32(Address a, uint256 offset) internal pure returns (uint32) { return uint32(Address.unwrap(a) >> offset); } /** * @notice Returns a uint64 value stored at a specific bit offset in the provided address. * @param a The address containing the uint64 value. * @param offset The bit offset at which the uint64 value is stored. * @return The uint64 value stored in the address at the specified bit offset. */ function getUint64(Address a, uint256 offset) internal pure returns (uint64) { return uint64(Address.unwrap(a) >> offset); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @dev Timelocks for the source and the destination chains plus the deployment timestamp. * Timelocks store the number of seconds from the time the contract is deployed to the start of a specific period. * For illustrative purposes, it is possible to describe timelocks by two structures: * struct SrcTimelocks { * uint256 withdrawal; * uint256 publicWithdrawal; * uint256 cancellation; * uint256 publicCancellation; * } * * struct DstTimelocks { * uint256 withdrawal; * uint256 publicWithdrawal; * uint256 cancellation; * } * * withdrawal: Period when only the taker with a secret can withdraw tokens for taker (source chain) or maker (destination chain). * publicWithdrawal: Period when anyone with a secret can withdraw tokens for taker (source chain) or maker (destination chain). * cancellation: Period when escrow can only be cancelled by the taker. * publicCancellation: Period when escrow can be cancelled by anyone. * * @custom:security-contact [email protected] */ type Timelocks is uint256; /** * @title Timelocks library for compact storage of timelocks in a uint256. */ library TimelocksLib { enum Stage { SrcWithdrawal, SrcPublicWithdrawal, SrcCancellation, SrcPublicCancellation, DstWithdrawal, DstPublicWithdrawal, DstCancellation } uint256 private constant _DEPLOYED_AT_MASK = 0xffffffff00000000000000000000000000000000000000000000000000000000; uint256 private constant _DEPLOYED_AT_OFFSET = 224; /** * @notice Sets the Escrow deployment timestamp. * @param timelocks The timelocks to set the deployment timestamp to. * @param value The new Escrow deployment timestamp. * @return The timelocks with the deployment timestamp set. */ function setDeployedAt(Timelocks timelocks, uint256 value) internal pure returns (Timelocks) { return Timelocks.wrap((Timelocks.unwrap(timelocks) & ~uint256(_DEPLOYED_AT_MASK)) | value << _DEPLOYED_AT_OFFSET); } /** * @notice Returns the start of the rescue period. * @param timelocks The timelocks to get the rescue delay from. * @return The start of the rescue period. */ function rescueStart(Timelocks timelocks, uint256 rescueDelay) internal pure returns (uint256) { unchecked { return rescueDelay + (Timelocks.unwrap(timelocks) >> _DEPLOYED_AT_OFFSET); } } /** * @notice Returns the timelock value for the given stage. * @param timelocks The timelocks to get the value from. * @param stage The stage to get the value for. * @return The timelock value for the given stage. */ function get(Timelocks timelocks, Stage stage) internal pure returns (uint256) { uint256 data = Timelocks.unwrap(timelocks); uint256 bitShift = uint256(stage) * 32; // The maximum uint32 value will be reached in 2106. return (data >> _DEPLOYED_AT_OFFSET) + uint32(data >> bitShift); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { IBaseEscrow } from "../interfaces/IBaseEscrow.sol"; /** * @title Library for escrow immutables. * @custom:security-contact [email protected] */ library ImmutablesLib { uint256 internal constant ESCROW_IMMUTABLES_SIZE = 0x100; /** * @notice Returns the hash of the immutables. * @param immutables The immutables to hash. * @return ret The computed hash. */ function hash(IBaseEscrow.Immutables calldata immutables) internal pure returns(bytes32 ret) { assembly ("memory-safe") { let ptr := mload(0x40) calldatacopy(ptr, immutables, ESCROW_IMMUTABLES_SIZE) ret := keccak256(ptr, ESCROW_IMMUTABLES_SIZE) } } /** * @notice Returns the hash of the immutables. * @param immutables The immutables to hash. * @return ret The computed hash. */ function hashMem(IBaseEscrow.Immutables memory immutables) internal pure returns(bytes32 ret) { assembly ("memory-safe") { ret := keccak256(immutables, ESCROW_IMMUTABLES_SIZE) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IEscrow } from "./IEscrow.sol"; /** * @title Source Escrow interface for cross-chain atomic swap. * @notice Interface implies locking funds initially and then unlocking them with verification of the secret presented. * @custom:security-contact [email protected] */ interface IEscrowSrc is IEscrow { /** * @notice Withdraws funds to a specified target. * @dev Withdrawal can only be made during the withdrawal period and with secret with hash matches the hashlock. * The safety deposit is sent to the caller. * @param secret The secret that unlocks the escrow. * @param target The address to withdraw the funds to. * @param immutables The immutables of the escrow contract. */ function withdrawTo(bytes32 secret, address target, IEscrow.Immutables calldata immutables) external; /** * @notice Withdraws funds to the taker. * @dev Withdrawal can only be made during the public withdrawal period and with secret with hash matches the hashlock. * @param secret The secret that unlocks the escrow. * @param immutables The immutables of the escrow contract. */ function publicWithdraw(bytes32 secret, Immutables calldata immutables) external; /** * @notice Cancels the escrow and returns tokens to the maker. * @dev The escrow can only be cancelled during the public cancellation period. * The safety deposit is sent to the caller. * @param immutables The immutables of the escrow contract. */ function publicCancel(IEscrow.Immutables calldata immutables) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import { IERC20 } from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import { AddressLib, Address } from "solidity-utils/contracts/libraries/AddressLib.sol"; import { SafeERC20 } from "solidity-utils/contracts/libraries/SafeERC20.sol"; import { ImmutablesLib } from "./libraries/ImmutablesLib.sol"; import { Timelocks, TimelocksLib } from "./libraries/TimelocksLib.sol"; import { IBaseEscrow } from "./interfaces/IBaseEscrow.sol"; /** * @title Base abstract Escrow contract for cross-chain atomic swap. * @dev {IBaseEscrow-withdraw}, {IBaseEscrow-cancel} and _validateImmutables functions must be implemented in the derived contracts. * @custom:security-contact [email protected] */ abstract contract BaseEscrow is IBaseEscrow { using AddressLib for Address; using SafeERC20 for IERC20; using TimelocksLib for Timelocks; using ImmutablesLib for Immutables; // Token that is used to access public withdraw or cancel functions. IERC20 private immutable _ACCESS_TOKEN; /// @notice See {IBaseEscrow-RESCUE_DELAY}. uint256 public immutable RESCUE_DELAY; /// @notice See {IBaseEscrow-FACTORY}. address public immutable FACTORY = msg.sender; constructor(uint32 rescueDelay, IERC20 accessToken) { RESCUE_DELAY = rescueDelay; _ACCESS_TOKEN = accessToken; } modifier onlyTaker(Immutables calldata immutables) { if (msg.sender != immutables.taker.get()) revert InvalidCaller(); _; } modifier onlyValidImmutables(Immutables calldata immutables) virtual { _validateImmutables(immutables); _; } modifier onlyValidSecret(bytes32 secret, Immutables calldata immutables) { if (_keccakBytes32(secret) != immutables.hashlock) revert InvalidSecret(); _; } modifier onlyAfter(uint256 start) { if (block.timestamp < start) revert InvalidTime(); _; } modifier onlyBefore(uint256 stop) { if (block.timestamp >= stop) revert InvalidTime(); _; } modifier onlyAccessTokenHolder() { if (_ACCESS_TOKEN.balanceOf(msg.sender) == 0) revert InvalidCaller(); _; } /** * @notice See {IBaseEscrow-rescueFunds}. */ function rescueFunds(address token, uint256 amount, Immutables calldata immutables) external onlyTaker(immutables) onlyValidImmutables(immutables) onlyAfter(immutables.timelocks.rescueStart(RESCUE_DELAY)) { _uniTransfer(token, msg.sender, amount); emit FundsRescued(token, amount); } /** * @dev Transfers ERC20 or native tokens to the recipient. */ function _uniTransfer(address token, address to, uint256 amount) internal { if (token == address(0)) { _ethTransfer(to, amount); } else { IERC20(token).safeTransfer(to, amount); } } /** * @dev Transfers native tokens to the recipient. */ function _ethTransfer(address to, uint256 amount) internal { (bool success,) = to.call{ value: amount }(""); if (!success) revert NativeTokenSendingFailure(); } /** * @dev Should verify that the computed escrow address matches the address of this contract. */ function _validateImmutables(Immutables calldata immutables) internal view virtual; /** * @dev Computes the Keccak-256 hash of the secret. * @param secret The secret that unlocks the escrow. * @return ret The computed hash. */ function _keccakBytes32(bytes32 secret) private pure returns (bytes32 ret) { assembly ("memory-safe") { mstore(0, secret) ret := keccak256(0, 0x20) } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import { Create2 } from "openzeppelin-contracts/contracts/utils/Create2.sol"; import { ImmutablesLib } from "./libraries/ImmutablesLib.sol"; import { ProxyHashLib } from "./libraries/ProxyHashLib.sol"; import { IEscrow } from "./interfaces/IEscrow.sol"; import { BaseEscrow } from "./BaseEscrow.sol"; /** * @title Abstract Escrow contract for cross-chain atomic swap. * @dev {IBaseEscrow-withdraw} and {IBaseEscrow-cancel} functions must be implemented in the derived contracts. * @custom:security-contact [email protected] */ abstract contract Escrow is BaseEscrow, IEscrow { using ImmutablesLib for Immutables; /// @notice See {IEscrow-PROXY_BYTECODE_HASH}. bytes32 public immutable PROXY_BYTECODE_HASH = ProxyHashLib.computeProxyBytecodeHash(address(this)); /** * @dev Verifies that the computed escrow address matches the address of this contract. */ function _validateImmutables(Immutables calldata immutables) internal view virtual override { bytes32 salt = immutables.hash(); if (Create2.computeAddress(salt, PROXY_BYTECODE_HASH, FACTORY) != address(this)) { revert InvalidImmutables(); } } }
// 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 ERC-20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[ERC-2612]. * * Adds the {permit} method, which can be used to change an account's ERC-20 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 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 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; /** * @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; 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; /** * @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 { Address } from "solidity-utils/contracts/libraries/AddressLib.sol"; import { Timelocks } from "../libraries/TimelocksLib.sol"; /** * @title Base Escrow interface for cross-chain atomic swap. * @notice Interface implies locking funds initially and then unlocking them with verification of the secret presented. * @custom:security-contact [email protected] */ interface IBaseEscrow { struct Immutables { bytes32 orderHash; bytes32 hashlock; // Hash of the secret. Address maker; Address taker; Address token; uint256 amount; uint256 safetyDeposit; Timelocks timelocks; } /** * @notice Emitted on escrow cancellation. */ event EscrowCancelled(); /** * @notice Emitted when funds are rescued. * @param token The address of the token rescued. Zero address for native token. * @param amount The amount of tokens rescued. */ event FundsRescued(address token, uint256 amount); /** * @notice Emitted on successful withdrawal. * @param secret The secret that unlocks the escrow. */ event EscrowWithdrawal(bytes32 secret); error InvalidCaller(); error InvalidImmutables(); error InvalidSecret(); error InvalidTime(); error NativeTokenSendingFailure(); /* solhint-disable func-name-mixedcase */ /// @notice Returns the delay for rescuing funds from the escrow. function RESCUE_DELAY() external view returns (uint256); /// @notice Returns the address of the factory that created the escrow. function FACTORY() external view returns (address); /* solhint-enable func-name-mixedcase */ /** * @notice Withdraws funds to a predetermined recipient. * @dev Withdrawal can only be made during the withdrawal period and with secret with hash matches the hashlock. * The safety deposit is sent to the caller. * @param secret The secret that unlocks the escrow. * @param immutables The immutables of the escrow contract. */ function withdraw(bytes32 secret, Immutables calldata immutables) external; /** * @notice Cancels the escrow and returns tokens to a predetermined recipient. * @dev The escrow can only be cancelled during the cancellation period. * The safety deposit is sent to the caller. * @param immutables The immutables of the escrow contract. */ function cancel(Immutables calldata immutables) external; /** * @notice Rescues funds from the escrow. * @dev Funds can only be rescued by the taker after the rescue delay. * @param token The address of the token to rescue. Zero address for native token. * @param amount The amount of tokens to rescue. * @param immutables The immutables of the escrow contract. */ function rescueFunds(address token, uint256 amount, Immutables calldata immutables) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IBaseEscrow } from "./IBaseEscrow.sol"; /** * @title Escrow interface for cross-chain atomic swap. * @notice Interface implies locking funds initially and then unlocking them with verification of the secret presented. * @custom:security-contact [email protected] */ interface IEscrow is IBaseEscrow { /// @notice Returns the bytecode hash of the proxy contract. function PROXY_BYTECODE_HASH() external view returns (bytes32); // solhint-disable-line func-name-mixedcase }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Create2.sol) pragma solidity ^0.8.20; import {Errors} from "./Errors.sol"; /** * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. * `CREATE2` can be used to compute in advance the address where a smart * contract will be deployed, which allows for interesting new mechanisms known * as 'counterfactual interactions'. * * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more * information. */ library Create2 { /** * @dev There's no code to deploy. */ error Create2EmptyBytecode(); /** * @dev Deploys a contract using `CREATE2`. The address where the contract * will be deployed can be known in advance via {computeAddress}. * * The bytecode for a contract can be obtained from Solidity with * `type(contractName).creationCode`. * * Requirements: * * - `bytecode` must not be empty. * - `salt` must have not been used for `bytecode` already. * - the factory must have a balance of at least `amount`. * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. */ function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) { if (address(this).balance < amount) { revert Errors.InsufficientBalance(address(this).balance, amount); } if (bytecode.length == 0) { revert Create2EmptyBytecode(); } /// @solidity memory-safe-assembly assembly { addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt) } if (addr == address(0)) { revert Errors.FailedDeployment(); } } /** * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the * `bytecodeHash` or `salt` will result in a new destination address. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) { return computeAddress(salt, bytecodeHash, address(this)); } /** * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) // Get free memory pointer // | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... | // |-------------------|---------------------------------------------------------------------------| // | bytecodeHash | CCCCCCCCCCCCC...CC | // | salt | BBBBBBBBBBBBB...BB | // | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA | // | 0xFF | FF | // |-------------------|---------------------------------------------------------------------------| // | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC | // | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ | mstore(add(ptr, 0x40), bytecodeHash) mstore(add(ptr, 0x20), salt) mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff mstore8(start, 0xff) addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @title Library to compute the hash of the proxy bytecode. * @custom:security-contact [email protected] */ library ProxyHashLib { /** * @notice Returns the hash of the proxy bytecode concatenated with the implementation address. * @param implementation The address of the contract to clone. * @return bytecodeHash The hash of the resulting bytecode. */ function computeProxyBytecodeHash(address implementation) internal pure returns (bytes32 bytecodeHash) { assembly ("memory-safe") { // Stores the bytecode after address mstore(0x20, 0x5af43d82803e903d91602b57fd5bf3) // implementation address mstore(0x11, implementation) // Packs the first 3 bytes of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0x88, implementation), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) bytecodeHash := keccak256(0x09, 0x37) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @dev Collection of common custom errors used in multiple contracts * * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library. * It is recommended to avoid relying on the error API for critical functionality. */ library Errors { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error InsufficientBalance(uint256 balance, uint256 needed); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedCall(); /** * @dev The deployment failed. */ error FailedDeployment(); }
{ "remappings": [ "@1inch/limit-order-protocol-contract/=lib/limit-order-protocol/", "@1inch/limit-order-settlement/=lib/limit-order-settlement/", "@1inch/solidity-utils/=lib/solidity-utils/", "solidity-utils/=lib/solidity-utils/", "limit-order-protocol/=lib/limit-order-protocol/", "limit-order-settlement/=lib/limit-order-settlement/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "murky/=lib/murky/", "openzeppelin-contracts/=lib/openzeppelin-contracts/" ], "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "shanghai", "viaIR": true, "libraries": {} }
[{"inputs":[{"internalType":"uint32","name":"rescueDelay","type":"uint32"},{"internalType":"contract IERC20","name":"accessToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[],"name":"InvalidImmutables","type":"error"},{"inputs":[],"name":"InvalidSecret","type":"error"},{"inputs":[],"name":"InvalidTime","type":"error"},{"inputs":[],"name":"NativeTokenSendingFailure","type":"error"},{"inputs":[],"name":"SafeTransferFailed","type":"error"},{"anonymous":false,"inputs":[],"name":"EscrowCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"secret","type":"bytes32"}],"name":"EscrowWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FundsRescued","type":"event"},{"inputs":[],"name":"FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROXY_BYTECODE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESCUE_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"bytes32","name":"hashlock","type":"bytes32"},{"internalType":"Address","name":"maker","type":"uint256"},{"internalType":"Address","name":"taker","type":"uint256"},{"internalType":"Address","name":"token","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"safetyDeposit","type":"uint256"},{"internalType":"Timelocks","name":"timelocks","type":"uint256"}],"internalType":"struct IBaseEscrow.Immutables","name":"immutables","type":"tuple"}],"name":"cancel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"bytes32","name":"hashlock","type":"bytes32"},{"internalType":"Address","name":"maker","type":"uint256"},{"internalType":"Address","name":"taker","type":"uint256"},{"internalType":"Address","name":"token","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"safetyDeposit","type":"uint256"},{"internalType":"Timelocks","name":"timelocks","type":"uint256"}],"internalType":"struct IBaseEscrow.Immutables","name":"immutables","type":"tuple"}],"name":"publicCancel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"secret","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"bytes32","name":"hashlock","type":"bytes32"},{"internalType":"Address","name":"maker","type":"uint256"},{"internalType":"Address","name":"taker","type":"uint256"},{"internalType":"Address","name":"token","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"safetyDeposit","type":"uint256"},{"internalType":"Timelocks","name":"timelocks","type":"uint256"}],"internalType":"struct IBaseEscrow.Immutables","name":"immutables","type":"tuple"}],"name":"publicWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"bytes32","name":"hashlock","type":"bytes32"},{"internalType":"Address","name":"maker","type":"uint256"},{"internalType":"Address","name":"taker","type":"uint256"},{"internalType":"Address","name":"token","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"safetyDeposit","type":"uint256"},{"internalType":"Timelocks","name":"timelocks","type":"uint256"}],"internalType":"struct IBaseEscrow.Immutables","name":"immutables","type":"tuple"}],"name":"rescueFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"secret","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"bytes32","name":"hashlock","type":"bytes32"},{"internalType":"Address","name":"maker","type":"uint256"},{"internalType":"Address","name":"taker","type":"uint256"},{"internalType":"Address","name":"token","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"safetyDeposit","type":"uint256"},{"internalType":"Timelocks","name":"timelocks","type":"uint256"}],"internalType":"struct IBaseEscrow.Immutables","name":"immutables","type":"tuple"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"secret","type":"bytes32"},{"internalType":"address","name":"target","type":"address"},{"components":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"bytes32","name":"hashlock","type":"bytes32"},{"internalType":"Address","name":"maker","type":"uint256"},{"internalType":"Address","name":"taker","type":"uint256"},{"internalType":"Address","name":"token","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"safetyDeposit","type":"uint256"},{"internalType":"Timelocks","name":"timelocks","type":"uint256"}],"internalType":"struct IBaseEscrow.Immutables","name":"immutables","type":"tuple"}],"name":"withdrawTo","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.