Source Code
Overview
S Balance
S Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
CrosschainFacet
Compiler Version
v0.8.25+commit.b61c2a91
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {OpsCrosschain, Ops} from "./Ops.sol";
import {ICrosschainFacet, ICoreFacet} from "./interfaces/ICrosschainFacet.sol";
import {IERC20WithPermit} from "../interfaces/IERC20WithPermit.sol";
import {IAddressBook} from "../interfaces/IAddressBook.sol";
import {IPortalV2} from "../interfaces/IPortalV2.sol";
import {ISynthesisV2} from "../interfaces/ISynthesisV2.sol";
import {ISynthAdapter} from "../interfaces/ISynth.sol";
import {IWhitelist} from "../interfaces/IWhitelist.sol";
import {IWETH9} from "../interfaces/IWETH.sol";
import {CoreFacetStorage, ProcessedOps} from "./libraries/CoreFacetStorage.sol";
import {TypecastLib} from "../utils/TypecastLib.sol";
contract CrosschainFacet is ICrosschainFacet, OpsCrosschain, Ops {
using CoreFacetStorage for CoreFacetStorage.DS;
/// @inheritdoc ICrosschainFacet
function executeCrosschainOp(
bool isOpHalfDone,
bytes32 op,
bytes32 nextOp,
bytes memory params,
ICoreFacet.MaskedParams memory prevMaskedParams
)
external
payable
onlyDiamond
returns (
uint64 chainIdTo,
bytes memory updatedParams,
ICoreFacet.MaskedParams memory maskedParams,
ICoreFacet.ExecutionResult result
)
{
CoreFacetStorage.DS storage ds = CoreFacetStorage.ds();
result = ICoreFacet.ExecutionResult.Succeeded;
if (PERMIT_CODE == op) {
ICrosschainFacet.PermitParams memory p = abi.decode(params, (ICrosschainFacet.PermitParams));
address token = TypecastLib.castToAddress(p.token);
address owner = TypecastLib.castToAddress(p.owner);
try IERC20WithPermit(token).permit(
owner,
address(this),
p.amount,
p.deadline,
p.v,
p.r,
p.s
) {
} catch {
require(IERC20(token).allowance(owner, address(this)) >= p.amount, "CrosschainFacet: permit failure");
}
} else if (LOCK_MINT_CODE == op || BURN_UNLOCK_CODE == op || BURN_MINT_CODE == op) {
ICrosschainFacet.SynthParams memory p = abi.decode(params, (ICrosschainFacet.SynthParams));
if (isOpHalfDone == false) {
(p.amountIn, p.from, p.emergencyTo) = checkMaskedParams(p.amountIn, p.from, p.emergencyTo, prevMaskedParams);
p.to = checkTo(p.to, p.emergencyTo, p.chainIdTo, op, nextOp);
address tokenIn = TypecastLib.castToAddress(p.tokenIn);
address from = TypecastLib.castToAddress(p.from);
if (LOCK_MINT_CODE == op) {
_lock(p);
p.tokenInChainIdFrom = uint64(block.chainid);
} else {
address synthesis = IAddressBook(ds.addressBook).synthesis(uint64(block.chainid));
address possibleAdapter = ISynthesisV2(synthesis).synthBySynth(tokenIn);
if (possibleAdapter != address(0)) {
if (from != synthesis) {
SafeERC20.safeTransferFrom(IERC20(tokenIn), from, synthesis, p.amountIn);
}
from = synthesis;
p.from = TypecastLib.castToBytes32(synthesis);
} else {
// check for backward compatibility with deployed SynthesisV2
address whitelist = IAddressBook(ds.addressBook).whitelist();
require(IWhitelist(whitelist).tokenState(tokenIn) >= 0, "CrosschainFacet: synth must be whitelisted");
possibleAdapter = tokenIn;
}
ISynthesisV2(synthesis).burn(tokenIn, p.amountIn, from, TypecastLib.castToAddress(p.to), p.chainIdTo);
ISynthAdapter synthImpl = ISynthAdapter(possibleAdapter);
p.tokenIn = TypecastLib.castToBytes32(synthImpl.originalToken());
p.tokenInChainIdFrom = synthImpl.chainIdFrom();
}
chainIdTo = p.chainIdTo;
updatedParams = abi.encode(p);
} else {
require(
ds.processedOps[CoreFacetStorage.currentRequestId()].crossChainOpState == uint8(ICoreFacet.CrossChainOpState.Unknown),
"CrosschainFacet: op processed"
);
ds.processedOps[CoreFacetStorage.currentRequestId()] = ProcessedOps({
hashSynthParams: keccak256(params),
crossChainOpState: uint8(ICoreFacet.CrossChainOpState.Succeeded)
});
// TODO: check
if (p.to == 0) {
p.to = checkTo(p.to, p.emergencyTo, p.chainIdTo, op, nextOp);
}
maskedParams.amountOut = BURN_UNLOCK_CODE == op ? _unlock(p) : _mint(p);
maskedParams.to = p.to;
maskedParams.emergencyTo = p.emergencyTo;
}
} else if (WRAP_CODE == op || UNWRAP_CODE == op) {
ICrosschainFacet.WrapParams memory p = abi.decode(params, (ICrosschainFacet.WrapParams));
(p.amountIn, p.from, ) = checkMaskedParams(p.amountIn, p.from, bytes32(0), prevMaskedParams);
p.to = checkTo(p.to, bytes32(0), uint64(block.chainid), op, nextOp);
maskedParams.amountOut = WRAP_CODE == op ? _wrap(p) : _unwrap(p);
maskedParams.to = p.to;
maskedParams.emergencyTo = prevMaskedParams.emergencyTo;
} else if (EMERGENCY_UNLOCK_CODE == op || EMERGENCY_MINT_CODE == op) {
ICrosschainFacet.CancelParams memory p = abi.decode(params, (ICrosschainFacet.CancelParams));
if (isOpHalfDone == false) {
ProcessedOps memory processedOps = ds.processedOps[p.requestId];
if (processedOps.crossChainOpState == uint8(ICoreFacet.CrossChainOpState.Unknown)) {
require(p.hashSynthParams == bytes32(0), "CrosschainFacet: wrong hashSynthParams - not zero");
ds.processedOps[p.requestId].crossChainOpState = uint8(ICoreFacet.CrossChainOpState.Reverted);
} else if (processedOps.crossChainOpState == uint8(ICoreFacet.CrossChainOpState.Succeeded)){
require(p.hashSynthParams == processedOps.hashSynthParams, "CrosschainFacet: wrong hashSynthParams - not equal");
require(p.hashSynthParams != keccak256(abi.encode(p.emergencyParams)), "CrosschainFacet: wrong emergency parameters - equal");
}
chainIdTo = p.chainIdTo;
} else {
bytes32 hashSynthParams = ds.startedOps[p.requestId];
require(hashSynthParams != 0, "CrosschainFacet: op not started");
if (p.hashSynthParams != bytes32(0)) {
require(hashSynthParams != p.hashSynthParams, "CrosschainFacet: wrong hashSynthParams - equal");
}
require(hashSynthParams == keccak256(abi.encode(p.emergencyParams)), "CrosschainFacet: wrong emergency parameters - not equal");
require(CoreFacetStorage.currentChainIdFrom() == p.emergencyParams.chainIdTo, "CrosschainFacet: wrong emergency init");
delete ds.startedOps[p.requestId];
if (EMERGENCY_UNLOCK_CODE == op) {
maskedParams.amountOut = _emergencyUnlock(p.emergencyParams);
} else {
maskedParams.amountOut = _emergencyMint(p.emergencyParams);
}
}
} else {
// maskedParams = prevMaskedParams; // TODO check
result = ICoreFacet.ExecutionResult.Failed;
}
}
/// @notice Locks tokens into the Portal contract on the origin chain.
/// @param p SynthParams containing token info and destination.
function _lock(ICrosschainFacet.SynthParams memory p) private {
IAddressBook addressBook = IAddressBook(CoreFacetStorage.ds().addressBook);
address portal = addressBook.portal(uint64(block.chainid));
address tokenIn = TypecastLib.castToAddress(p.tokenIn);
address from = TypecastLib.castToAddress(p.from);
if (from != portal) {
SafeERC20.safeTransferFrom(
IERC20(tokenIn),
from,
portal,
p.amountIn
);
}
IPortalV2(portal).lock(
tokenIn,
p.amountIn,
from,
TypecastLib.castToAddress(p.to)
);
}
/// @notice Unlocks tokens via the Portal contract on the destination chain.
/// @param p SynthParams for unlocking.
/// @return amountOut Amount of tokens unlocked.
function _unlock(ICrosschainFacet.SynthParams memory p) private returns (uint256 amountOut) {
address portal = IAddressBook(CoreFacetStorage.ds().addressBook).portal(uint64(block.chainid));
amountOut = IPortalV2(portal).unlock(
TypecastLib.castToAddress(p.tokenIn),
p.amountIn,
TypecastLib.castToAddress(p.from),
TypecastLib.castToAddress(p.to)
);
}
/// @notice Performs an emergency unlock of tokens via Portal.
/// @param p SynthParams for the operation.
/// @return amountOut Amount unlocked.
function _emergencyUnlock(ICrosschainFacet.SynthParams memory p) private returns (uint256 amountOut) {
address portal = IAddressBook(CoreFacetStorage.ds().addressBook).portal(uint64(block.chainid));
amountOut = IPortalV2(portal).emergencyUnlock(
TypecastLib.castToAddress(p.tokenIn),
p.amountIn,
TypecastLib.castToAddress(p.from),
TypecastLib.castToAddress(p.emergencyTo)
);
}
/// @notice Mints synthetic tokens on the destination chain via Synthesis.
/// @param p SynthParams for minting.
/// @return amountOut Amount minted.
function _mint(ICrosschainFacet.SynthParams memory p) private returns (uint256 amountOut) {
address synthesis = IAddressBook(CoreFacetStorage.ds().addressBook).synthesis(uint64(block.chainid));
amountOut = ISynthesisV2(synthesis).mint(
TypecastLib.castToAddress(p.tokenIn),
p.amountIn,
TypecastLib.castToAddress(p.from),
TypecastLib.castToAddress(p.to),
p.tokenInChainIdFrom
);
}
/// @notice Mints tokens in an emergency (e.g. bridge recovery).
/// @param p SynthParams containing fallback minting details.
/// @return amountOut Amount minted.
function _emergencyMint(ICrosschainFacet.SynthParams memory p) private returns (uint256 amountOut) {
address synthesis = IAddressBook(CoreFacetStorage.ds().addressBook).synthesis(uint64(block.chainid));
address tokenIn = ISynthesisV2(synthesis).synthByOriginal(p.tokenInChainIdFrom, TypecastLib.castToAddress(p.tokenIn));
p.tokenIn = TypecastLib.castToBytes32(tokenIn);
amountOut = ISynthesisV2(synthesis).emergencyMint(
tokenIn,
p.amountIn,
TypecastLib.castToAddress(p.from),
TypecastLib.castToAddress(p.emergencyTo)
);
}
/// @notice Wraps ETH into WETH and transfers it to recipient.
/// @param p WrapParams containing token and amount info.
/// @return amountOut Wrapped token amount (equal to input).
function _wrap(ICrosschainFacet.WrapParams memory p) private returns (uint256 amountOut) {
if (CoreFacetStorage.isOriginNetwork()) {
CoreFacetStorage.consumeMsgValue(p.amountIn);
} else {
require(msg.value == 0, "CrosschainFacet: unexpected msg.value");
require(address(this).balance >= p.amountIn, "CrosschainFacet: insufficient ETH");
}
address tokenIn = TypecastLib.castToAddress(p.tokenIn);
IWETH9(tokenIn).deposit{ value: p.amountIn }();
SafeERC20.safeTransfer(IERC20(tokenIn), TypecastLib.castToAddress(p.to), p.amountIn);
amountOut = p.amountIn;
}
/// @notice Unwraps WETH into ETH and sends to recipient.
/// @param p WrapParams containing token and amount info.
/// @return amountOut Unwrapped ETH amount (equal to input).
function _unwrap(ICrosschainFacet.WrapParams memory p) private returns (uint256 amountOut) {
address from = TypecastLib.castToAddress(p.from);
address tokenIn = TypecastLib.castToAddress(p.tokenIn);
if (from != address(this)) {
SafeERC20.safeTransferFrom(IERC20(tokenIn), from, address(this), p.amountIn);
}
IWETH9(tokenIn).withdraw(p.amountIn);
(bool sent, ) = TypecastLib.castToAddress(p.to).call{ value: p.amountIn }("");
require(sent, "CrosschainFacet: failed to send ETH");
amountOut = p.amountIn;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
pragma solidity ^0.8.0;
/**
* @title Counters
* @author Matt Condon (@shrugs)
* @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
* of elements in a mapping, issuing ERC721 ids, or counting request ids.
*
* Include with `using Counters for Counters.Counter;`
*/
library Counters {
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
// this feature: see https://github.com/ethereum/solidity/issues/4637
uint256 _value; // default: 0
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
unchecked {
counter._value += 1;
}
}
function decrement(Counter storage counter) internal {
uint256 value = counter._value;
require(value > 0, "Counter: decrement overflow");
unchecked {
counter._value = value - 1;
}
}
function reset(Counter storage counter) internal {
counter._value = 0;
}
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity ^0.8.17;
interface IAddressBook {
/// @dev returns portal by given chainId
function portal(uint64 chainId) external view returns (address);
/// @dev returns synthesis by given chainId
function synthesis(uint64 chainId) external view returns (address);
/// @dev returns router by given chainId
function router(uint64 chainId) external view returns (address);
/// @dev returns portal by given chainId
function portalV3(uint64 chainId) external view returns (bytes32);
/// @dev returns synthesis by given chainId
function synthesisV3(uint64 chainId) external view returns (bytes32);
/// @dev returns router by given chainId
function routerV3(uint64 chainId) external view returns (bytes32);
/// @dev returns whitelist
function whitelist() external view returns (address);
/// @dev returns treasury
function treasury() external view returns (address);
/// @dev returns gateKeeper
function gateKeeper() external view returns (address);
/// @dev returns receiver
function receiver() external view returns (address);
/// @dev returns wrapped native asset (WETH)
function WETH() external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2023 - all rights reserved
pragma solidity ^0.8.17;
interface IERC20WithPermit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2023 - all rights reserved
pragma solidity ^0.8.17;
interface IPortalV2 {
function lock(
address token,
uint256 amount,
address from,
address to
) external;
function unlock(
address token,
uint256 amount,
address from,
address to
) external returns (uint256);
function emergencyUnlock(
address token,
uint256 amount,
address from,
address to
) external returns (uint256);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2023 - all rights reserved
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @dev Should be implemented by "treasury" contract in cases when third party token used instead of our synth.
*
* Mint\Burn can be implemented as Lock\Unlock in treasury contract.
*/
interface ISynthAdapter {
enum SynthType { Unknown, DefaultSynth, CustomSynth, ThirdPartySynth, ThirdPartyToken }
function mint(address account, uint256 amount) external;
function burn(address account, uint256 amount) external;
function setCap(uint256) external;
function decimals() external view returns (uint8);
function originalToken() external view returns (address);
function synthToken() external view returns (address);
function chainIdFrom() external view returns (uint64); // TODO what if token native in 2-3-4 chains? // []
function chainSymbolFrom() external view returns (string memory);
function synthType() external view returns (uint8);
function cap() external view returns (uint256);
event CapSet(uint256 cap);
}
interface ISynthERC20 is ISynthAdapter, IERC20 {
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2023 - all rights reserved
pragma solidity ^0.8.17;
interface ISynthesisV2 {
function synthByOriginal(uint64 chainIdFrom, address otoken) external view returns (address stoken);
function synthBySynth(address stoken) external view returns (address adapter);
function mint(
address token,
uint256 amount,
address from,
address to,
uint64 chainIdFrom
) external returns (uint256 amountOut);
function emergencyMint(
address token,
uint256 amount,
address from,
address to
) external returns (uint256 amountOut);
function burn(
address stoken,
uint256 amount,
address from,
address to,
uint64 chainIdTo
) external;
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2023 - all rights reserved
pragma solidity ^0.8.17;
interface IWETH9 {
function withdraw(uint wad) external;
function deposit() external payable;
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2023 - all rights reserved
pragma solidity ^0.8.17;
interface IWhitelist {
enum TokenState { NotSet, InOut }
enum PoolState { NotSet, AddSwapRemove }
struct TokenStatus {
address token;
uint256 min;
uint256 max;
uint256 bridgeFee;
TokenState state;
}
struct PoolStatus {
address pool;
uint256 aggregationFee;
PoolState state;
}
function tokenMin(address token) external view returns (uint256);
function tokenMax(address token) external view returns (uint256);
function tokenMinMax(address token) external view returns (uint256, uint256);
function bridgeFee(address token) external view returns (uint256);
function tokenState(address token) external view returns (uint8);
function tokenStatus(address token) external view returns (TokenStatus memory);
function tokens(uint256 offset, uint256 count) external view returns (TokenStatus[] memory);
function aggregationFee(address pool) external view returns (uint256);
function poolState(address pool) external view returns (uint8);
function poolStatus(address pool) external view returns (PoolStatus memory);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
import {ICoreFacet} from "./ICoreFacet.sol";
interface IBreakFacet {
/// @notice Break operation attempted as the first step of a resumed tail.
/// @dev Used by {resumeBr} to forbid placing `br` at index 0 of the resumed pipeline.
error BrFirstStepForbidden();
/// @notice Previous step output was not routed back into the router.
/// @dev Thrown when `prevMaskedParams.to` is not equal to the router address during `br`,
/// meaning the router does not currently custody the funds to be escrowed.
error BrPrevToNotRouter();
/// @notice Break with this requestId already exists.
/// @dev Signals that a new `br` tried to reuse a `requestId` whose status is not `None`.
error BrAlreadyExists();
/// @notice Nothing to stash for this break.
/// @dev Thrown when the previous step produced `amountOut == 0`, so there is no value
/// to put under break escrow.
error BrNothingToStash();
/// @notice Invalid break state.
/// @dev Used as a generic internal-safety error, for example when `requestId` is zero
/// or other invariant assumptions do not hold.
error BrInternalState();
/// @notice Break metadata not found.
/// @dev Indicates that no record exists in storage for the specified `requestId`.
error BrNotFound();
/// @notice Break is not in the Pending state.
/// @dev Thrown when an action that requires `Pending` (cancel or resume) is attempted
/// on a break with a different status.
error BrNotPending();
/// @notice Wrong chain.
/// @dev Raised when `resumeBr` is called on a chain whose `block.chainid` does not match
/// the chain recorded in the break metadata.
error BrWrongChain();
/// @notice Wrong cursor position.
/// @dev Thrown if the `cPos` argument passed into `resumeBr` does not match the cursor
/// that was persisted at break time.
error BrWrongCPos();
/// @notice Tail mismatch.
/// @dev Used when:
/// - the provided tail length differs from the stored `tailLen`, or
/// - `keccak256(abi.encode(opsTail, paramsTail))` does not match the stored `tailHash`.
error BrWrongTail();
/// @notice Break resume already started.
/// @dev Signaled when `resumeBr` is invoked again for a break where `resumeStarted`
/// is already true.
error BrAlreadyStarted();
/// @notice Caller is not authorized.
/// @dev Thrown when a function restricted to `initiator` or `emergencyTo` is called
/// by a different address.
error BrNotAuthorized();
/// @notice Break funds have already been consumed.
/// @dev Raised when `resumeBr` is called but the stored reserved balance for this break
/// is already zero, meaning the funds were processed earlier.
error BrAlreadyProcessed();
/// @notice Non-zero msg.value where zero was expected.
/// @dev Used by `resumeBr` and other break-related entry points that must never accept
/// direct ETH.
error BrMsgValueNotAllowed();
/// @notice Router is paused.
/// @dev Thrown when break flows are invoked while the router is paused via the pausable
/// facet.
error BrPaused();
/// @notice Insufficient native balance to escrow.
/// @dev Signals that the router’s native balance is lower than the amount that must
/// be placed into break escrow for the current operation.
error BrInsufficientNative();
/// @notice Insufficient ERC20 balance to escrow.
/// @dev Signals that the router’s ERC20 balance for the given asset is lower than the
/// amount that must be placed into break escrow.
error BrInsufficientToken();
/// @notice Break escrow address is not configured.
/// @dev Used when a break operation requires the escrow contract but the stored escrow
/// address is zero.
error BrEscrowNotSet();
/// @notice Caller is not the configured break escrow contract.
/// @dev Enforces that the router-side callback for native returns can only be invoked
/// by the current BreakEscrow instance.
error BrOnlyEscrow();
/// @notice Stage context was not found for this break.
/// @dev Thrown when `executeBreakOp` is invoked but no staged asset and amount
/// were recorded in `BreakFacetStorage` by the preceding step (e.g. Rubic/Bungee/Runner),
/// typically meaning `BREAK` was not placed directly after a staging operation.
error BrMissingStageContext();
/// @notice Staged amount does not match previous step output.
/// @dev Used when the amount recorded in `BreakFacetStorage` differs from
/// `prevMaskedParams.amountOut` inside `executeBreakOp`, indicating a corrupted
/// or misconfigured pipeline for the current break.
error BrInconsistentAmount();
/// @notice Emitted when a new break (`br`) is created and funds are escrowed.
/// @dev Records the full metadata snapshot for a newly created break:
/// - `requestId` uniquely identifies the break,
/// - `initiator` is the address that triggered `br`,
/// - `emergencyTo` is the account that may later cancel or resume,
/// - `chainId` is the chain on which the break must be resumed,
/// - `cPos` is the cursor position where execution halted,
/// - `tailHash` is `keccak256(abi.encode(opsTail, paramsTail))`,
/// - `tailLen` is the number of operations in the saved tail.
/// @param requestId Unique break identifier.
/// @param initiator Address that initiated the break.
/// @param emergencyTo Emergency recipient that may cancel or resume.
/// @param chainId Chain ID where this break must later be resumed.
/// @param cPos Cursor position within the pipeline where execution stopped.
/// @param tailHash Hash of the recorded tail ops and params.
/// @param tailLen Number of operations stored in the tail.
event BreakCreated(
bytes32 indexed requestId,
address indexed initiator,
address indexed emergencyTo,
uint64 chainId,
uint8 cPos,
bytes32 tailHash,
uint16 tailLen
);
/// @notice Emitted when a break is successfully resumed and its funds consumed.
/// @dev Emitted at the end of a successful `resumeBr` flow, after the reserved balances
/// for this break have been cleared and the status set to `Consumed`.
/// @param requestId Break identifier that was resumed and consumed.
event BreakConsumed(bytes32 indexed requestId);
/// @notice Emitted when a pending break is cancelled with `!br`.
/// @dev Emitted after a break is marked `Cancelled` and its reserved funds are released
/// to the configured emergency recipient.
/// @param requestId Break identifier that was cancelled.
/// @param to Recipient of released funds (usually `emergencyTo`).
event BreakCancelled(bytes32 indexed requestId, address indexed to);
/// @notice Configures the BreakEscrow contract used to hold break funds.
/// @dev Must be called by a privileged router role before any `br` / `!br` / `resumeBr`
/// flows are executed. Reverts with {BrEscrowNotSet} if `escrow` is the zero address.
/// @param escrow Address of the BreakEscrow contract that will custody break funds.
function setBreakEscrow(address escrow) external;
/// @notice Creates a new break (`br`) inside the router pipeline and interrupts execution.
/// @dev Intended to be invoked only by the router’s core execution loop via delegatecall.
/// The function:
/// - validates that `prevMaskedParams.to` points back to the router,
/// - derives the amount to be escrowed from `prevMaskedParams.amountOut`,
/// - moves native or ERC20 funds into the configured BreakEscrow,
/// - records break metadata keyed by `requestId`,
/// - returns `ExecutionResult.Interrupted` so the router stops processing further ops.
/// After a successful call the break status becomes `Pending`.
/// @param isResumeStart Reserved flag propagated by the router. Always false for `br`.
/// @param currentOp Operation code for this step. Expected to equal `BREAK_CODE`.
/// @param nextOp Next operation code in the pipeline. Expected to be zero, since `br` is terminal.
/// @param rawParams ABI-encoded break parameters:
/// - `requestId`: unique ID for this break,
/// - `asset`: token being escrowed (zero address = native),
/// - `cPos`: cursor position of the break,
/// - `tailHash`: keccak256 hash of `(opsTail, paramsTail)`,
/// - `tailLen`: number of ops in the tail.
/// @param prevMaskedParams Masked context from the previous step, including `amountOut`,
/// `to`, and `emergencyTo`. Used to:
/// - determine how much value to escrow,
/// - assert that the router currently holds the funds,
/// - persist the authorized `emergencyTo`.
/// @return chainIdTo Always 0. No cross-chain hop is initiated by `br`.
/// @return updatedParams Always empty. The break does not forward param patches.
/// @return newMaskedParams Masked params that will be carried forward unchanged.
/// @return result Always `ExecutionResult.Interrupted`. Signals the pipeline to stop.
function executeBreakOp(
bool isResumeStart,
bytes32 currentOp,
bytes32 nextOp,
bytes calldata rawParams,
ICoreFacet.MaskedParams calldata prevMaskedParams
)
external
returns (
uint64 chainIdTo,
bytes memory updatedParams,
ICoreFacet.MaskedParams memory newMaskedParams,
ICoreFacet.ExecutionResult result
);
/// @notice Cancels a pending break (`!br`) and releases the reserved funds to the emergency recipient.
/// @dev Intended to be invoked only by the router’s core execution loop via delegatecall.
/// The function:
/// - requires that the break status is `Pending`,
/// - requires the caller to be either `initiator` or `emergencyTo`,
/// - requests the BreakEscrow to transfer reserved funds to `emergencyTo`,
/// - clears the logical reserved balance in break storage,
/// - marks the break as `Cancelled`,
/// - returns `ExecutionResult.Succeeded`.
/// After a successful cancellation the break cannot be resumed anymore.
/// @param isResumeStart Reserved flag propagated by the router. Unused for `!br`.
/// @param currentOp Operation code. Expected to equal `CANCEL_BREAK_CODE`.
/// @param nextOp Next operation code. Expected to be zero, since `!br` is terminal.
/// @param rawParams ABI-encoded single field:
/// - `requestId`: identifier of the break to cancel.
/// @param prevMaskedParams Masked context from the previous step. Forwarded unchanged.
/// @return chainIdTo Always 0. No cross-chain hop is initiated by `!br`.
/// @return updatedParams Always empty. No param patching is forwarded.
/// @return newMaskedParams Same masked params as `prevMaskedParams`.
/// @return result Always `ExecutionResult.Succeeded`. Indicates success to the router.
function executeCancelBreakOp(
bool isResumeStart,
bytes32 currentOp,
bytes32 nextOp,
bytes calldata rawParams,
ICoreFacet.MaskedParams calldata prevMaskedParams
)
external
returns (
uint64 chainIdTo,
bytes memory updatedParams,
ICoreFacet.MaskedParams memory newMaskedParams,
ICoreFacet.ExecutionResult result
);
/// @notice Resumes a pending break by executing the stored tail of operations.
/// @dev Called directly by `initiator` or `emergencyTo` outside of the normal pipeline.
/// The function:
/// - requires `msg.value == 0`,
/// - checks that the router is not paused,
/// - verifies that the break exists and is `Pending`,
/// - validates caller authorization against `initiator` / `emergencyTo`,
/// - enforces that `chainId`, `cPos`, `tailLen` and `tailHash` match the stored metadata,
/// - reads the reserved balance from break storage and reverts if it is already zero,
/// - pulls the reserved funds from BreakEscrow back to the router,
/// - records resume context via `setResumeMask` in the break-core facet,
/// - ensures the first step of the tail is not another `br`,
/// - executes the tail through `runTailFrom`,
/// - after success, clears reserved balances and marks the break as `Consumed`.
/// If a step in the tail triggers a cross-chain hop, the helper facet handles
/// dispatch and local execution stops at that point.
/// @param requestId Unique break identifier to resume.
/// @param cPos Cursor position saved at break time. Must match the stored cursor.
/// @param opsTail Array of operation names (string identifiers) representing the saved
/// tail of the pipeline after the break.
/// @param paramsTail ABI-encoded params for each op in `opsTail`. Must match `opsTail`
/// one-to-one.
/// @param bridgeOptions Encoded bridge configuration stack used if any resumed step
/// performs a cross-chain hop.
function resumeBr(
bytes32 requestId,
uint8 cPos,
string[] calldata opsTail,
bytes[] calldata paramsTail,
bytes calldata bridgeOptions
)
external
payable;
/// @notice Callback used by the BreakEscrow contract to return native funds to the router.
/// @dev Called by BreakEscrow when native funds are withdrawn back to the router during
/// a resume flow. The function:
/// - requires `msg.sender` to be the configured escrow address,
/// - checks that `requestId` is non-zero,
/// - accepts `msg.value` as router balance without mutating break storage.
/// Logical reserved balances remain tracked inside `BreakFacetStorage`.
/// @param requestId Break identifier whose native funds are being returned to the router.
function receiveNativeFromEscrow(bytes32 requestId) external payable;
/// @notice Returns internal metadata for a break.
/// @dev Provides a read-only view into the stored break record:
/// - `initiator`: address that called `br`,
/// - `emergencyTo`: address allowed to cancel or resume,
/// - `chainId`: chain where the break is anchored,
/// - `cPos`: cursor position where execution paused,
/// - `tailHash`: keccak256(abi.encode(opsTail, paramsTail)) captured at break time,
/// - `tailLen`: number of ops in the recorded tail,
/// - `status`: encoded status enum (None / Pending / Consumed / Cancelled),
/// - `resumeStarted`: whether `resumeBr` has already been invoked.
/// The underlying asset address is stored in the internal struct but is not part
/// of this return set.
/// @param requestId Break identifier to inspect.
/// @return initiator Address that initiated the break.
/// @return emergencyTo Authorized emergency recipient for this break.
/// @return chainId Chain ID where the break must be resumed.
/// @return cPos Saved cursor position for resume.
/// @return tailHash Hash of the recorded tail.
/// @return tailLen Tail length recorded during `br`.
/// @return status Encoded status enum value for this break.
/// @return resumeStarted True if `resumeBr` has already been started.
function getBreakMeta(
bytes32 requestId
)
external
view
returns (
address initiator,
address emergencyTo,
uint64 chainId,
uint8 cPos,
bytes32 tailHash,
uint16 tailLen,
uint8 status,
bool resumeStarted
);
/// @notice Returns the reserved ERC20 balance for a given break and token.
/// @dev Reads the logical reserved amount tracked in break storage for (`requestId`, `token`).
/// The physical custody of these funds is held by BreakEscrow until a resume or cancel
/// operation is executed.
/// @param requestId Break identifier.
/// @param token ERC20 token address.
/// @return balance Amount of `token` currently reserved for this break.
function getTokenBalance(bytes32 requestId, address token) external view returns (uint256 balance);
/// @notice Returns the reserved native balance for a given break.
/// @dev Reads the logical reserved native amount tracked in break storage for `requestId`.
/// The actual ETH is held in BreakEscrow until resume or cancel flows are executed.
/// @param requestId Break identifier.
/// @return balance Amount of native currency currently reserved for this break.
function getNativeBalance(bytes32 requestId) external view returns (uint256 balance);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
import {ICoreFacet} from "./ICoreFacet.sol";
interface IBungeeFacet {
/// @notice ERC20 approval data for a Bungee "manual route" call built off-chain.
/// @dev
/// If `approvalTokenAddress` is non-zero, the facet will:
/// - compute how much of that token it is about to send into the downstream call,
/// - make sure allowance for `allowanceTarget` is at least `minimumApprovalAmount`
/// (or that computed amount, whichever is larger),
/// - then later reset that allowance to zero.
struct ManualApprovalData {
/// @notice Address of the ERC20 that needs approval.
/// @dev Zero address means "no ERC20 approval needed / expect native-only".
address approvalTokenAddress;
/// @notice Spender to approve.
/// @dev Must be allowlisted via {setBungeeSpender}.
address allowanceTarget;
/// @notice Minimum allowance that must exist before calling the downstream target.
/// @dev The facet will approve `max(minimumApprovalAmount, totalInputAmountOfToken)`.
uint256 minimumApprovalAmount;
}
/// @notice Off-chain constructed call description for a manual Bungee route.
/// @dev
/// The idea of "manual" is: integrator / backend calls Bungee (Socket) API,
/// gets a ready-to-execute tx (target, data, value), and passes it here.
/// The facet will:
/// - forward the summed native input + `value` + `nativeValue` from params,
/// - optionally set ERC20 allowance using `approvalData`,
/// - perform the external call,
/// - zero the allowance again.
struct ManualBuildTx {
/// @notice Target contract that actually executes the Bungee route.
/// @dev Must be allowlisted via {setBungeeTarget}.
address payable txTarget;
/// @notice Calldata to send to `txTarget`.
bytes txData;
/// @notice Extra ETH to attach specifically to this call,
/// @dev in addition to any ETH aggregated from user inputs and `nativeValue`.
uint256 value;
/// @notice Optional ERC20 approval info.
/// @dev If `approvalTokenAddress` is zero, no ERC20 approval path is executed.
ManualApprovalData approvalData;
}
/// @notice Parameters for starting a manual Bungee operation.
/// @dev
/// High-level flow:
/// - We collect inputs (native and/or ERC20s) from `from`.
/// - We may approve an allowlisted spender.
/// - We call `buildTx.txTarget` with `buildTx.txData` and the computed ETH value.
/// - We reset approvals back to zero.
/// - We measure how much of `stageAsset` ended up staged on the router after that call.
///
/// `tokens` / `amounts`:
/// - `tokens[i]` is the input token encoded as bytes32; `address(0)` (cast from bytes32) means native token.
/// - `amounts[i]` is how much of that token to use.
/// - If `amounts[i] == type(uint256).max`, we substitute `prevMaskedParams.amountOut` for that entry.
///
/// `from`:
/// - bytes32-encoded address we should pull ERC20 funds from (using safeTransferFrom).
/// - If `from == 0`, we fallback to `prevMaskedParams.to`. (Meaning: use output of previous pipeline step.)
/// - If `from != 0`, then on the origin call it MUST equal `msg.sender`.
///
/// `emergencyTo`:
/// - bytes32-encoded address for emergency withdrawal fallback.
/// - If `emergencyTo == 0`, we fallback to `prevMaskedParams.emergencyTo`.
/// - If `emergencyTo != 0`, then on the origin call it MUST equal `msg.sender`.
///
/// `nativeValue`:
/// - Extra ETH we must include in the downstream call in addition to any ETH amounts coming from `tokens[i]`.
///
/// Staging (`stageAsset`, `minStageAmount`) and BREAK:
/// - `stageAsset` is the token (as bytes32 address; `0` = native ETH) we EXPECT to remain sitting on the router
/// after Bungee completes.
/// - We snapshot router balance of that asset before and after the Bungee call.
/// For ETH, we adjust the "before" snapshot by subtracting exactly the ETH we are about to forward
/// so that forwarded call value is not counted as staged output.
/// - The delta is "stagedDelta".
/// - If the next op is BREAK (`nextOp == BreakOps.BREAK_CODE`), we REQUIRE `stagedDelta >= minStageAmount`.
/// Then we surface that staged amount to the BREAK via `maskedParams`.
/// - If the next op is terminal (`nextOp == 0`), we do not enforce staging and return zeroed amountOut/to.
struct ManualStartParams {
/// @notice Input asset addresses encoded as bytes32 (`0` = native token).
bytes32[] tokens;
/// @notice Amounts corresponding to `tokens`.
/// @dev `type(uint256).max` means "use prevMaskedParams.amountOut" for that entry.
uint256[] amounts;
/// @notice bytes32-encoded source of funds.
/// @dev If zero, defaults to `prevMaskedParams.to`. Otherwise must match `msg.sender` on origin.
bytes32 from;
/// @notice Extra ETH to add on top of summed native inputs.
/// @dev Used for fees / integrator-required msg.value top-ups.
uint256 nativeValue;
/// @notice bytes32-encoded emergency recipient.
/// @dev If zero, defaults to `prevMaskedParams.emergencyTo`. Otherwise must match `msg.sender` on origin.
bytes32 emergencyTo;
/// @notice Asset we expect to remain staged on the router if we BREAK next.
/// @dev bytes32-encoded token address. `0` means native ETH.
bytes32 stageAsset;
/// @notice Minimal acceptable amount of `stageAsset` that must be staged on the router post-call.
/// @dev Only enforced if `nextOp == BreakOps.BREAK_CODE`.
uint256 minStageAmount;
/// @notice Off-chain prebuilt call definition for the Bungee step.
ManualBuildTx buildTx;
}
/// @notice ERC20 approval data for an "auto route" Bungee call.
/// @dev
/// "Auto" = we already know the target, calldata, and required approval on-chain
/// (for example, a pre-integrated Socket/Bungee router).
///
/// If `tokenAddress` is non-zero, the facet:
/// - computes total ERC20 amount it's about to send,
/// - grants allowance to `spenderAddress` for `max(amount, totalInputAmount)`,
/// - executes the call,
/// - resets allowance back to zero.
struct AutoApprovalData {
/// @notice ERC20 that must be approved for spending.
/// @dev Zero means "no ERC20 approval path / expect native-only".
address tokenAddress;
/// @notice Spender that will pull the tokens.
/// @dev Must be allowlisted via {setBungeeSpender}.
address spenderAddress;
/// @notice Minimum allowance that must be guaranteed before the call.
/// @dev The facet will approve `max(amount, totalInputAmount)`.
uint256 amount;
}
/// @notice On-chain call specification for an auto Bungee route.
/// @dev
/// The facet will call `to` directly with `data`, attach the required ETH,
/// handle approvals according to `approval`, and then revoke approvals.
struct AutoTxData {
/// @notice Destination contract to invoke.
/// @dev Must be allowlisted via {setBungeeTarget}.
address payable to;
/// @notice Calldata for the Bungee route call.
bytes data;
/// @notice Extra ETH to attach to the call in addition to aggregated native inputs.
uint256 value;
/// @notice Optional ERC20 approval config.
AutoApprovalData approval;
}
/// @notice Parameters for starting an auto Bungee operation.
/// @dev
/// Semantics match {ManualStartParams} but instead of `ManualBuildTx` we have fixed `autoTx`.
///
/// `tokens`, `amounts`, `from`, `emergencyTo`, `nativeValue`:
/// - behave exactly like in {ManualStartParams}.
///
/// Staging / BREAK semantics are also identical:
/// - We snapshot balance of `stageAsset` before and after the `autoTx` call (with ETH "before" adjusted
/// by subtracting the ETH we're about to forward).
/// - Compute stagedDelta.
/// - If `nextOp == BreakOps.BREAK_CODE`, require `stagedDelta >= minStageAmount`
/// and surface it to BREAK in `maskedParams`.
struct AutoStartParams {
/// @notice Input asset addresses (bytes32, `0` = native).
bytes32[] tokens;
/// @notice Input amounts matching `tokens`.
/// @dev `type(uint256).max` means "use prevMaskedParams.amountOut" for that index".
uint256[] amounts;
/// @notice bytes32-encoded address from which ERC20 funds should be sourced.
/// @dev Zero means "use prevMaskedParams.to". Otherwise must equal `msg.sender` on origin.
bytes32 from;
/// @notice Extra ETH to include on top of summed native inputs.
uint256 nativeValue;
/// @notice bytes32-encoded emergency recipient for fallback withdrawal.
/// @dev Zero means "use prevMaskedParams.emergencyTo". Otherwise must equal `msg.sender` on origin.
bytes32 emergencyTo;
/// @notice Asset whose balance increase on the router we treat as staged output if we BREAK next.
/// @dev bytes32-encoded token address, or zero for native ETH.
bytes32 stageAsset;
/// @notice Minimal acceptable staged amount of `stageAsset` after the call.
/// @dev Only enforced if `nextOp == BreakOps.BREAK_CODE`.
uint256 minStageAmount;
/// @notice On-chain call data for the auto route.
AutoTxData autoTx;
}
/// @notice One refund transfer instruction to be executed by {executeBungeeRefundOp}.
/// @dev
/// `token` encoding:
/// - `token == bytes32(0)` means native ETH.
/// - otherwise `token` is a bytes32-encoded ERC20 token address.
///
/// `unwrapWETH`:
/// - If `unwrapWETH == true`, then:
/// - `token` MUST be WETH (as configured in Core storage),
/// - the facet unwraps WETH into native ETH and sends ETH to the recipient.
/// - If `token == 0` (native ETH), `unwrapWETH` MUST be false.
struct RefundItem {
/// @notice Asset identifier (bytes32-encoded token address; `0` = native ETH).
bytes32 token;
/// @notice Amount of the asset to refund.
uint256 amount;
/// @notice Whether to unwrap WETH into native ETH before sending.
bool unwrapWETH;
}
/// @notice Parameters for a single refund payout executed by {executeBungeeRefundOp}.
/// @dev
/// This op is intended for backend-triggered refunds on the origin chain, paid from the router’s
/// existing balance (funds must already be on the router at the time of execution).
///
/// Constraints expected by the implementation:
/// - `to` MUST decode to a non-zero address.
/// - `refundId` MUST be non-zero and is used for idempotency / replay protection:
/// the facet marks `refundId` as used and reverts if the same `refundId` is submitted again.
/// - `item.amount` MUST be non-zero.
/// - `item.token` and `item.unwrapWETH` semantics are described in {RefundItem}.
struct RefundParams {
/// @notice bytes32-encoded recipient address.
bytes32 to;
/// @notice Mandatory idempotency / replay-protection key.
bytes32 refundId;
/// @notice Refund transfer instruction.
RefundItem item;
}
/// @notice Scratch space for intermediate values in `executeBungeeManualOp` / `executeBungeeAutoOp`.
/// @dev
/// This struct exists only to avoid "stack too deep" during execution of the Bungee ops.
/// Instead of keeping many separate local variables on the stack, the facet creates a single
/// `LocalVars memory v;` and stores all working state there.
///
/// High-level meaning:
/// - We resolve who provides funds (`fromAddr`), pull ERC20s if needed, and possibly approve a spender.
/// - We aggregate native (ETH) input and compute how much ETH must be forwarded into the external Bungee call.
/// - We snapshot router balances of the expected "staging" asset before and after the Bungee call to determine
/// how much actually remained on the router (`stagedDelta`).
/// - If the pipeline is about to BREAK, `stagedDelta` is enforced against `minStageAmount` and then surfaced
/// back through `maskedParams`.
struct LocalVars {
/// @notice Final resolved address whose funds are being used for this step.
/// @dev
/// Comes from `p.from` (or `prevMaskedParams.to` if `p.from == 0`) via `checkMaskedParams`.
/// On origin calls, if `p.from` was non-zero it MUST equal `msg.sender`.
address fromAddr;
/// @notice ERC20 token address actually being spent for this Bungee call, if any.
/// @dev
/// `_collectInputs` enforces that all non-native inputs are the same ERC20;
/// if there are no ERC20 inputs this will remain the zero address.
address erc20TokenIn;
/// @notice Asset we are tracking as "staged" on the router after the Bungee call.
/// @dev
/// This is `p.stageAsset` decoded to an `address`. `address(0)` means native ETH.
/// We snapshot this asset’s balance on the router before and after the external call
/// to measure how much value actually stayed here for the BREAK step.
address stageAssetAddr;
/// @notice Masked emergency withdrawal recipient that will be propagated downstream.
/// @dev
/// Obtained from `checkMaskedParams`. If a non-zero emergency recipient was provided
/// on origin, it must equal `msg.sender`; otherwise we fall back to `prevMaskedParams.emergencyTo`.
bytes32 emergencyTo;
/// @notice Total native amount requested from inputs.
/// @dev
/// Sum of all `p.amounts[i]` where the corresponding `p.tokens[i] == address(0)`.
/// Represents user-supplied ETH that should go into the Bungee call.
uint256 nativeRequired;
/// @notice Total ERC20 amount being provided across inputs for `erc20TokenIn`.
/// @dev
/// `_collectInputs` accumulates this. If `fromAddr` is not the router itself,
/// we `safeTransferFrom` this amount to the router before approval/call.
uint256 erc20TotalIn;
/// @notice The ERC20 allowance amount we grant to the downstream spender.
/// @dev
/// For manual mode: `max(erc20TotalIn, minimumApprovalAmount)`.
/// For auto mode: `max(erc20TotalIn, autoTx.approval.amount)`.
/// After the call we always reset this allowance back to zero.
uint256 approveAmount;
/// @notice Router balance of `stageAssetAddr` before performing the external Bungee call.
/// @dev
/// For ETH, this is `address(this).balance`.
/// For ERC20, this is `IERC20(stageAssetAddr).balanceOf(address(this))`.
uint256 balBefore;
/// @notice Exact ETH value that will be forwarded to the external Bungee call.
/// @dev
/// Computed as `nativeRequired + p.nativeValue + buildTx.value` (manual)
/// or `nativeRequired + p.nativeValue + autoTx.value` (auto).
/// `_checkMsgValue` enforces:
/// - On origin call: `msg.value` MUST equal this.
/// - On resume call: `msg.value` MUST be 0 and the router balance MUST already cover it.
uint256 valueToSend;
/// @notice Router balance of `stageAssetAddr` after the external Bungee call returns (and after approvals are cleared).
/// @dev
/// Measured the same way as `balBefore`.
uint256 balAfter;
/// @notice Adjusted "before" balance used to measure the actually staged output.
/// @dev
/// For ETH, we conceptually subtract `valueToSend` from `balBefore` so we do not count
/// ETH that we intentionally forwarded into the external call as if it remained staged.
/// For ERC20, this is simply `balBefore`.
uint256 effectiveBefore;
/// @notice Net amount of `stageAssetAddr` that truly ended up staged on the router.
/// @dev
/// Computed as `balAfter - effectiveBefore`.
/// If `nextOp == BreakOps.BREAK_CODE`, this must be `>= p.minStageAmount`, otherwise we revert.
/// On success in BREAK mode, this becomes `maskedParams.amountOut` and we mark
/// `maskedParams.to = address(this)` so the BREAK step knows funds are held by the router.
uint256 stagedDelta;
}
/// @notice Emitted once per refunded item when {executeBungeeRefundOp} performs a payout.
/// @dev
/// - `refundId` is the idempotency key provided in {RefundParams}. May be zero.
/// - `token` reflects the item token (native ETH is represented as `address(0)`).
/// - If `unwrapped == true`, the item source token was WETH and the user received native ETH.
/// (I.e., transfer asset is ETH, while the source token is WETH.)
/// @param refundId The refund id used for replay protection (may be zero).
/// @param to The recipient of the refund.
/// @param token The asset identifier: `address(0)` for native ETH, otherwise ERC20 token address.
/// @param amount The amount refunded.
/// @param unwrapped True if WETH was unwrapped into native ETH before sending.
event RefundExecuted(
bytes32 indexed refundId,
address indexed to,
address indexed token,
uint256 amount,
bool unwrapped
);
/// @notice Executes the manual Bungee op.
/// @dev
/// Two routing modes depending on `nextOp`:
///
/// 1. Terminal mode:
/// - `nextOp == bytes32(0)`.
/// - We:
/// * pull/aggregate inputs (ERC20 and/or native),
/// * optionally approve `approvalData.allowanceTarget`,
/// * call `buildTx.txTarget` with the composed ETH value,
/// * reset approval to zero.
/// - We DO NOT require or expose staged output.
/// - Return convention:
/// * `chainIdTo = 0`
/// * `updatedParams = ""`
/// * `maskedParams.amountOut = 0`
/// * `maskedParams.to = 0`
/// * `maskedParams.emergencyTo = propagated emergencyTo`
/// * `result = ICoreFacet.ExecutionResult.Succeeded`
///
/// 2. Pre-break mode:
/// - `nextOp == BreakOps.BREAK_CODE`.
/// - After the external call succeeds, we snapshot how much of `p.stageAsset`
/// actually ended up sitting on the router ("stagedDelta"), where for ETH we
/// adjust the "before" snapshot by subtracting the ETH we were about to forward.
/// - We REQUIRE `stagedDelta >= p.minStageAmount`, otherwise revert.
/// - Return convention:
/// * `chainIdTo = 0`
/// * `updatedParams = ""`
/// * `maskedParams.amountOut = stagedDelta`
/// * `maskedParams.to = address(this)` (router now holds staged funds)
/// * `maskedParams.emergencyTo = propagated emergencyTo`
/// * `result = ICoreFacet.ExecutionResult.Succeeded`
///
/// msg.value rules (enforced internally via `_checkMsgValue`):
/// - On origin call (`CoreFacetStorage.currentRequestId() == 0`), `msg.value` MUST equal
/// the ETH we are about to forward downstream.
/// - On resume call, `msg.value` MUST be zero, and the router's own ETH balance MUST
/// already cover the required call value.
///
/// @param isOpHalfDone Reserved flag propagated by the router, ignored by this facet.
/// @param op Operation code; must equal `BUNGEE_MANUAL_START_CODE`.
/// @param nextOp Next pipeline step. Must be either:
/// - `bytes32(0)` for terminal mode, or
/// - `BreakOps.BREAK_CODE` if we intend to pause after staging funds.
/// @param params ABI-encoded {ManualStartParams} describing inputs, approvals, call target, etc.
/// @param prevMaskedParams Masked params from the previous pipeline step. Supplies fallback
/// `from`, `emergencyTo`, and previous `amountOut` for sentinel `type(uint256).max`.
/// @return chainIdTo Always 0. This facet does not itself schedule a cross-chain hop.
/// @return updatedParams Always empty bytes (`""`). No param mutation for downstream ops.
/// @return maskedParams
/// - Terminal mode: {amountOut=0, to=0, emergencyTo=propagated}
/// - Pre-break mode: {amountOut=stagedDelta, to=router, emergencyTo=propagated}
/// @return result Always `ICoreFacet.ExecutionResult.Succeeded` if we didn't revert.
function executeBungeeManualOp(
bool isOpHalfDone,
bytes32 op,
bytes32 nextOp,
bytes calldata params,
ICoreFacet.MaskedParams calldata prevMaskedParams
)
external
payable
returns (
uint64 chainIdTo,
bytes memory updatedParams,
ICoreFacet.MaskedParams memory maskedParams,
ICoreFacet.ExecutionResult result
);
/// @notice Executes the auto Bungee op.
/// @dev
/// Same behavioral contract as {executeBungeeManualOp}, except that:
/// - Instead of `ManualBuildTx`, we use `AutoTxData` (which is expected to be known/curated on-chain).
/// - All approvals / target contracts must still pass allowlist checks.
///
/// Modes:
///
/// 1. Terminal mode (`nextOp == 0`):
/// - Pull inputs (ERC20/native), approve if needed, call `autoTx.to` with `autoTx.data`
/// and the computed ETH value, then revoke approvals.
/// - Return masked params with `amountOut=0`, `to=0`, `emergencyTo` propagated.
///
/// 2. Pre-break mode (`nextOp == BreakOps.BREAK_CODE`):
/// - Same call flow, but we also:
/// * snapshot router balance of `p.stageAsset` before/after (ETH "before" is adjusted
/// by subtracting the ETH we're about to forward),
/// * compute `stagedDelta`,
/// * require `stagedDelta >= p.minStageAmount`,
/// * return `amountOut=stagedDelta`, `to=router`, `emergencyTo` propagated.
///
/// msg.value rules:
/// - On origin call, `msg.value` MUST equal the ETH to be forwarded.
/// - On resume call, `msg.value` MUST be 0 and the router must already hold enough ETH.
///
/// @param isOpHalfDone Reserved pipeline flag, ignored here.
/// @param op Operation code; must equal `BUNGEE_AUTO_START_CODE`.
/// @param nextOp Must be either zero (terminal) or `BreakOps.BREAK_CODE` (break/stage).
/// @param params ABI-encoded {AutoStartParams}.
/// @param prevMaskedParams Previous step's masked params for fallback `from`, `emergencyTo`,
/// and substitution for `type(uint256).max` amounts.
/// @return chainIdTo Always 0. Auto route itself doesn't hop chains here.
/// @return updatedParams Always empty (`""`). No param forwarding mutation.
/// @return maskedParams
/// - Terminal mode: {amountOut=0, to=0, emergencyTo=propagated}
/// - Pre-break mode: {amountOut=stagedDelta, to=router, emergencyTo=propagated}
/// @return result Always `ICoreFacet.ExecutionResult.Succeeded` if successful.
function executeBungeeAutoOp(
bool isOpHalfDone,
bytes32 op,
bytes32 nextOp,
bytes calldata params,
ICoreFacet.MaskedParams calldata prevMaskedParams
)
external
payable
returns (
uint64 chainIdTo,
bytes memory updatedParams,
ICoreFacet.MaskedParams memory maskedParams,
ICoreFacet.ExecutionResult result
);
/// @notice Executes a backend-triggered refund from the router’s balance on the origin network.
/// @dev
/// Purpose:
/// - Some Bungee/Socket routes may produce delayed refunds (funds arrive to the router later).
/// - This op allows an operator to transfer already-received funds out to the user.
///
/// Execution constraints (enforced by implementation):
/// - `op` MUST equal `BungeeOps.BUNGEE_REFUND_CODE`.
/// - `nextOp` MUST be `bytes32(0)` (refund is always terminal).
/// - Must run on the origin network only: `CoreFacetStorage.isOriginNetwork() == true`.
/// - Caller must be the diamond (`onlyDiamond` in the facet implementation); access control
/// may additionally be enforced at the router/diamond level.
///
/// Idempotency / replay protection:
/// - `RefundParams.refundId` MUST be non-zero.
/// - The facet records `refundId` as consumed and reverts if it was already used.
///
/// Refund item semantics:
/// - If `item.token == 0`: sends native ETH; `item.unwrapWETH` MUST be false.
/// - If `item.token != 0 && item.unwrapWETH == false`: transfers ERC20 token via `safeTransfer`.
/// - If `item.unwrapWETH == true`: `item.token` MUST equal WETH configured in Core storage;
/// the facet unwraps WETH into ETH and sends native ETH.
///
/// Events:
/// - Emits {RefundExecuted} once for this payout.
///
/// @param isOpHalfDone Reserved pipeline flag propagated by the router; ignored by this op.
/// @param op Operation code; must equal `BUNGEE_REFUND_CODE`.
/// @param nextOp Must be `bytes32(0)` (terminal).
/// @param params ABI-encoded {RefundParams}.
/// @param prevMaskedParams Unused for refunds (present for unified op interface).
/// @return chainIdTo Always 0 (no cross-chain hop is scheduled by this op).
/// @return updatedParams Always empty bytes (`""`).
/// @return maskedParams Always zeroed (refund op does not produce staged output).
/// @return result Always {ICoreFacet.ExecutionResult.Succeeded} if the op does not revert.
function executeBungeeRefundOp(
bool isOpHalfDone,
bytes32 op,
bytes32 nextOp,
bytes calldata params,
ICoreFacet.MaskedParams calldata prevMaskedParams
)
external
returns (
uint64 chainIdTo,
bytes memory updatedParams,
ICoreFacet.MaskedParams memory maskedParams,
ICoreFacet.ExecutionResult result
);
/// @notice Adds or removes a downstream call target from the allowlist.
/// @dev
/// - `target` cannot be the zero address.
/// - Only callable by an account with `OPERATOR_ROLE` on the diamond.
/// - Calls to Bungee (Socket) routers and helpers MUST go through allowlisted targets.
///
/// @param target The external contract that this facet is allowed to call.
/// @param allowed Whether that contract address is considered allowed.
function setBungeeTarget(address target, bool allowed) external;
/// @notice Adds or removes an address from the spender allowlist.
/// @dev
/// - `spender` cannot be the zero address.
/// - Only callable by an account with `OPERATOR_ROLE` on the diamond.
/// - Before giving any ERC20 allowance, the facet checks that the spender is allowlisted.
///
/// @param spender The spender (router / proxy / aggregator) to add or remove.
/// @param allowed Whether this spender address is allowed to receive approvals.
function setBungeeSpender(address spender, bool allowed) external;
/// @notice Returns whether a given `target` is currently allowlisted as a valid Bungee call target.
/// @param target The contract address to check.
/// @return isAllowed True if `target` is allowed, false otherwise.
function isBungeeTargetAllowed(address target) external view returns (bool isAllowed);
/// @notice Returns whether a given `spender` is allowlisted for ERC20 approvals.
/// @param spender The spender address to check.
/// @return isAllowed True if `spender` is allowed, false otherwise.
function isBungeeSpenderAllowed(address spender) external view returns (bool isAllowed);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
import {ProcessedOps} from "../libraries/CoreFacetStorage.sol";
interface ICoreFacet {
/// @notice Result of an execution step.
enum ExecutionResult {
/// @dev The operation failed.
Failed,
/// @dev The operation succeeded.
Succeeded,
/// @dev The operation was interrupted and should resume on the next chain.
Interrupted
}
/// @notice State of a cross-chain operation tracked by requestId.
enum CrossChainOpState {
/// @dev Operation has not yet been processed.
Unknown,
/// @dev Operation successfully completed.
Succeeded,
/// @dev Operation was reverted (e.g. emergency cancel).
Reverted
}
/// @notice Signed invoice data for execution payment.
struct Invoice {
uint256 executionPrice;
uint256 deadline;
uint8 v;
bytes32 r;
bytes32 s;
}
/// @notice Signed invoice data for execution payment.
struct InvoiceV4 {
Invoice invoice;
uint256 feeShare;
address feeShareRecipient;
address feeToken;
}
/// @notice Holds result values between cross-chain operation steps.
struct MaskedParams {
uint256 amountOut;
bytes32 to;
bytes32 emergencyTo;
}
/// @notice Execution context passed between operation steps.
struct ExecutionContext {
MaskedParams maskedParams;
bytes updatedParams;
}
/// @notice Emitted when a cross-chain operation completes or transfers to another chain.
/// @param currentChainId The chain ID where the event occurred.
/// @param currentRequestId The current request ID being processed.
/// @param nextChainId Target chain ID if continuation is needed.
/// @param nextRequestId New request ID for the next chain.
/// @param result Result of the execution.
/// @param lastOp Index of the last successfully executed operation.
event ComplexOpProcessed(
uint64 indexed currentChainId,
bytes32 indexed currentRequestId,
uint64 indexed nextChainId,
bytes32 nextRequestId,
ExecutionResult result,
uint8 lastOp
);
/// @notice Emitted when execution fees are paid.
/// @param payer The address that submitted the request and paid the fee.
/// @param token The token in which fee was paid.
/// @param accountant The address that received the fee.
/// @param executionPrice Amount of ETH paid for the execution.
event FeePaid(address indexed payer, address token, address accountant, uint256 executionPrice);
/// @notice Sets the address book contract address.
/// @param addressBook_ The address of the address book contract.
/// @dev Can only be called by an account with DEFAULT_ADMIN_ROLE.
function setAddressBook(address addressBook_) external;
/// @notice Caches WETH from address book.
/// @dev Can only be called by an account with DEFAULT_ADMIN_ROLE.
function resetWETH() external;
/// @notice Caches Treasury from address book.
/// @dev Can only be called by an account with DEFAULT_ADMIN_ROLE.
function resetTreasury() external;
/// @notice Verifies that the caller is the trusted router from the source chain and correct selector is passed.
/// @param selector The function selector, expected to be `ICoreFacet.resume.selector`.
/// @param from The sender address from the source chain.
/// @param chainIdFrom The chain ID from which the call originated.
/// @return True if validation passed, otherwise reverts.
function receiveValidatedData(
bytes4 selector,
bytes32 from,
uint64 chainIdFrom
) external returns (bool);
/// @notice Starts the execution of a cross-chain operation on the origin network.
/// @param operations List of operation names to execute.
/// @param params Encoded parameters for each operation.
/// @param receipt Signed receipt (invoice) for fee validation.
/// @param bridgeOptions Encoded options for bridging to other chains.
function start(
string[] calldata operations,
bytes[] calldata params,
InvoiceV4 calldata receipt,
bytes memory bridgeOptions
) external payable;
/// @notice Resumes the execution of a cross-chain operation on the destination network.
/// @param requestId The request ID of the cross-chain operation.
/// @param cPos The index of the operation to resume from.
/// @param operations List of operation names.
/// @param params Parameters for each operation.
/// @param bridgeOptions Encoded options for continued bridging.
/// @dev Can only be called by the configured receiver and requires valid requestId context.
function resume(
bytes32 requestId,
uint8 cPos,
string[] calldata operations,
bytes[] memory params,
bytes memory bridgeOptions
) external;
/// @notice Current nonce counter for a given address.
/// @param account User address whose nonce is requested.
/// @return Present value of `RouterStorage.DS.nonces[account]`.
function nonces(address account) external view returns (uint256);
/// @notice Parameters hash with which a cross-chain request was started.
/// @param requestId Identifier of the cross-chain request.
/// @return keccak256 hash stored in `RouterStorage.DS.startedOps[requestId]`.
function startedOps(bytes32 requestId) external view returns (bytes32);
/// @notice Processing state of a cross-chain request.
/// @param requestId Identifier of the cross-chain request.
/// @return The structure with information about cross-chain transaction delivery (see defination of ProcessedOps).
function processedOps(bytes32 requestId) external view returns (ProcessedOps memory);
/// @notice Cached AddressBook contract address.
/// @return Address stored in `RouterStorage.DS.AddressBook`.
function addressBook() external view returns (address);
/// @notice Cached Treasury contract address.
/// @return Address stored in `RouterStorage.DS.treasury`.
function treasury() external view returns (address);
/// @notice Returns address of wrapped ETH contract.
/// @return The address of wrapped ETH contract.
function WETH() external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
import {ICoreFacet} from "./ICoreFacet.sol";
interface ICrosschainFacet {
/// @notice Parameters for ERC20 `permit` signature-based approval.
struct PermitParams {
bytes32 token;
bytes32 owner;
uint256 amount;
uint256 deadline;
uint8 v;
bytes32 r;
bytes32 s;
}
/// @notice Parameters for synthetic token operations (mint/burn/lock/unlock).
struct SynthParams {
bytes32 tokenIn;
uint256 amountIn;
bytes32 from;
bytes32 to;
uint64 chainIdTo;
uint64 tokenInChainIdFrom;
bytes32 emergencyTo;
}
/// @notice Parameters for wrapping and unwrapping tokens (e.g. ETH <-> WETH).
struct WrapParams {
bytes32 tokenIn;
uint256 amountIn;
bytes32 from;
bytes32 to;
}
/// @notice Parameters for emergency cancellation of a cross-chain request.
struct CancelParams {
bytes32 requestId;
bytes32 hashSynthParams;
uint64 chainIdTo;
SynthParams emergencyParams;
}
/// @notice Handles main operational logic for core operation types (permit, mint, lock, etc.).
/// @param isOpHalfDone True if operation is continuing from another chain.
/// @param op Current operation code.
/// @param nextOp Next operation code.
/// @param params Parameters for the operation.
/// @param prevMaskedParams Previous masked parameters to inherit state.
/// @return chainIdTo Target chain ID (if any).
/// @return updatedParams Updated params to pass for cross-chain.
/// @return maskedParams Resulting masked params.
/// @return result Execution result (Succeeded, Failed, or Interrupted).
function executeCrosschainOp(
bool isOpHalfDone,
bytes32 op,
bytes32 nextOp,
bytes memory params,
ICoreFacet.MaskedParams memory prevMaskedParams
)
external
payable
returns (
uint64 chainIdTo,
bytes memory updatedParams,
ICoreFacet.MaskedParams memory maskedParams,
ICoreFacet.ExecutionResult result
);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
import {ICoreFacet} from "./ICoreFacet.sol";
interface ICurveFacet {
/// @notice Parameters for adding liquidity to a pool.
struct AddParams {
bytes32 tokenIn;
uint256 amountIn;
bytes32 from;
bytes32 to;
bytes32 pool;
uint256 minAmountOut;
uint8 i;
bytes32 emergencyTo;
}
/// @notice Parameters for removing liquidity from a pool.
struct RemoveParams {
bytes32 tokenIn;
uint256 amountIn;
bytes32 from;
bytes32 to;
bytes32 pool;
uint256 minAmountOut;
uint8 j;
bytes32 emergencyTo;
}
/// @notice Parameters for performing a token swap.
struct SwapParams {
bytes32 tokenIn;
uint256 amountIn;
bytes32 from;
bytes32 to;
bytes32 pool;
uint256 minAmountOut;
uint8 i;
uint8 j;
bytes32 emergencyTo;
}
/// @notice Emitted when a new adapter is set for a liquidity pool.
/// @param pool The address of the liquidity pool.
/// @param adapter The address of the adapter associated with the pool.
event PoolAdapterSet(address pool, address adapter);
/// @notice Sets the adapter for a specific liquidity pool.
/// @param pool_ The address of the liquidity pool.
/// @param poolAdapter_ The address of the corresponding pool adapter.
/// @dev Can only be called by an account with OPERATOR_ROLE.
/// Reverts if the pool address is zero.
function setPoolAdapter(
address pool_,
address poolAdapter_
) external;
/// @notice Handles core (permit, mint, lock, etc.) and pool-related operations (add, remove, swap).
/// @param isOpHalfDone True if operation is resuming.
/// @param op Operation code.
/// @param nextOp Next operation code.
/// @param params Parameters for the operation.
/// @param prevMaskedParams Previously accumulated masked params.
/// @return chainIdTo Target chain ID.
/// @return updatedParams Updated parameters (for cross-chain execution).
/// @return maskedParams Updated masked state.
/// @return result Execution result.
function executeCurveAMMOp(
bool isOpHalfDone,
bytes32 op,
bytes32 nextOp,
bytes memory params,
ICoreFacet.MaskedParams memory prevMaskedParams
)
external
payable
returns (
uint64 chainIdTo,
bytes memory updatedParams,
ICoreFacet.MaskedParams memory maskedParams,
ICoreFacet.ExecutionResult result
);
/// @notice Adapter contract that the router uses for a specific liquidity pool.
/// @param pool Address of the liquidity pool.
/// @return Address stored in `RouterStorage.DS.poolAdapter[pool]`.
function poolAdapter(address pool) external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
import {ICoreFacet} from "./ICoreFacet.sol";
import {ResumedOps} from "../libraries/OFTFacetStorage.sol";
interface IOFTFacet {
/// @notice State of a lzCompose operation tracked by guid.
enum OftOpState {
/// @dev Operation has not yet been processed.
Unknown,
/// @dev Operation successfully completed.
Succeeded,
/// @dev Operation was reverted (e.g. emergency cancel).
Reverted,
/// @dev Operation was corrected.
Pushed
}
/// @notice Patterns of interaction between the OFT contract and the token.
enum OftType {
/// @dev The token itself supports the OFT standard.
Token,
/// @dev The OFT contract is the owner of the token.
Owner,
/// @dev The OFT contract is an adapter for the token.
Adapter
}
/// @notice Parameters for send OFT token operations to dist chain.
struct OftParams {
bytes32 oft;
uint256 amountIn;
bytes32 from;
bytes32 to;
uint64 chainIdTo;
uint32 dstEid;
bytes extraOptions;
uint256 nativeFeeLimit;
OftType oftTypeSource;
OftType oftTypeDist;
bytes32 emergencyTo;
}
/// @notice Parameters for execute oft operation.
/// @param cPos Current operation number.
/// @param operationsCode Array of operation codes.
/// @param operations Array of operation names to execute.
/// @param params Array of encoded parameters for each operation.
/// @param bridgeOptions Encoded bridge-related options for further continuation.
/// @param prevMaskedParams Previous masked parameters to inherit state.
struct ExecuteOftParams {
uint256 cPos;
bytes32[] operationsCode;
string[] operations;
bytes[] params;
bytes bridgeOptions;
ICoreFacet.MaskedParams prevMaskedParams;
}
/// @notice Parameters for emergency oft operations.
struct EmergencyOftParams {
bytes32 guid;
OftParams oftParams;
}
/// @notice Parameters for push OFT token operations in dist chain.
struct pushOftParams {
bytes32 guid;
}
/// @notice Emitted when the new value of endPoint is set.
/// @param endPoint The endPoint address.
event SetEndPoint(address endPoint);
/// @notice Emitted when a cross-chain operation completes or transfers to another chain.
/// @param guid The current ID being processed.
/// @param nextChainId Target chain ID if continuation is needed.
/// @param oftOpState The state of OFT operation.
event OFTProcessed(
bytes32 indexed guid,
uint64 indexed nextChainId,
OftOpState indexed oftOpState
);
/// @notice Handles main operational logic for send OFT tokens operation.
/// @param ep Parameters for execute oft operation
/// @return chainIdTo Id of the chain to which OFT tokens were sent.
/// @return updatedParams Id of operation in Layer Zero protocol.
/// @return maskedParams Updated masked parameters to inherit state.
/// @return result xecution result (Succeeded, Failed, or Interrupted).
function executeOftOp(
ExecuteOftParams memory ep
)
external
payable
returns (
uint64 chainIdTo,
bytes memory updatedParams,
ICoreFacet.MaskedParams memory maskedParams,
ICoreFacet.ExecutionResult result
);
/// @notice Processing a message sent with OFT tokens.
/// @param from OFT token address.
/// @param guid Id of operation in Layer Zero protocol.
/// @param message Message with parameters for further processing.
/// @param sender Executor address.
/// @param extraData Options for sending a message.
function lzCompose(
address from,
bytes32 guid,
bytes calldata message,
address sender,
bytes calldata extraData
) external payable;
/// @notice Sets the EndPoint contract address.
/// @param endPoint The address of the EndPoint contract.
/// @dev Can only be called by an account with DEFAULT_ADMIN_ROLE.
function setEndPoint(address endPoint) external;
/// @notice Retrun EndPoint contract address.
/// @return Address stored in `OFTFacetStorage.DS.endPoint`.
function endPoint() external view returns(address);
/// @notice struct whith hashs of the BMo operation and subsequent operations obtained in the lzCompose() function
/// @param guid The id of LZ operation.
/// @return keccak256 hash stored in `OFTFacetStorage.DS.resumedOps[guid]`.
function resumedOps(bytes32 guid) external view returns (ResumedOps memory);
/// @notice Processing state of a lzCompose request.
/// @param guid The id of LZ operation.
/// @return 0 – Unknown, 1 – Succeeded, 2 – Reverted (see `CrossChainOpState`).
function processedOftOps(bytes32 guid) external view returns (uint8);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
import {ICoreFacet} from "./ICoreFacet.sol";
interface IRubicFacet {
/// @notice Parameters to start a Rubic route from the router.
/// @dev
/// - `tokens[i]` is the input asset address encoded as bytes32. `address(0)` (cast from bytes32) means native token (ETH).
/// - `amounts[i]` is the amount for `tokens[i]`. If `amounts[i] == type(uint256).max`, the facet will substitute
/// `prevMaskedParams.amountOut` for that entry.
///
/// - `from` is a bytes32-encoded address indicating whose funds to pull:
/// * If `from == 0`, the facet will fallback to `prevMaskedParams.to`
/// (i.e. use the output holder from the previous op in the pipeline).
/// * If `from != 0`, then on the origin call it MUST match `msg.sender`.
///
/// - `emergencyTo` is the emergency withdrawal recipient:
/// * If `emergencyTo == 0`, we fallback to `prevMaskedParams.emergencyTo`.
/// * If `emergencyTo != 0`, then on the origin call it MUST match `msg.sender`.
///
/// - `nativeValue` is extra ETH that must be forwarded to Rubic in addition to any ETH that appears
/// in `tokens[i] == address(0)`. The facet enforces msg.value rules so that:
/// * on origin call, `msg.value` must exactly equal the ETH we're going to forward;
/// * on resume call, `msg.value` must be 0 and the router must already hold enough ETH.
///
/// - `stageAsset` / `minStageAmount` are only enforced if the router is about to `break`
/// (i.e. `nextOp == BreakOps.BREAK_CODE`):
/// * `stageAsset` is the asset we expect to remain "staged" (left behind) on the router after Rubic finishes.
/// It is encoded as bytes32; `address(0)` means native ETH.
/// * The facet snapshots the router's balance of `stageAsset` before and after the Rubic call.
/// For ETH, the "before" snapshot is adjusted by subtracting the ETH we are about to forward,
/// so we don't count forwarded call value as staged profit.
/// * The delta is the actually staged amount. The facet requires `delta >= minStageAmount`.
/// * If that check passes, the facet will report this staged amount through `maskedParams.amountOut`
/// and will set `maskedParams.to = address(this)` so the next step (the break) knows the funds
/// are already sitting on the router.
struct RubicStartParams {
/// @notice Input asset addresses as bytes32; `0` means native token.
bytes32[] tokens;
/// @notice Amounts for each input asset; can be `type(uint256).max` to reuse `prevMaskedParams.amountOut`.
uint256[] amounts;
/// @notice Opaque calldata blob for Rubic's proxy (`startViaRubic`).
bytes facetCallData;
/// @notice Source of funds (bytes32-encoded address). `0` means "use prevMaskedParams.to".
bytes32 from;
/// @notice Extra ETH that must be sent into Rubic on top of summed native inputs.
uint256 nativeValue;
/// @notice Emergency recipient. `0` means "use prevMaskedParams.emergencyTo".
bytes32 emergencyTo;
/// @notice Asset we require to stay staged on the router if the next op is BREAK.
/// @dev bytes32-encoded token address; `0` means native ETH.
bytes32 stageAsset;
/// @notice Minimal acceptable staged amount of `stageAsset` after the Rubic call,
/// @dev enforced only when `nextOp == BreakOps.BREAK_CODE`.
uint256 minStageAmount;
}
/// @notice Scratch container for intermediate values in `executeRubicOp`.
/// @dev
/// This struct exists purely to avoid "stack too deep" in `executeRubicOp`.
/// Instead of keeping many independent local variables (which quickly exceeds the
/// stack slot limit), we group them under a single in-memory struct `LocalVars v`.
///
/// High-level meaning of the fields:
/// - We resolve who is the actual payer of ERC20 inputs (`fromAddr`), and we may pull
/// their tokens into the router.
/// - We accumulate total native input required by the Rubic route (`nativeRequired`)
/// and compute the final ETH that must be forwarded to Rubic (`valueToSend`).
/// - We snapshot router balances of the expected "staging" asset before and after
/// calling Rubic (`balBefore`, `balAfter`), then compute how much actually remained
/// on the router (`stagedDelta`). This is enforced against `minStageAmount` when
/// `nextOp == BreakOps.BREAK_CODE`.
/// - We also keep the Rubic proxy address (`proxy`), the chosen staging asset
/// (`stageAssetAddr`), and the deduplicated ERC20 count (`idx`).
/// - We carry forward the masked emergency recipient (`emergencyTo`) to propagate it
/// into `maskedParams.emergencyTo`.
struct LocalVars {
/// @notice Final resolved address that provides ERC20 inputs for this Rubic step.
/// @dev
/// Derived from `p.from` and `prevMaskedParams.to` using `checkMaskedParams`.
/// If `p.from` was zero, this will typically be the previous op's output holder.
/// Otherwise, on origin calls, it must match `msg.sender`.
address fromAddr;
/// @notice Address of the Rubic ERC20 proxy we will call (`startViaRubic`).
/// @dev
/// Loaded from facet storage (`RubicFacetStorage.ds().erc20Proxy`). Must be non-zero
/// or we revert. Used both for approvals and for the external call.
address proxy;
/// @notice Asset we are tracking as "staged" on the router after Rubic finishes.
/// @dev
/// This is `p.stageAsset` decoded to `address`. `address(0)` means native ETH.
/// We snapshot this asset's balance on the router before/after the Rubic call
/// to measure how much value actually remained here to be consumed by BREAK.
address stageAssetAddr;
/// @notice Total native amount requested by the route inputs.
/// @dev
/// This is the sum of all `p.amounts[i]` where `p.tokens[i] == address(0)`.
/// It reflects the ETH that is part of the user-supplied swap/bridge input.
uint256 nativeRequired;
/// @notice The exact ETH value that must be forwarded to the Rubic proxy.
/// @dev
/// Computed as `nativeRequired + p.nativeValue`.
/// Enforced via `_checkMsgValue`:
/// - On origin calls, `msg.value` must equal this.
/// - On resume calls, `msg.value` must be zero and the router must already
/// hold at least this much ETH.
uint256 valueToSend;
/// @notice Router balance of `stageAssetAddr` before calling Rubic.
/// @dev
/// For ETH, this is `address(this).balance`.
/// For ERC20, this is `IERC20(stageAssetAddr).balanceOf(address(this))`.
/// Captured to later compute how much actually stayed on the router.
uint256 balBefore;
/// @notice Router balance of `stageAssetAddr` after calling Rubic.
/// @dev
/// Same measurement logic as `balBefore`, but taken after `startViaRubic`
/// returns and after we reset approvals.
uint256 balAfter;
/// @notice Adjusted "before" balance used to measure staged value.
/// @dev
/// For ETH, we conceptually subtract the ETH we intentionally forwarded
/// (`valueToSend`) so we do not count that outgoing ETH as if it had
/// "remained on the router". For ERC20, this is just `balBefore`.
uint256 effectiveBefore;
/// @notice Net amount of `stageAssetAddr` that actually ended up staged on the router.
/// @dev
/// Computed as `balAfter - effectiveBefore`.
/// If `nextOp == BreakOps.BREAK_CODE`, we require `stagedDelta >= p.minStageAmount`.
/// When that holds, we propagate `stagedDelta` via `maskedParams.amountOut`
/// and mark `maskedParams.to = address(this)` so BREAK knows funds are on the router.
uint256 stagedDelta;
/// @notice Number of distinct ERC20 tokens that we aggregated for this Rubic call.
/// @dev
/// While iterating inputs we merge same-token amounts. `idx` is how many unique
/// tokens we ended up with. We then truncate the temporary `erc20Tokens` /
/// `erc20Amounts` arrays to length `idx` before calling `startViaRubic`.
uint256 idx;
/// @notice Masked emergency withdrawal recipient to forward in `maskedParams.emergencyTo`.
/// @dev
/// Comes from `checkMaskedParams`, which enforces (on origin) that if a non-zero
/// emergency address is provided it must equal `msg.sender`, or otherwise falls
/// back to `prevMaskedParams.emergencyTo`.
bytes32 emergencyTo;
}
/// @notice Executes the Rubic step in the pipeline.
/// @dev
/// Flow types:
///
/// 1. Terminal flow (no break next):
/// - `nextOp == bytes32(0)`.
/// - Facet pulls/approves inputs, forwards assets into Rubic, calls Rubic proxy,
/// and then clears approvals.
/// - The pipeline stops here.
/// - Return convention:
/// * `chainIdTo = 0`
/// * `updatedParams = ""`
/// * `maskedParams.amountOut = 0`
/// * `maskedParams.to = 0`
/// * `maskedParams.emergencyTo = propagated emergencyTo`
/// * `result = ICoreFacet.ExecutionResult.Succeeded`
///
/// 2. Pre-break flow:
/// - `nextOp == BreakOps.BREAK_CODE`.
/// - Facet does the same Rubic interaction (pull assets, approve, forward ETH, call proxy, reset approvals),
/// but additionally:
/// * It snapshots the router's balance of `p.stageAsset` before and after the call.
/// For ETH, it subtracts the ETH we are about to forward so we don't "double count" the call value.
/// * It computes the staged delta = (adjusted afterBalance - adjusted beforeBalance).
/// * Requires `stagedDelta >= p.minStageAmount`, otherwise revert.
/// * It returns:
/// - `maskedParams.amountOut = stagedDelta`
/// - `maskedParams.to = address(this)` (router holds the staged funds)
/// - `maskedParams.emergencyTo = propagated emergencyTo`
/// - `chainIdTo = 0`
/// - `updatedParams = ""`
/// - `result = ICoreFacet.ExecutionResult.Succeeded`
///
/// Common rules for both flows:
/// - ERC20 inputs: we aggregate all requested ERC20 amounts per token, optionally pull them
/// from `from` (or `prevMaskedParams.to` if `from == 0`), approve the Rubic proxy for those amounts,
/// and later reset approvals to zero.
/// - Native inputs: we sum all native amounts (tokens[i] == address(0)) plus `nativeValue`.
/// - `msg.value` policy:
/// * On origin call (`CoreFacetStorage.currentRequestId() == 0`), `msg.value` MUST equal the ETH
/// we are about to forward.
/// * On resume call, `msg.value` MUST be 0, and the router's ETH balance MUST be large enough to fund the call.
///
/// @param isOpHalfDone Reserved flag propagated by the router. Not used by Rubic logic.
/// @param op Operation code; must be the Rubic start code (`RUBIC_START_CODE`).
/// @param nextOp Next operation code. Must be either:
/// - `bytes32(0)` for terminal flow, or
/// - `BreakOps.BREAK_CODE` if we're staging funds for a BREAK next.
/// @param params ABI-encoded `RubicStartParams` specifying inputs, routing data, staging expectations, etc.
/// @param prevMaskedParams Masked params from the previous pipeline step. Supplies default `from`,
/// `emergencyTo`, and `amountOut` substitutions.
/// @return chainIdTo Always 0. RubicFacet itself does not directly schedule a cross-chain hop.
/// @return updatedParams Always empty (`""`). RubicFacet does not mutate downstream op params.
/// @return maskedParams
/// Terminal flow (`nextOp == 0`):
/// - amountOut = 0
/// - to = 0
/// - emergencyTo = propagated
/// Pre-break flow (`nextOp == BreakOps.BREAK_CODE`):
/// - amountOut = stagedDelta (measured staged amount)
/// - to = address(this) (router now holds the staged funds)
/// - emergencyTo = propagated
/// @return result Always `ICoreFacet.ExecutionResult.Succeeded` if we didn't revert.
function executeRubicOp(
bool isOpHalfDone,
bytes32 op,
bytes32 nextOp,
bytes calldata params,
ICoreFacet.MaskedParams calldata prevMaskedParams
)
external
payable
returns (
uint64 chainIdTo,
bytes memory updatedParams,
ICoreFacet.MaskedParams memory maskedParams,
ICoreFacet.ExecutionResult result
);
/// @notice Sets the Rubic ERC20 proxy address for the current chain.
/// @dev
/// - Restricted to `OPERATOR_ROLE` enforced in the facet implementation via `onlyRole`.
/// - Must not be the zero address.
/// @param erc20Proxy Address of Rubic's ERC20 proxy contract on this chain.
function setRubicProxy(address erc20Proxy) external;
/// @notice Returns the Rubic ERC20 proxy address configured for this chain.
/// @return The address of the Rubic ERC20 proxy contract currently stored in facet storage.
function rubicProxy() external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
import {ICoreFacet} from "./ICoreFacet.sol";
interface IRunnerFacet {
/// @notice Immutable configuration parameters embedded into a UniversalRunner clone.
/// @dev These values are encoded as immutable args in the clone bytecode (no storage used).
/// They define who can pull funds, on which chain, until when, and under which logical request.
/// @param router Router (diamond) address that is allowed to call collect() on the runner.
/// @param factory UniversalRunnerFactory that deployed this runner via CREATE2.
/// @param beneficiary Recipient of emergencyWithdraw() after deadline expiry.
/// @param token Asset expected on the runner:
/// - non-zero: ERC20 token to be collected,
/// - zero: native asset (ETH) to be collected.
/// @param deadline Last valid timestamp (inclusive) when collect() is allowed to succeed.
/// @param chainId Chain id on which this runner is valid; must equal block.chainid.
/// @param requestId Logical request identifier associated with this runner instance.
struct RunnerImmutableArgs {
address router;
address factory;
address beneficiary;
address token;
uint64 deadline;
uint64 chainId;
bytes32 requestId;
}
/// @notice Parameters for the RUNNER_COLLECT pipeline operation.
/// @dev Encoded into `params` when calling executeRunnerCollectOp via the router.
/// @param runnerArgs Immutable configuration that will be embedded into the UniversalRunner clone.
/// @param salt CREATE2 salt used to deterministically derive the runner address.
/// @param amountIn Desired amount to pull from the runner (or type(uint256).max when using masking).
/// @param emergencyTo Emergency recipient for the overall pipeline; propagated into masked params.
struct RunnerCollectParams {
RunnerImmutableArgs runnerArgs;
bytes32 salt;
uint256 amountIn;
bytes32 emergencyTo;
}
/// @notice Scratch space for executeRunnerCollectOp to avoid "stack too deep" errors.
/// @dev
/// Groups together intermediate values used during RUNNER_COLLECT execution so they
/// do not each occupy a separate stack slot. This struct is not stored on-chain and
/// is only used inside RunnerFacet.executeRunnerCollectOp.
/// @param amountIn Resolved amount to collect from the runner after applying masking rules.
/// @param emergencyTo Resolved emergency recipient encoded as bytes32 (address-packed).
/// @param factory UniversalRunnerFactory address loaded from RunnerFacetStorage.
/// @param runner Deployed UniversalRunner clone address used for the collect() call.
/// @param collected Actual amount returned by runner.collect(), compared against amountIn.
/// @param emergencyToAddr Emergency recipient decoded as an address, used to validate beneficiary.
struct LocalVars {
uint256 amountIn;
bytes32 emergencyTo;
address factory;
address runner;
uint256 collected;
address emergencyToAddr;
}
/// @notice Thrown when the caller lacks the required role to perform an admin-only action.
/// @dev Used by {setUniversalRunnerFactory}.
error RunnerFacetMissingRole();
/// @notice Thrown when a zero factory address is provided to an admin setter.
/// @dev Protects configuration from being set to address(0).
error RunnerFacetFactoryZero();
/// @notice Thrown when the universal runner factory is not configured.
/// @dev Raised in executeRunnerCollectOp when no factory address is set in storage.
error RunnerFacetFactoryNotSet();
/// @notice Thrown when the factory in params does not match the configured factory.
/// @dev Ensures that the runner is deployed only through the trusted UniversalRunnerFactory.
error RunnerFacetFactoryMismatch();
/// @notice Thrown when executeRunnerCollectOp is called with an unsupported op code.
/// @dev `op` must equal RUNNER_COLLECT_CODE for this facet.
error RunnerFacetWrongOp();
/// @notice Thrown when the router in RunnerImmutableArgs does not match the current diamond.
/// @dev Protects against misconfigured or spoofed runner args.
error RunnerFacetWrongRouter();
/// @notice Thrown when the runner's configured chainId does not match the current block.chainid.
/// @dev Prevents re-use of runner configs across chains.
error RunnerFacetChainIdMismatch();
/// @notice Thrown when the runner deadline has already passed at the time of execution.
/// @dev Used to prevent collect() from being used after expiration.
error RunnerFacetDeadlineExpired();
/// @notice Thrown when the resolved amount to collect is zero.
/// @dev Applies both when amountIn is zero directly or after masking resolution.
error RunnerFacetAmountZero();
/// @notice Thrown when the runner returns less funds than requested.
/// @dev Indicates that the actual collected amount is strictly less than `amountIn`.
error RunnerFacetInsufficientCollected();
/// @notice Thrown when executeRunnerCollectOp is called in a "half-done" (resumed) mode.
/// @dev RUNNER_COLLECT is a one-step operation and does not support isOpHalfDone == true.
error RunnerFacetHalfDoneNotSupported();
/// @notice Thrown when the resolved emergency recipient address is zero.
/// @dev Ensures that RUNNER_COLLECT always has a non-zero emergencyTo in masked params.
error RunnerFacetEmergencyToZero();
/// @notice Thrown when the runner's configured beneficiary does not match the resolved emergencyTo.
/// @dev Binds UniversalRunner.beneficiary to the pipeline initiator by enforcing beneficiary == emergencyTo.
error RunnerFacetWrongBeneficiary();
/// @notice Sets the UniversalRunnerFactory used to deploy runner clones.
/// @dev Admin-gated. Reverts if `factory` is the zero address.
/// @param factory The UniversalRunnerFactory contract address.
function setUniversalRunnerFactory(address factory) external;
/// @notice Executes the RUNNER_COLLECT operation as part of the router pipeline.
/// @dev
/// High-level behavior:
/// - Validates that `op` equals RUNNER_COLLECT_CODE and that `isOpHalfDone` is false.
/// - Requires `msg.value == 0` (no native value is accepted at this stage).
/// - Decodes RunnerCollectParams from `params`.
/// - Ensures:
/// * factory is configured and matches `runnerArgs.factory`,
/// * `runnerArgs.router == address(this)` (the current diamond),
/// * `runnerArgs.chainId == block.chainid`,
/// * `runnerArgs.deadline >= block.timestamp`.
/// - Resolves `amountIn` and `emergencyTo` using `prevMaskedParams` and masking rules.
/// - Deploys a UniversalRunner clone via factory if it does not exist yet.
/// - Calls `collect(runnerArgs.token, address(this), amountIn)` on the runner.
/// - Requires that the collected amount is at least `amountIn`.
/// - Returns updated masked params with:
/// * `amountOut = collected`,
/// * `to = address(this)`,
/// * `emergencyTo` propagated from the resolved value.
///
/// This operation does not schedule a cross-chain hop and does not mutate params for next ops.
///
/// @param isOpHalfDone Indicates whether the router is resuming a half-executed op; must be false.
/// @param op Operation code for this step; must equal RUNNER_COLLECT_CODE.
/// @param nextOp Operation code for the next step in the pipeline; ignored by this facet.
/// @param params ABI-encoded RunnerCollectParams structure.
/// @param prevMaskedParams Masked params from the previous step in the pipeline.
/// @return chainIdTo Always 0, since RUNNER_COLLECT does not initiate a cross-chain operation.
/// @return updatedParams Always empty bytes, as this op does not mutate subsequent params.
/// @return maskedParams Updated masked params with the collected amount and router as `to`.
/// @return result Execution status; always ExecutionResult.Succeeded on success, otherwise reverts.
function executeRunnerCollectOp(
bool isOpHalfDone,
bytes32 op,
bytes32 nextOp,
bytes calldata params,
ICoreFacet.MaskedParams calldata prevMaskedParams
)
external
payable
returns (
uint64 chainIdTo,
bytes memory updatedParams,
ICoreFacet.MaskedParams memory maskedParams,
ICoreFacet.ExecutionResult result
);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
import "@openzeppelin/contracts/utils/Counters.sol";
/// @notice Structure with information about cross-chain transaction delivery.
/// @param hashSynthParams hash from the parameters of a successfully delivered cross-chain transaction.
/// @param crossChainOpState cross-chain transaction delivery status.
struct ProcessedOps {
bytes32 hashSynthParams;
uint8 crossChainOpState;
}
library CoreFacetStorage {
/// @notice Fixed storage slot for the CoreFacet data structure.
bytes32 private constant POSITION = keccak256("crosscurve.core.facet.storage");
/// @notice Slot for the currentRequestId.
/// @dev keccak256("crosscurve.core.facet.transient.currentRequestId");
bytes32 private constant CURRENT_REQUEST_ID_POS = 0x4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f4;
/// @notice Slot for the currentChainIdFrom.
/// @dev keccak256("crosscurve.core.facet.transient.currentChainIdFrom");
bytes32 private constant CURRENT_CHAIN_ID_FROM_POS = 0x67b6f0fb6bca8398a49a7966c252819542f4d36560c9c6961937957fdf20f9f4;
/// @notice Slot for the isOriginNetwork.
/// @dev keccak256("crosscurve.core.facet.transient.isOriginNetwork");
bytes32 private constant IS_ORIGIN_NETWORK_POS = 0xb13bd13498ce9409b85a4287d16ff0fcdaca732822a47a97d2bdada3325aa451;
/// @notice Slot for the isDiamondCall.
/// @dev keccak256("crosscurve.core.facet.transient.isDiamondCall");
bytes32 private constant IS_DIAMOND_CALL = 0x2e445143a211ecbb689de5812742d02a96369c482aec76832fc56490cf3cc6f2;
/// @notice Slot for the isReentrancyLocked.
/// @dev keccak256("crosscurve.core.facet.transient.isReentrancyLocked");
bytes32 private constant IS_REENTRANCY_LOCKED = 0x1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d562498;
/// @notice Slot for msgValueLeft budget (origin start only).
/// @dev keccak256("crosscurve.core.facet.transient.msgValueLeft");
bytes32 private constant MSG_VALUE_LEFT_POS = 0x7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc;
/// @notice Thrown when an op tries to spend more ETH than was provided in msg.value for the whole start().
error MsgValueBudgetExceeded(uint256 requested, uint256 left);
/// @notice Layout for all persistent CoreFacet state.
/// @dev Every variable needed by CoreFacet logic is grouped here and kept
/// at the single slot defined in `POSITION`.
struct DS {
mapping(address => Counters.Counter) nonces;
mapping(bytes32 => bytes32) startedOps;
mapping(bytes32 => ProcessedOps) processedOps;
address addressBook;
address treasury;
address WETH;
}
/// @notice Accessor to the CoreFacet storage struct.
/// @dev Assembly ties the returned reference to the predefined slot so the
/// compiler understands where the struct actually lives in storage.
/// @return s Pointer to the `DS` struct residing at `POSITION`.
function ds() internal pure returns (DS storage s) {
bytes32 pos = POSITION;
assembly {
s.slot := pos
}
}
function setCurrentRequestId(bytes32 currentRequestId_) internal {
assembly ("memory-safe") {
tstore(CURRENT_REQUEST_ID_POS, currentRequestId_)
}
}
function currentRequestId() internal view returns (bytes32 currentRequestId_) {
assembly ("memory-safe") {
currentRequestId_ := tload(CURRENT_REQUEST_ID_POS)
}
}
function setCurrentChainIdFrom(uint64 currentChainIdFrom_) internal {
assembly ("memory-safe") {
tstore(CURRENT_CHAIN_ID_FROM_POS, currentChainIdFrom_)
}
}
function currentChainIdFrom() internal view returns (uint64 currentChainIdFrom_) {
assembly ("memory-safe") {
currentChainIdFrom_ := tload(CURRENT_CHAIN_ID_FROM_POS)
}
}
function setOriginNetwork(bool isOriginNetwork_) internal {
assembly ("memory-safe") {
tstore(IS_ORIGIN_NETWORK_POS, isOriginNetwork_)
}
}
function isOriginNetwork() internal view returns (bool isOriginNetwork_) {
assembly ("memory-safe") {
isOriginNetwork_ := tload(IS_ORIGIN_NETWORK_POS)
}
}
function setDiamondCall(bool isDiamondCall_) internal {
assembly ("memory-safe") {
tstore(IS_DIAMOND_CALL, isDiamondCall_)
}
}
function isDiamondCall() internal view returns (bool isDiamondCall_) {
assembly ("memory-safe") {
isDiamondCall_ := tload(IS_DIAMOND_CALL)
}
}
function setReentrancyLock(bool isLocked_) internal {
assembly ("memory-safe") {
tstore(IS_REENTRANCY_LOCKED, isLocked_)
}
}
function isReentrancyLocked() internal view returns (bool isLocked_) {
assembly ("memory-safe") {
isLocked_ := tload(IS_REENTRANCY_LOCKED)
}
}
function setMsgValueLeft(uint256 msgValueLeft_) internal {
assembly ("memory-safe") {
tstore(MSG_VALUE_LEFT_POS, msgValueLeft_)
}
}
function msgValueLeft() internal view returns (uint256 msgValueLeft_) {
assembly ("memory-safe") {
msgValueLeft_ := tload(MSG_VALUE_LEFT_POS)
}
}
function consumeMsgValue(uint256 amount) internal {
uint256 left;
assembly ("memory-safe") {
left := tload(MSG_VALUE_LEFT_POS)
}
if (amount > left) {
revert MsgValueBudgetExceeded(amount, left);
}
unchecked {
left -= amount;
}
assembly ("memory-safe") {
tstore(MSG_VALUE_LEFT_POS, left)
}
}
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
/// @param bmoOpHash Hash from the parameters of the BMo operation, which was reverted
/// @param opsHash Hash from the sequence of operation parameters that was reverted
struct ResumedOps{
bytes32 bmoOpHash;
bytes32 opsHash;
}
library OFTFacetStorage {
/// @notice Fixed storage slot for the CoreFacet data structure.
bytes32 private constant POSITION = keccak256("crosscurve.oft.facet.storage");
/// @notice Slot for the lzComposeIsProcessed.
/// @dev keccak256("crosscurve.oft.facet.transient.lzComposeIsProcessed");
bytes32 private constant LZ_COMPOSE_IS_PROCESSED = 0x30273a0c1aadada7e550a5e2c5e4d1944f67a058b948828ccf2622923a9aae4b;
/// @notice Layout for all persistent CoreFacet state.
/// @dev Every variable needed by CoreFacet logic is grouped here and kept
/// at the single slot defined in `POSITION`.
struct DS {
mapping(bytes32 => ResumedOps) resumedOps;
mapping(bytes32 => uint8) processedOps;
mapping (bytes32 => bool) guids;
address endPoint;
}
/// @notice Accessor to the CoreFacet storage struct.
/// @dev Assembly ties the returned reference to the predefined slot so the
/// compiler understands where the struct actually lives in storage.
/// @return s Pointer to the `DS` struct residing at `POSITION`.
function ds() internal pure returns (DS storage s) {
bytes32 pos = POSITION;
assembly {
s.slot := pos
}
}
function setLzComposeIsProcessed(bool lzComposeIsProcessed_) internal {
assembly ("memory-safe") {
tstore(LZ_COMPOSE_IS_PROCESSED, lzComposeIsProcessed_)
}
}
function lzComposeIsProcessed() internal view returns (bool lzComposeIsProcessed_) {
assembly ("memory-safe") {
lzComposeIsProcessed_ := tload(LZ_COMPOSE_IS_PROCESSED)
}
}
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
library BreakOps {
/// @notice Operation code for starting a break ("br").
/// @dev The router must map this code to `executeBreakOp`, which:
/// - records break metadata and pipeline tail,
/// - stashes the produced funds,
/// - returns `ExecutionResult.Interrupted`.
bytes32 public constant BREAK_CODE = keccak256(abi.encodePacked("br"));
/// @notice Operation code for cancelling a break ("!br").
/// @dev The router must map this code to `executeCancelBreakOp`, which:
/// - requires the break to still be pending,
/// - transfers escrowed funds to `emergencyTo`,
/// - marks the break as cancelled.
bytes32 public constant CANCEL_BREAK_CODE = keccak256(abi.encodePacked("!br"));
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
library BungeeOps {
/// @notice Operation code for the Bungee "manual" start (off-chain built tx).
bytes32 public constant BUNGEE_MANUAL_START_CODE = keccak256(abi.encodePacked("BMAN"));
/// @notice Operation code for the Bungee "auto" start (on-chain tx data).
bytes32 public constant BUNGEE_AUTO_START_CODE = keccak256(abi.encodePacked("BAUTO"));
/// @notice Operation code for backend-triggered refunds from router balance.
bytes32 public constant BUNGEE_REFUND_CODE = keccak256(abi.encodePacked("BRFD"));
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
library CrosschainOps {
/// @notice Operation code for lock and mint operations.
bytes32 public constant LOCK_MINT_CODE = keccak256(abi.encodePacked("LM"));
/// @notice Operation code for burn and unlock operations.
bytes32 public constant BURN_UNLOCK_CODE = keccak256(abi.encodePacked("BU"));
/// @notice Operation code for burn and mint operations.
bytes32 public constant BURN_MINT_CODE = keccak256(abi.encodePacked("BM"));
/// @notice Operation code for emergency unlock (cancel lock) operations.
bytes32 public constant EMERGENCY_UNLOCK_CODE = keccak256(abi.encodePacked("!M"));
/// @notice Operation code for emergency mint (cancel burn) operations.
bytes32 public constant EMERGENCY_MINT_CODE = keccak256(abi.encodePacked("!U"));
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
library CurveAMMOps {
/// @notice Operation code for adding liquidity or assets.
bytes32 public constant ADD_CODE = keccak256(abi.encodePacked("A"));
/// @notice Operation code for removing liquidity or assets.
bytes32 public constant REMOVE_CODE = keccak256(abi.encodePacked("R"));
/// @notice Operation code for token or asset swap.
bytes32 public constant SWAP_CODE = keccak256(abi.encodePacked("S"));
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
library OftOps {
/// @notice Operation code for OFT burn and mint operations.
bytes32 public constant BURN_MINT_OFT_CODE = keccak256(abi.encodePacked("BMo"));
/// @notice Operation code for emergency OFT mint operations.
bytes32 public constant EMERGENCY_MINT_OFT_CODE = keccak256(abi.encodePacked("!Mo"));
/// @notice Operation code for push OFT operations in dist chain.
bytes32 public constant PUSH_MINT_OFT_CODE = keccak256(abi.encodePacked("PMo"));
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
library RubicOps {
/// @notice Operation code for Rubic start (terminal step).
bytes32 public constant RUBIC_START_CODE = keccak256(abi.encodePacked("RS"));
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
library RunnerOps {
/// @notice Operation code for the "collect from UniversalRunner" operation.
/// @dev Routed to {IRunnerFacet.executeRunnerCollectOp} when present
/// as the first operation in the pipeline.
bytes32 public constant RUNNER_COLLECT_CODE = keccak256("RUNNER_COLLECT");
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
library UtilsOps {
/// @notice Operation code for `permit` functionality.
bytes32 public constant PERMIT_CODE = keccak256(abi.encodePacked("P"));
/// @notice Operation code for token wrapping.
bytes32 public constant WRAP_CODE = keccak256(abi.encodePacked("W"));
/// @notice Operation code for token unwrapping.
bytes32 public constant UNWRAP_CODE = keccak256(abi.encodePacked("Uw"));
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
import {ICoreFacet} from "./interfaces/ICoreFacet.sol";
import {ICrosschainFacet} from "./interfaces/ICrosschainFacet.sol";
import {ICurveFacet} from "./interfaces/ICurveFacet.sol";
import {IOFTFacet} from "./interfaces/IOFTFacet.sol";
import {IRubicFacet} from "./interfaces/IRubicFacet.sol";
import {IBungeeFacet} from "./interfaces/IBungeeFacet.sol";
import {IBreakFacet} from "./interfaces/IBreakFacet.sol";
import {IRunnerFacet} from "./interfaces/IRunnerFacet.sol";
import {IAddressBook} from "../interfaces/IAddressBook.sol";
import {CoreFacetStorage} from "./libraries/CoreFacetStorage.sol";
import {CrosschainOps} from "./libraries/ops/CrosschainOps.sol";
import {CurveAMMOps} from "./libraries/ops/CurveAMMOps.sol";
import {OftOps} from "./libraries/ops/OftOps.sol";
import {RubicOps} from "./libraries/ops/RubicOps.sol";
import {BungeeOps} from "./libraries/ops/BungeeOps.sol";
import {BreakOps} from "./libraries/ops/BreakOps.sol";
import {RunnerOps} from "./libraries/ops/RunnerOps.sol";
import {UtilsOps} from "./libraries/ops/UtilsOps.sol";
import {TypecastLib} from "../utils/TypecastLib.sol";
abstract contract OpsCrosschain {
/// @notice Operation code for lock and mint operations.
bytes32 public constant LOCK_MINT_CODE = CrosschainOps.LOCK_MINT_CODE;
/// @notice Operation code for burn and unlock operations.
bytes32 public constant BURN_UNLOCK_CODE = CrosschainOps.BURN_UNLOCK_CODE;
/// @notice Operation code for burn and mint operations.
bytes32 public constant BURN_MINT_CODE = CrosschainOps.BURN_MINT_CODE;
/// @notice Operation code for emergency unlock (cancel lock) operations.
bytes32 public constant EMERGENCY_UNLOCK_CODE = CrosschainOps.EMERGENCY_UNLOCK_CODE;
/// @notice Operation code for emergency mint (cancel burn) operations.
bytes32 public constant EMERGENCY_MINT_CODE = CrosschainOps.EMERGENCY_MINT_CODE;
/// @notice Operation code for `permit` functionality.
bytes32 public constant PERMIT_CODE = UtilsOps.PERMIT_CODE;
/// @notice Operation code for token wrapping.
bytes32 public constant WRAP_CODE = UtilsOps.WRAP_CODE;
/// @notice Operation code for token unwrapping.
bytes32 public constant UNWRAP_CODE = UtilsOps.UNWRAP_CODE;
}
abstract contract OpsCurveAMM {
/// @notice Operation code for adding liquidity or assets.
bytes32 public constant ADD_CODE = CurveAMMOps.ADD_CODE;
/// @notice Operation code for removing liquidity or assets.
bytes32 public constant REMOVE_CODE = CurveAMMOps.REMOVE_CODE;
/// @notice Operation code for token or asset swap.
bytes32 public constant SWAP_CODE = CurveAMMOps.SWAP_CODE;
}
abstract contract OpsOFT {
/// @notice Operation code for OFT burn and mint operations.
bytes32 public constant BURN_MINT_OFT_CODE = OftOps.BURN_MINT_OFT_CODE;
/// @notice Operation code for emergency OFT mint operations.
bytes32 public constant EMERGENCY_MINT_OFT_CODE = OftOps.EMERGENCY_MINT_OFT_CODE;
/// @notice Operation code for push OFT operations in dist chain.
bytes32 public constant PUSH_MINT_OFT_CODE = OftOps.PUSH_MINT_OFT_CODE;
}
abstract contract OpsRubic {
bytes32 public constant RUBIC_START_CODE = RubicOps.RUBIC_START_CODE;
}
abstract contract OpsBungee {
bytes32 public constant BUNGEE_MANUAL_START_CODE = BungeeOps.BUNGEE_MANUAL_START_CODE;
bytes32 public constant BUNGEE_AUTO_START_CODE = BungeeOps.BUNGEE_AUTO_START_CODE;
bytes32 public constant BUNGEE_REFUND_CODE = BungeeOps.BUNGEE_REFUND_CODE;
}
abstract contract Ops {
using CoreFacetStorage for CoreFacetStorage.DS;
/// @notice The modifier checks that the current operation is being on CoreFacet.
modifier onlyDiamond() {
require(CoreFacetStorage.isDiamondCall(), "Router: isn't Diamond call");
require(!CoreFacetStorage.isReentrancyLocked(), "Router: reentrancy");
CoreFacetStorage.setReentrancyLock(true);
_;
CoreFacetStorage.setReentrancyLock(false);
}
function getOpSelector(bytes32 op) internal pure returns (bytes4 selector) {
if (
op == UtilsOps.PERMIT_CODE
|| op == UtilsOps.WRAP_CODE
|| op == UtilsOps.UNWRAP_CODE
) {
return ICrosschainFacet.executeCrosschainOp.selector;
} else if (
op == CrosschainOps.LOCK_MINT_CODE
|| op == CrosschainOps.BURN_UNLOCK_CODE
|| op == CrosschainOps.BURN_MINT_CODE
|| op == CrosschainOps.EMERGENCY_UNLOCK_CODE
|| op == CrosschainOps.EMERGENCY_MINT_CODE
) {
return ICrosschainFacet.executeCrosschainOp.selector;
} else if (
op == CurveAMMOps.ADD_CODE
|| op == CurveAMMOps.REMOVE_CODE
|| op == CurveAMMOps.SWAP_CODE
) {
return ICurveFacet.executeCurveAMMOp.selector;
} else if (
op == OftOps.BURN_MINT_OFT_CODE
|| op == OftOps.EMERGENCY_MINT_OFT_CODE
|| op == OftOps.PUSH_MINT_OFT_CODE
) {
return IOFTFacet.executeOftOp.selector;
} else if (op == RubicOps.RUBIC_START_CODE) {
return IRubicFacet.executeRubicOp.selector;
} else if (op == BungeeOps.BUNGEE_MANUAL_START_CODE) {
return IBungeeFacet.executeBungeeManualOp.selector;
} else if (op == BungeeOps.BUNGEE_AUTO_START_CODE) {
return IBungeeFacet.executeBungeeAutoOp.selector;
} else if (op == BungeeOps.BUNGEE_REFUND_CODE) {
return IBungeeFacet.executeBungeeRefundOp.selector;
} else if (op == BreakOps.BREAK_CODE) {
return IBreakFacet.executeBreakOp.selector;
} else if (op == BreakOps.CANCEL_BREAK_CODE) {
return IBreakFacet.executeCancelBreakOp.selector;
} else if (op == RunnerOps.RUNNER_COLLECT_CODE) {
return IRunnerFacet.executeRunnerCollectOp.selector;
}
revert("Ops: op is not supported");
}
/// @notice Returns the final values for amountIn, from, and emergencyTo based on masking logic.
/// @param currentAmountIn The current input amount, can be type(uint256).max for masking.
/// @param currentFrom The current sender address (as bytes32), may be 0.
/// @param currentEmergencyTo The current emergency address (as bytes32), may be 0.
/// @param prevMaskedParams Previously masked values used to fill in missing data.
/// @return amountIn Final resolved input amount.
/// @return from Final resolved sender address.
/// @return emergencyTo Final resolved emergency address.
function checkMaskedParams(
uint256 currentAmountIn,
bytes32 currentFrom,
bytes32 currentEmergencyTo,
ICoreFacet.MaskedParams memory prevMaskedParams
) internal view returns (uint256 amountIn, bytes32 from, bytes32 emergencyTo) {
amountIn = currentAmountIn == type(uint256).max ? prevMaskedParams.amountOut : currentAmountIn;
if (currentFrom != 0) {
require(TypecastLib.castToAddress(currentFrom) == msg.sender, "Router: wrong sender");
from = currentFrom;
} else {
from = prevMaskedParams.to;
}
if (CoreFacetStorage.currentRequestId() == 0 && currentEmergencyTo != 0) {
require(TypecastLib.castToAddress(currentEmergencyTo) == msg.sender, "Router: wrong emergencyTo");
emergencyTo = currentEmergencyTo;
} else {
emergencyTo = prevMaskedParams.emergencyTo;
}
}
/// @notice Validates and resolves the correct `to` address for an operation sequence.
/// @dev Calls `coreOpCheckTo` on the current contract (as ICrosschainFacet), and if it returns zero,
/// falls back to calling `poolOpCheckTo` on the current contract (as ICurveFacet).
/// @param to The initially proposed recipient address (encoded as bytes32).
/// @param emergencyTo The emergency fallback address (encoded as bytes32).
/// @param chainId The chain ID for which the address resolution is taking place.
/// @param currentOp The identifier of the current operation (hashed name).
/// @param nextOp The identifier of the next operation in sequence (hashed name).
/// @return correctTo The final resolved recipient address (encoded as bytes32). If invalid, may revert.
function checkTo(bytes32 to, bytes32 emergencyTo, uint64 chainId, bytes32 currentOp, bytes32 nextOp) internal view returns (bytes32 correctTo) {
CoreFacetStorage.DS storage ds = CoreFacetStorage.ds();
require(
nextOp == 0 && to != 0 || nextOp != 0 && to == 0,
"Router: wrong to"
);
if (CoreFacetStorage.currentRequestId() == 0 && currentOp != UtilsOps.WRAP_CODE && currentOp != UtilsOps.UNWRAP_CODE) {
require(TypecastLib.castToAddress(emergencyTo) == msg.sender, "Router: emergencyTo is not equal the sender");
}
if (nextOp == bytes32(0)) {
correctTo = to;
} else if (nextOp == CrosschainOps.LOCK_MINT_CODE) {
correctTo = IAddressBook(ds.addressBook).portalV3(chainId);
} else if (nextOp == CrosschainOps.BURN_UNLOCK_CODE || nextOp == CrosschainOps.BURN_MINT_CODE) {
correctTo = IAddressBook(ds.addressBook).synthesisV3(chainId);
} else if (UtilsOps.WRAP_CODE == nextOp || UtilsOps.UNWRAP_CODE == nextOp) {
correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
} else if (nextOp == CurveAMMOps.ADD_CODE || nextOp == CurveAMMOps.REMOVE_CODE || nextOp == CurveAMMOps.SWAP_CODE) {
correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
} else if (nextOp == OftOps.BURN_MINT_OFT_CODE || nextOp == OftOps.EMERGENCY_MINT_OFT_CODE || nextOp == OftOps.PUSH_MINT_OFT_CODE) {
correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
} else if (nextOp == RubicOps.RUBIC_START_CODE) {
correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
} else if (nextOp == BreakOps.BREAK_CODE) {
correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
} else if (
nextOp == BungeeOps.BUNGEE_MANUAL_START_CODE ||
nextOp == BungeeOps.BUNGEE_AUTO_START_CODE
) {
correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
} else if (nextOp == RunnerOps.RUNNER_COLLECT_CODE) {
correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
}
}
/// @notice Validates a sequence of operation codes starting from a given position.
/// @param cPos Starting index in the operation sequence.
/// @param operationsCode Array of operation codes to validate.
/// @return true if the sequence is valid, false otherwise.
function checkOperations(uint256 cPos, bytes32[] memory operationsCode) internal view returns (bool) {
for (uint256 i = cPos; i < operationsCode.length - 1; i++) {
bytes32 operationCode = operationsCode[i];
if (operationCode == BreakOps.CANCEL_BREAK_CODE) {
if (operationsCode.length != 2) {
return false;
}
if (i != 0) {
return false;
}
if (i != operationsCode.length - 2) {
return false;
}
if (CoreFacetStorage.currentRequestId() != 0) {
return false;
}
continue;
}
if (
CoreFacetStorage.currentRequestId() != 0 &&
i == cPos &&
!(
operationCode == CrosschainOps.LOCK_MINT_CODE ||
operationCode == CrosschainOps.BURN_UNLOCK_CODE ||
operationCode == CrosschainOps.BURN_MINT_CODE ||
operationCode == CrosschainOps.EMERGENCY_UNLOCK_CODE ||
operationCode == CrosschainOps.EMERGENCY_MINT_CODE ||
operationCode == OftOps.BURN_MINT_OFT_CODE
)
) {
return false;
}
if (
(
operationCode == UtilsOps.PERMIT_CODE ||
operationCode == UtilsOps.WRAP_CODE ||
operationCode == OftOps.EMERGENCY_MINT_OFT_CODE ||
operationCode == OftOps.PUSH_MINT_OFT_CODE
) && i != 0
) {
return false;
}
if (operationCode == UtilsOps.UNWRAP_CODE && i != operationsCode.length - 2) {
return false;
}
if (
operationCode == CrosschainOps.LOCK_MINT_CODE &&
operationsCode[i + 1] == CrosschainOps.LOCK_MINT_CODE
) {
return false;
}
if (
operationCode == CrosschainOps.BURN_UNLOCK_CODE &&
operationsCode[i + 1] == CrosschainOps.BURN_UNLOCK_CODE
) {
return false;
}
if (
operationCode == CrosschainOps.BURN_MINT_CODE &&
operationsCode[i + 1] == CrosschainOps.BURN_MINT_CODE
) {
return false;
}
if (
(
operationCode == CrosschainOps.EMERGENCY_UNLOCK_CODE ||
operationCode == CrosschainOps.EMERGENCY_MINT_CODE ||
operationCode == OftOps.EMERGENCY_MINT_OFT_CODE
) &&
operationsCode.length > 2
) {
return false;
}
if (operationCode == BreakOps.BREAK_CODE) {
if (i == 0) {
return false;
}
if (i != operationsCode.length - 2) {
return false;
}
if (CoreFacetStorage.currentRequestId() != 0) {
return false;
}
bytes32 prev = operationsCode[i - 1];
if (
prev != RubicOps.RUBIC_START_CODE &&
prev != BungeeOps.BUNGEE_MANUAL_START_CODE &&
prev != BungeeOps.BUNGEE_AUTO_START_CODE &&
prev != RunnerOps.RUNNER_COLLECT_CODE
) {
return false;
}
continue;
}
if (operationCode == RubicOps.RUBIC_START_CODE) {
bool isTerminal = (i == operationsCode.length - 2);
bool followedByBr = (operationsCode[i + 1] == BreakOps.BREAK_CODE);
if (!isTerminal && !followedByBr) {
return false;
}
continue;
}
if (
operationCode == BungeeOps.BUNGEE_MANUAL_START_CODE ||
operationCode == BungeeOps.BUNGEE_AUTO_START_CODE
) {
bool isTerminal = (i == operationsCode.length - 2);
bool followedByBr = (operationsCode[i + 1] == BreakOps.BREAK_CODE);
if (!isTerminal && !followedByBr) {
return false;
}
continue;
}
if (operationCode == BungeeOps.BUNGEE_REFUND_CODE) {
if (i != 0) {
return false;
}
if (i != operationsCode.length - 2) {
return false;
}
if (CoreFacetStorage.currentRequestId() != 0) {
return false;
}
if (!CoreFacetStorage.isOriginNetwork()) {
return false;
}
continue;
}
if (operationCode == RunnerOps.RUNNER_COLLECT_CODE) {
if (i != 0) {
return false;
}
if (CoreFacetStorage.currentRequestId() != 0) {
return false;
}
if (!CoreFacetStorage.isOriginNetwork()) {
return false;
}
continue;
}
}
return true;
}
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity ^0.8.17;
library TypecastLib {
function castToAddress(bytes32 x) internal pure returns (address) {
return address(uint160(uint256(x)));
}
function castToBytes32(address a) internal pure returns (bytes32) {
return bytes32(uint256(uint160(a)));
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "cancun",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"left","type":"uint256"}],"name":"MsgValueBudgetExceeded","type":"error"},{"inputs":[],"name":"BURN_MINT_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BURN_UNLOCK_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EMERGENCY_MINT_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EMERGENCY_UNLOCK_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LOCK_MINT_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNWRAP_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WRAP_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"isOpHalfDone","type":"bool"},{"internalType":"bytes32","name":"op","type":"bytes32"},{"internalType":"bytes32","name":"nextOp","type":"bytes32"},{"internalType":"bytes","name":"params","type":"bytes"},{"components":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"bytes32","name":"to","type":"bytes32"},{"internalType":"bytes32","name":"emergencyTo","type":"bytes32"}],"internalType":"struct ICoreFacet.MaskedParams","name":"prevMaskedParams","type":"tuple"}],"name":"executeCrosschainOp","outputs":[{"internalType":"uint64","name":"chainIdTo","type":"uint64"},{"internalType":"bytes","name":"updatedParams","type":"bytes"},{"components":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"bytes32","name":"to","type":"bytes32"},{"internalType":"bytes32","name":"emergencyTo","type":"bytes32"}],"internalType":"struct ICoreFacet.MaskedParams","name":"maskedParams","type":"tuple"},{"internalType":"enum ICoreFacet.ExecutionResult","name":"result","type":"uint8"}],"stateMutability":"payable","type":"function"}]Contract Creation Code
6080604052348015600e575f80fd5b50612b318061001c5f395ff3fe608060405260043610610084575f3560e01c806376a3fb3e1161005757806376a3fb3e146100eb578063778c89b9146100ff5780639bb68c7514610113578063ad351f9f14610127578063f1613bea1461013b575f80fd5b80630ff53ba7146100885780632b385bcf146100af5780632ee63e44146100c35780636869cb96146100d7575b5f80fd5b348015610093575f80fd5b5061009c61015e565b6040519081526020015b60405180910390f35b3480156100ba575f80fd5b5061009c610189565b3480156100ce575f80fd5b5061009c61019e565b3480156100e2575f80fd5b5061009c6101b4565b3480156100f6575f80fd5b5061009c6101ca565b34801561010a575f80fd5b5061009c6101e0565b34801561011e575f80fd5b5061009c6101f6565b348015610132575f80fd5b5061009c61020c565b61014e610149366004612627565b610221565b6040516100a6949392919061272d565b60405161215560f01b60208201526022015b6040516020818303038152906040528051906020012081565b604051605760f81b6020820152602101610170565b604051614c4d60f01b6020820152602201610170565b60405161424d60f01b6020820152602201610170565b60405161425560f01b6020820152602201610170565b60405161557760f01b6020820152602201610170565b60405161214d60f01b6020820152602201610170565b604051600560fc1b6020820152602101610170565b60408051606081810183525f80835260208301819052928201839052905f7f2e445143a211ecbb689de5812742d02a96369c482aec76832fc56490cf3cc6f25c6102b25760405162461bcd60e51b815260206004820152601a60248201527f526f757465723a2069736e2774204469616d6f6e642063616c6c00000000000060448201526064015b60405180910390fd5b7f1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d5624985c156103165760405162461bcd60e51b8152602060048201526012602482015271526f757465723a207265656e7472616e637960701b60448201526064016102a9565b61032060016111d6565b50604051600560fc1b60208201526001905f80516020612adc83398151915290899060210160405160208183030381529060405280519060200120036104ee575f8780602001905181019061037591906127ab565b80519091505f610386836020015190565b6040808501516060860151608087015160a088015160c0890151945163d505accf60e01b81526001600160a01b0380881660048301523060248301526044820195909552606481019390935260ff909116608483015260a482015260c48101929092529192509083169063d505accf9060e4015f604051808303815f87803b158015610410575f80fd5b505af1925050508015610421575060015b6104e6576040838101519051636eb1769f60e11b81526001600160a01b03838116600483015230602483015284169063dd62ed3e90604401602060405180830381865afa158015610474573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104989190612814565b10156104e65760405162461bcd60e51b815260206004820152601f60248201527f43726f7373636861696e46616365743a207065726d6974206661696c7572650060448201526064016102a9565b5050506111c0565b604051614c4d60f01b60208201528990602201604051602081830303815290604052805190602001201480610549575060405161425560f01b6020820152899060220160405160208183030381529060405280519060200120145b8061057a575060405161424d60f01b6020820152899060220160405160208183030381529060405280519060200120145b15610bc3575f8780602001905181019061059491906128af565b90508a15155f03610a56576105b7816020015182604001518360c001518a6111fc565b60c0840181905260408401919091526020830191909152606082015160808301516105e492908d8d611307565b606082015280515f6105f7836040015190565b90508b60405160200161061290614c4d60f01b815260020190565b604051602081830303815290604052805190602001200361064a57610636836119ec565b6001600160401b03461660a0840152610a26565b600384015460405163d4f0cceb60e01b81526001600160401b03461660048201525f916001600160a01b03169063d4f0cceb90602401602060405180830381865afa15801561069b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106bf91906128c9565b6040516305b338c160e21b81526001600160a01b0385811660048301529192505f918316906316cce30490602401602060405180830381865afa158015610708573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061072c91906128c9565b90506001600160a01b0381161561078357816001600160a01b0316836001600160a01b031614610766576107668484848860200151611b24565b819250610779826001600160a01b031690565b60408601526108c6565b6003860154604080516393e59dc160e01b815290515f926001600160a01b0316916393e59dc19160048083019260209291908290030181865afa1580156107cc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107f091906128c9565b60405163166dbc3760e21b81526001600160a01b0387811660048301529192505f918316906359b6f0dc90602401602060405180830381865afa158015610839573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061085d91906128ef565b60ff1610156108c15760405162461bcd60e51b815260206004820152602a60248201527f43726f7373636861696e46616365743a2073796e7468206d75737420626520776044820152691a1a5d195b1a5cdd195960b21b60648201526084016102a9565b849150505b816001600160a01b031663b6ff156a858760200151866108e78a6060015190565b8a608001516040518663ffffffff1660e01b815260040161090c959493929190612908565b5f604051808303815f87803b158015610923575f80fd5b505af1158015610935573d5f803e3d5ffd5b505050505f8190506109ac816001600160a01b0316630e7c1cb56040518163ffffffff1660e01b8152600401602060405180830381865afa15801561097c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109a091906128c9565b6001600160a01b031690565b865f018181525050806001600160a01b031663376c16e86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109f0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a149190612943565b6001600160401b031660a08701525050505b8260800151975082604051602001610a3e919061295c565b60405160208183030381529060405296505050610bbd565b5f80516020612abc8339815191525c5f90815260028301602052604090206001015460ff1615610ac85760405162461bcd60e51b815260206004820152601d60248201527f43726f7373636861696e46616365743a206f702070726f63657373656400000060448201526064016102a9565b60405180604001604052808980519060200120815260200160016002811115610af357610af3612719565b60ff9081169091525f80516020612abc8339815191525c5f90815260028501602090815260408220845181559301516001909301805460ff19169390921692909217905560608201519003610b6157610b5b81606001518260c0015183608001518d8d611307565b60608201525b60405161425560f01b60208201528a906022016040516020818303038152906040528051906020012014610b9d57610b9881611b95565b610ba6565b610ba681611caf565b84526060810151602085015260c081015160408501525b506111c0565b604051605760f81b60208201528990602101604051602081830303815290604052805190602001201480610c1d575060405161557760f01b6020820152899060220160405160208183030381529060405280519060200120145b15610ccf575f87806020019051810190610c3791906129b8565b9050610c4f816020015182604001515f801b8a6111fc565b50604083015260208201526060810151610c6c905f468d8d611307565b6060820152604051605760f81b60208201528a906021016040516020818303038152906040528051906020012014610cac57610ca781611d7d565b610cb5565b610cb581611ece565b8452606001516020840152604086810151908401526111c0565b60405161214d60f01b60208201528990602201604051602081830303815290604052805190602001201480610d2a575060405161215560f01b6020820152899060220160405160208183030381529060405280519060200120145b156111bc575f87806020019051810190610d4491906129fc565b90508a15155f03610f3a5780515f9081526002830160209081526040918290208251808401909352805483526001015460ff16908201819052610e1357602082015115610ded5760405162461bcd60e51b815260206004820152603160248201527f43726f7373636861696e46616365743a2077726f6e67206861736853796e7468604482015270506172616d73202d206e6f74207a65726f60781b60648201526084016102a9565b81515f90815260028481016020526040909120600101805460ff19169091179055610f2d565b600160ff16816020015160ff1603610f2d578051602083015114610e945760405162461bcd60e51b815260206004820152603260248201527f43726f7373636861696e46616365743a2077726f6e67206861736853796e746860448201527114185c985b5cc80b481b9bdd08195c5d585b60721b60648201526084016102a9565b8160600151604051602001610ea9919061295c565b60405160208183030381529060405280519060200120826020015103610f2d5760405162461bcd60e51b815260206004820152603360248201527f43726f7373636861696e46616365743a2077726f6e6720656d657267656e6379604482015272081c185c985b595d195c9cc80b48195c5d585b606a1b60648201526084016102a9565b8160400151965050610bbd565b80515f90815260018301602052604081205490819003610f9c5760405162461bcd60e51b815260206004820152601f60248201527f43726f7373636861696e46616365743a206f70206e6f7420737461727465640060448201526064016102a9565b60208201511561101057816020015181036110105760405162461bcd60e51b815260206004820152602e60248201527f43726f7373636861696e46616365743a2077726f6e67206861736853796e746860448201526d14185c985b5cc80b48195c5d585b60921b60648201526084016102a9565b8160600151604051602001611025919061295c565b6040516020818303038152906040528051906020012081146110af5760405162461bcd60e51b815260206004820152603760248201527f43726f7373636861696e46616365743a2077726f6e6720656d657267656e637960448201527f20706172616d6574657273202d206e6f7420657175616c00000000000000000060648201526084016102a9565b6060820151608001516001600160401b03167f67b6f0fb6bca8398a49a7966c252819542f4d36560c9c6961937957fdf20f9f45c6001600160401b0316146111475760405162461bcd60e51b815260206004820152602560248201527f43726f7373636861696e46616365743a2077726f6e6720656d657267656e6379604482015264081a5b9a5d60da1b60648201526084016102a9565b81515f90815260018401602090815260408083209290925590518c91611176910161214d60f01b815260020190565b60405160208183030381529060405280519060200120036111a55761119e826060015161203a565b85526111b5565b6111b282606001516120ed565b85525b50506111c0565b5f91505b506111ca5f6111d6565b95509550955095915050565b807f1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d5624985d50565b5f805f5f19871461120d5786611210565b83515b9250851561126f5733866001600160a01b0316146112675760405162461bcd60e51b81526020600482015260146024820152732937baba32b91d103bb937b7339039b2b73232b960611b60448201526064016102a9565b859150611277565b836020015191505b5f80516020612abc8339815191525c15801561129257508415155b156112f65733856001600160a01b0316146112ef5760405162461bcd60e51b815260206004820152601960248201527f526f757465723a2077726f6e6720656d657267656e6379546f0000000000000060448201526064016102a9565b50836112fd565b5060408301515b9450945094915050565b5f5f80516020612adc8339815191528215801561132357508615155b8061133657508215801590611336575086155b6113755760405162461bcd60e51b815260206004820152601060248201526f526f757465723a2077726f6e6720746f60801b60448201526064016102a9565b5f80516020612abc8339815191525c1580156113b65750604051605760f81b6020820152602101604051602081830303815290604052805190602001208414155b80156113e8575060405161557760f01b6020820152602201604051602081830303815290604052805190602001208414155b156114595733866001600160a01b0316146114595760405162461bcd60e51b815260206004820152602b60248201527f526f757465723a20656d657267656e6379546f206973206e6f7420657175616c60448201526a103a34329039b2b73232b960a91b60648201526084016102a9565b82611466578691506119e2565b604051614c4d60f01b602082015260220160405160208183030381529060405280519060200120830361151057600381015460405163535cfbb760e11b81526001600160401b03871660048201526001600160a01b039091169063a6b9f76e906024015b602060405180830381865afa1580156114e5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115099190612814565b91506119e2565b60405161425560f01b602082015260220160405160208183030381529060405280519060200120831480611569575060405161424d60f01b60208201526022016040516020818303038152906040528051906020012083145b156115a95760038101546040516374a748ff60e11b81526001600160401b03871660048201526001600160a01b039091169063e94e91fe906024016114ca565b604051605760f81b60208201528390602101604051602081830303815290604052805190602001201480611603575060405161557760f01b6020820152839060220160405160208183030381529060405280519060200120145b156116435760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd91906024016114ca565b604051604160f81b60208201526021016040516020818303038152906040528051906020012083148061169a5750604051602960f91b60208201526021016040516020818303038152906040528051906020012083145b806116c95750604051605360f81b60208201526021016040516020818303038152906040528051906020012083145b156117095760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd91906024016114ca565b60405162424d6f60e81b602082015260230160405160208183030381529060405280519060200120831480611764575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012083145b80611795575060405162504d6f60e81b60208201526023016040516020818303038152906040528051906020012083145b156117d55760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd91906024016114ca565b60405161525360f01b602082015260220160405160208183030381529060405280519060200120830361183d5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd91906024016114ca565b60405161313960f11b60208201526022016040516020818303038152906040528051906020012083036118a55760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd91906024016114ca565b604051632126a0a760e11b602082015260240160405160208183030381529060405280519060200120831480611903575060405164424155544f60d81b60208201526025016040516020818303038152906040528051906020012083145b156119435760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd91906024016114ca565b7f33c4137323016cc703f06d36ae2c7f43f1a70fa030334bd76934bba665c80d6a83036119e25760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa1580156119bb573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119df9190612814565b91505b5095945050505050565b5f5f80516020612adc83398151915260030154604051630f5427af60e41b81526001600160401b03461660048201526001600160a01b0390911691505f90829063f5427af090602401602060405180830381865afa158015611a50573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a7491906128c9565b83519091505f611a85856040015190565b9050826001600160a01b0316816001600160a01b031614611ab057611ab08282858860200151611b24565b826001600160a01b0316633fea56b883876020015184611ad18a6060015190565b6040518563ffffffff1660e01b8152600401611af09493929190612a4d565b5f604051808303815f87803b158015611b07575f80fd5b505af1158015611b19573d5f803e3d5ffd5b505050505050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052611b8f9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612294565b50505050565b5f805f80516020612adc8339815191526003015460405163d4f0cceb60e01b81526001600160401b03461660048201526001600160a01b039091169063d4f0cceb90602401602060405180830381865afa158015611bf5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c1991906128c9565b9050806001600160a01b031663df543e81611c34855f015190565b6020860151604087015160608801518860a001516040518663ffffffff1660e01b8152600401611c68959493929190612908565b6020604051808303815f875af1158015611c84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ca89190612814565b9392505050565b5f805f80516020612adc83398151915260030154604051630f5427af60e41b81526001600160401b03461660048201526001600160a01b039091169063f5427af090602401602060405180830381865afa158015611d0f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d3391906128c9565b9050806001600160a01b031663fe4f4b72611d4e855f015190565b6020860151604087015160608801515b6040518563ffffffff1660e01b8152600401611c689493929190612a4d565b5f80611d8a836040015190565b83519091506001600160a01b0382163014611daf57611daf8183308760200151611b24565b6020840151604051632e1a7d4d60e01b815260048101919091526001600160a01b03821690632e1a7d4d906024015f604051808303815f87803b158015611df4575f80fd5b505af1158015611e06573d5f803e3d5ffd5b505050505f611e16856060015190565b6001600160a01b031685602001516040515f6040518083038185875af1925050503d805f8114611e61576040519150601f19603f3d011682016040523d82523d5f602084013e611e66565b606091505b5050905080611ec35760405162461bcd60e51b815260206004820152602360248201527f43726f7373636861696e46616365743a206661696c656420746f2073656e642060448201526208aa8960eb1b60648201526084016102a9565b505050506020015190565b5f7fb13bd13498ce9409b85a4287d16ff0fcdaca732822a47a97d2bdada3325aa4515c15611f0857611f03826020015161236c565b611fc2565b3415611f645760405162461bcd60e51b815260206004820152602560248201527f43726f7373636861696e46616365743a20756e6578706563746564206d73672e60448201526476616c756560d81b60648201526084016102a9565b8160200151471015611fc25760405162461bcd60e51b815260206004820152602160248201527f43726f7373636861696e46616365743a20696e73756666696369656e742045546044820152600960fb1b60648201526084016102a9565b8151806001600160a01b031663d0e30db084602001516040518263ffffffff1660e01b81526004015f604051808303818588803b158015612001575f80fd5b505af1158015612013573d5f803e3d5ffd5b505050505061203181612027856060015190565b85602001516123e3565b50506020015190565b5f805f80516020612adc83398151915260030154604051630f5427af60e41b81526001600160401b03461660048201526001600160a01b039091169063f5427af090602401602060405180830381865afa15801561209a573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120be91906128c9565b9050806001600160a01b031663c26b5bab6120d9855f015190565b6020860151604087015160c0880151611d5e565b5f805f80516020612adc8339815191526003015460405163d4f0cceb60e01b81526001600160401b03461660048201526001600160a01b039091169063d4f0cceb90602401602060405180830381865afa15801561214d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061217191906128c9565b90505f816001600160a01b0316637f9fa89f8560a00151612192875f015190565b6040516001600160e01b031960e085901b1681526001600160401b0390921660048301526001600160a01b03166024820152604401602060405180830381865afa1580156121e2573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061220691906128c9565b6001600160a01b038082168652602086015160408088015160c0890151915163550bf7db60e11b81529495509286169363aa17efb69361224c9387939092600401612a4d565b6020604051808303815f875af1158015612268573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061228c9190612814565b949350505050565b5f6122e8826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166124139092919063ffffffff16565b905080515f14806123085750808060200190518101906123089190612a78565b6123675760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016102a9565b505050565b7f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5c808211156123b95760405163161531e360e21b815260048101839052602481018290526044016102a9565b819003807f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5d5050565b6040516001600160a01b03831660248201526044810182905261236790849063a9059cbb60e01b90606401611b58565b606061228c84845f85855f80866001600160a01b031685876040516124389190612a93565b5f6040518083038185875af1925050503d805f8114612472576040519150601f19603f3d011682016040523d82523d5f602084013e612477565b606091505b509150915061248887838387612493565b979650505050505050565b606083156125015782515f036124fa576001600160a01b0385163b6124fa5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016102a9565b508161228c565b61228c83838151156125165781518083602001fd5b8060405162461bcd60e51b81526004016102a99190612aa9565b801515811461253d575f80fd5b50565b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b038111828210171561257657612576612540565b60405290565b604051608081016001600160401b038111828210171561257657612576612540565b604051601f8201601f191681016001600160401b03811182821017156125c6576125c6612540565b604052919050565b5f606082840312156125de575f80fd5b604051606081018181106001600160401b038211171561260057612600612540565b80604052508091508235815260208301356020820152604083013560408201525092915050565b5f805f805f60e0868803121561263b575f80fd5b853561264681612530565b945060208681013594506040870135935060608701356001600160401b0380821115612670575f80fd5b818901915089601f830112612683575f80fd5b81358181111561269557612695612540565b6126a7601f8201601f1916850161259e565b91508082528a848285010111156126bc575f80fd5b80848401858401375f848284010152508094505050506126df87608088016125ce565b90509295509295909350565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b634e487b7160e01b5f52602160045260245ffd5b6001600160401b038516815260c060208201525f61274e60c08301866126eb565b90508351604083015260208401516060830152604084015160808301526003831061278757634e487b7160e01b5f52602160045260245ffd5b8260a083015295945050505050565b805160ff811681146127a6575f80fd5b919050565b5f60e082840312156127bb575f80fd5b6127c3612554565b825181526020830151602082015260408301516040820152606083015160608201526127f160808401612796565b608082015260a083015160a082015260c083015160c08201528091505092915050565b5f60208284031215612824575f80fd5b5051919050565b80516001600160401b03811681146127a6575f80fd5b5f60e08284031215612851575f80fd5b612859612554565b9050815181526020820151602082015260408201516040820152606082015160608201526128896080830161282b565b608082015261289a60a0830161282b565b60a082015260c082015160c082015292915050565b5f60e082840312156128bf575f80fd5b611ca88383612841565b5f602082840312156128d9575f80fd5b81516001600160a01b0381168114611ca8575f80fd5b5f602082840312156128ff575f80fd5b611ca882612796565b6001600160a01b0395861681526020810194909452918416604084015290921660608201526001600160401b03909116608082015260a00190565b5f60208284031215612953575f80fd5b611ca88261282b565b5f60e0820190508251825260208301516020830152604083015160408301526060830151606083015260808301516001600160401b0380821660808501528060a08601511660a0850152505060c083015160c083015292915050565b5f608082840312156129c8575f80fd5b6129d061257c565b825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b5f6101408284031215612a0d575f80fd5b612a1561257c565b8251815260208301516020820152612a2f6040840161282b565b6040820152612a418460608501612841565b60608201529392505050565b6001600160a01b03948516815260208101939093529083166040830152909116606082015260800190565b5f60208284031215612a88575f80fd5b8151611ca881612530565b5f82518060208501845e5f920191825250919050565b602081525f611ca860208301846126eb56fe4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f41985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8101fa2646970667358221220ffa2eb3431141f2b6d63201138f599b0ded91f3a0360622e12c01664a44bba1064736f6c63430008190033
Deployed Bytecode
0x608060405260043610610084575f3560e01c806376a3fb3e1161005757806376a3fb3e146100eb578063778c89b9146100ff5780639bb68c7514610113578063ad351f9f14610127578063f1613bea1461013b575f80fd5b80630ff53ba7146100885780632b385bcf146100af5780632ee63e44146100c35780636869cb96146100d7575b5f80fd5b348015610093575f80fd5b5061009c61015e565b6040519081526020015b60405180910390f35b3480156100ba575f80fd5b5061009c610189565b3480156100ce575f80fd5b5061009c61019e565b3480156100e2575f80fd5b5061009c6101b4565b3480156100f6575f80fd5b5061009c6101ca565b34801561010a575f80fd5b5061009c6101e0565b34801561011e575f80fd5b5061009c6101f6565b348015610132575f80fd5b5061009c61020c565b61014e610149366004612627565b610221565b6040516100a6949392919061272d565b60405161215560f01b60208201526022015b6040516020818303038152906040528051906020012081565b604051605760f81b6020820152602101610170565b604051614c4d60f01b6020820152602201610170565b60405161424d60f01b6020820152602201610170565b60405161425560f01b6020820152602201610170565b60405161557760f01b6020820152602201610170565b60405161214d60f01b6020820152602201610170565b604051600560fc1b6020820152602101610170565b60408051606081810183525f80835260208301819052928201839052905f7f2e445143a211ecbb689de5812742d02a96369c482aec76832fc56490cf3cc6f25c6102b25760405162461bcd60e51b815260206004820152601a60248201527f526f757465723a2069736e2774204469616d6f6e642063616c6c00000000000060448201526064015b60405180910390fd5b7f1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d5624985c156103165760405162461bcd60e51b8152602060048201526012602482015271526f757465723a207265656e7472616e637960701b60448201526064016102a9565b61032060016111d6565b50604051600560fc1b60208201526001905f80516020612adc83398151915290899060210160405160208183030381529060405280519060200120036104ee575f8780602001905181019061037591906127ab565b80519091505f610386836020015190565b6040808501516060860151608087015160a088015160c0890151945163d505accf60e01b81526001600160a01b0380881660048301523060248301526044820195909552606481019390935260ff909116608483015260a482015260c48101929092529192509083169063d505accf9060e4015f604051808303815f87803b158015610410575f80fd5b505af1925050508015610421575060015b6104e6576040838101519051636eb1769f60e11b81526001600160a01b03838116600483015230602483015284169063dd62ed3e90604401602060405180830381865afa158015610474573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104989190612814565b10156104e65760405162461bcd60e51b815260206004820152601f60248201527f43726f7373636861696e46616365743a207065726d6974206661696c7572650060448201526064016102a9565b5050506111c0565b604051614c4d60f01b60208201528990602201604051602081830303815290604052805190602001201480610549575060405161425560f01b6020820152899060220160405160208183030381529060405280519060200120145b8061057a575060405161424d60f01b6020820152899060220160405160208183030381529060405280519060200120145b15610bc3575f8780602001905181019061059491906128af565b90508a15155f03610a56576105b7816020015182604001518360c001518a6111fc565b60c0840181905260408401919091526020830191909152606082015160808301516105e492908d8d611307565b606082015280515f6105f7836040015190565b90508b60405160200161061290614c4d60f01b815260020190565b604051602081830303815290604052805190602001200361064a57610636836119ec565b6001600160401b03461660a0840152610a26565b600384015460405163d4f0cceb60e01b81526001600160401b03461660048201525f916001600160a01b03169063d4f0cceb90602401602060405180830381865afa15801561069b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106bf91906128c9565b6040516305b338c160e21b81526001600160a01b0385811660048301529192505f918316906316cce30490602401602060405180830381865afa158015610708573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061072c91906128c9565b90506001600160a01b0381161561078357816001600160a01b0316836001600160a01b031614610766576107668484848860200151611b24565b819250610779826001600160a01b031690565b60408601526108c6565b6003860154604080516393e59dc160e01b815290515f926001600160a01b0316916393e59dc19160048083019260209291908290030181865afa1580156107cc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107f091906128c9565b60405163166dbc3760e21b81526001600160a01b0387811660048301529192505f918316906359b6f0dc90602401602060405180830381865afa158015610839573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061085d91906128ef565b60ff1610156108c15760405162461bcd60e51b815260206004820152602a60248201527f43726f7373636861696e46616365743a2073796e7468206d75737420626520776044820152691a1a5d195b1a5cdd195960b21b60648201526084016102a9565b849150505b816001600160a01b031663b6ff156a858760200151866108e78a6060015190565b8a608001516040518663ffffffff1660e01b815260040161090c959493929190612908565b5f604051808303815f87803b158015610923575f80fd5b505af1158015610935573d5f803e3d5ffd5b505050505f8190506109ac816001600160a01b0316630e7c1cb56040518163ffffffff1660e01b8152600401602060405180830381865afa15801561097c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109a091906128c9565b6001600160a01b031690565b865f018181525050806001600160a01b031663376c16e86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109f0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a149190612943565b6001600160401b031660a08701525050505b8260800151975082604051602001610a3e919061295c565b60405160208183030381529060405296505050610bbd565b5f80516020612abc8339815191525c5f90815260028301602052604090206001015460ff1615610ac85760405162461bcd60e51b815260206004820152601d60248201527f43726f7373636861696e46616365743a206f702070726f63657373656400000060448201526064016102a9565b60405180604001604052808980519060200120815260200160016002811115610af357610af3612719565b60ff9081169091525f80516020612abc8339815191525c5f90815260028501602090815260408220845181559301516001909301805460ff19169390921692909217905560608201519003610b6157610b5b81606001518260c0015183608001518d8d611307565b60608201525b60405161425560f01b60208201528a906022016040516020818303038152906040528051906020012014610b9d57610b9881611b95565b610ba6565b610ba681611caf565b84526060810151602085015260c081015160408501525b506111c0565b604051605760f81b60208201528990602101604051602081830303815290604052805190602001201480610c1d575060405161557760f01b6020820152899060220160405160208183030381529060405280519060200120145b15610ccf575f87806020019051810190610c3791906129b8565b9050610c4f816020015182604001515f801b8a6111fc565b50604083015260208201526060810151610c6c905f468d8d611307565b6060820152604051605760f81b60208201528a906021016040516020818303038152906040528051906020012014610cac57610ca781611d7d565b610cb5565b610cb581611ece565b8452606001516020840152604086810151908401526111c0565b60405161214d60f01b60208201528990602201604051602081830303815290604052805190602001201480610d2a575060405161215560f01b6020820152899060220160405160208183030381529060405280519060200120145b156111bc575f87806020019051810190610d4491906129fc565b90508a15155f03610f3a5780515f9081526002830160209081526040918290208251808401909352805483526001015460ff16908201819052610e1357602082015115610ded5760405162461bcd60e51b815260206004820152603160248201527f43726f7373636861696e46616365743a2077726f6e67206861736853796e7468604482015270506172616d73202d206e6f74207a65726f60781b60648201526084016102a9565b81515f90815260028481016020526040909120600101805460ff19169091179055610f2d565b600160ff16816020015160ff1603610f2d578051602083015114610e945760405162461bcd60e51b815260206004820152603260248201527f43726f7373636861696e46616365743a2077726f6e67206861736853796e746860448201527114185c985b5cc80b481b9bdd08195c5d585b60721b60648201526084016102a9565b8160600151604051602001610ea9919061295c565b60405160208183030381529060405280519060200120826020015103610f2d5760405162461bcd60e51b815260206004820152603360248201527f43726f7373636861696e46616365743a2077726f6e6720656d657267656e6379604482015272081c185c985b595d195c9cc80b48195c5d585b606a1b60648201526084016102a9565b8160400151965050610bbd565b80515f90815260018301602052604081205490819003610f9c5760405162461bcd60e51b815260206004820152601f60248201527f43726f7373636861696e46616365743a206f70206e6f7420737461727465640060448201526064016102a9565b60208201511561101057816020015181036110105760405162461bcd60e51b815260206004820152602e60248201527f43726f7373636861696e46616365743a2077726f6e67206861736853796e746860448201526d14185c985b5cc80b48195c5d585b60921b60648201526084016102a9565b8160600151604051602001611025919061295c565b6040516020818303038152906040528051906020012081146110af5760405162461bcd60e51b815260206004820152603760248201527f43726f7373636861696e46616365743a2077726f6e6720656d657267656e637960448201527f20706172616d6574657273202d206e6f7420657175616c00000000000000000060648201526084016102a9565b6060820151608001516001600160401b03167f67b6f0fb6bca8398a49a7966c252819542f4d36560c9c6961937957fdf20f9f45c6001600160401b0316146111475760405162461bcd60e51b815260206004820152602560248201527f43726f7373636861696e46616365743a2077726f6e6720656d657267656e6379604482015264081a5b9a5d60da1b60648201526084016102a9565b81515f90815260018401602090815260408083209290925590518c91611176910161214d60f01b815260020190565b60405160208183030381529060405280519060200120036111a55761119e826060015161203a565b85526111b5565b6111b282606001516120ed565b85525b50506111c0565b5f91505b506111ca5f6111d6565b95509550955095915050565b807f1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d5624985d50565b5f805f5f19871461120d5786611210565b83515b9250851561126f5733866001600160a01b0316146112675760405162461bcd60e51b81526020600482015260146024820152732937baba32b91d103bb937b7339039b2b73232b960611b60448201526064016102a9565b859150611277565b836020015191505b5f80516020612abc8339815191525c15801561129257508415155b156112f65733856001600160a01b0316146112ef5760405162461bcd60e51b815260206004820152601960248201527f526f757465723a2077726f6e6720656d657267656e6379546f0000000000000060448201526064016102a9565b50836112fd565b5060408301515b9450945094915050565b5f5f80516020612adc8339815191528215801561132357508615155b8061133657508215801590611336575086155b6113755760405162461bcd60e51b815260206004820152601060248201526f526f757465723a2077726f6e6720746f60801b60448201526064016102a9565b5f80516020612abc8339815191525c1580156113b65750604051605760f81b6020820152602101604051602081830303815290604052805190602001208414155b80156113e8575060405161557760f01b6020820152602201604051602081830303815290604052805190602001208414155b156114595733866001600160a01b0316146114595760405162461bcd60e51b815260206004820152602b60248201527f526f757465723a20656d657267656e6379546f206973206e6f7420657175616c60448201526a103a34329039b2b73232b960a91b60648201526084016102a9565b82611466578691506119e2565b604051614c4d60f01b602082015260220160405160208183030381529060405280519060200120830361151057600381015460405163535cfbb760e11b81526001600160401b03871660048201526001600160a01b039091169063a6b9f76e906024015b602060405180830381865afa1580156114e5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115099190612814565b91506119e2565b60405161425560f01b602082015260220160405160208183030381529060405280519060200120831480611569575060405161424d60f01b60208201526022016040516020818303038152906040528051906020012083145b156115a95760038101546040516374a748ff60e11b81526001600160401b03871660048201526001600160a01b039091169063e94e91fe906024016114ca565b604051605760f81b60208201528390602101604051602081830303815290604052805190602001201480611603575060405161557760f01b6020820152839060220160405160208183030381529060405280519060200120145b156116435760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd91906024016114ca565b604051604160f81b60208201526021016040516020818303038152906040528051906020012083148061169a5750604051602960f91b60208201526021016040516020818303038152906040528051906020012083145b806116c95750604051605360f81b60208201526021016040516020818303038152906040528051906020012083145b156117095760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd91906024016114ca565b60405162424d6f60e81b602082015260230160405160208183030381529060405280519060200120831480611764575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012083145b80611795575060405162504d6f60e81b60208201526023016040516020818303038152906040528051906020012083145b156117d55760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd91906024016114ca565b60405161525360f01b602082015260220160405160208183030381529060405280519060200120830361183d5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd91906024016114ca565b60405161313960f11b60208201526022016040516020818303038152906040528051906020012083036118a55760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd91906024016114ca565b604051632126a0a760e11b602082015260240160405160208183030381529060405280519060200120831480611903575060405164424155544f60d81b60208201526025016040516020818303038152906040528051906020012083145b156119435760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd91906024016114ca565b7f33c4137323016cc703f06d36ae2c7f43f1a70fa030334bd76934bba665c80d6a83036119e25760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa1580156119bb573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119df9190612814565b91505b5095945050505050565b5f5f80516020612adc83398151915260030154604051630f5427af60e41b81526001600160401b03461660048201526001600160a01b0390911691505f90829063f5427af090602401602060405180830381865afa158015611a50573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a7491906128c9565b83519091505f611a85856040015190565b9050826001600160a01b0316816001600160a01b031614611ab057611ab08282858860200151611b24565b826001600160a01b0316633fea56b883876020015184611ad18a6060015190565b6040518563ffffffff1660e01b8152600401611af09493929190612a4d565b5f604051808303815f87803b158015611b07575f80fd5b505af1158015611b19573d5f803e3d5ffd5b505050505050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052611b8f9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612294565b50505050565b5f805f80516020612adc8339815191526003015460405163d4f0cceb60e01b81526001600160401b03461660048201526001600160a01b039091169063d4f0cceb90602401602060405180830381865afa158015611bf5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c1991906128c9565b9050806001600160a01b031663df543e81611c34855f015190565b6020860151604087015160608801518860a001516040518663ffffffff1660e01b8152600401611c68959493929190612908565b6020604051808303815f875af1158015611c84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ca89190612814565b9392505050565b5f805f80516020612adc83398151915260030154604051630f5427af60e41b81526001600160401b03461660048201526001600160a01b039091169063f5427af090602401602060405180830381865afa158015611d0f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d3391906128c9565b9050806001600160a01b031663fe4f4b72611d4e855f015190565b6020860151604087015160608801515b6040518563ffffffff1660e01b8152600401611c689493929190612a4d565b5f80611d8a836040015190565b83519091506001600160a01b0382163014611daf57611daf8183308760200151611b24565b6020840151604051632e1a7d4d60e01b815260048101919091526001600160a01b03821690632e1a7d4d906024015f604051808303815f87803b158015611df4575f80fd5b505af1158015611e06573d5f803e3d5ffd5b505050505f611e16856060015190565b6001600160a01b031685602001516040515f6040518083038185875af1925050503d805f8114611e61576040519150601f19603f3d011682016040523d82523d5f602084013e611e66565b606091505b5050905080611ec35760405162461bcd60e51b815260206004820152602360248201527f43726f7373636861696e46616365743a206661696c656420746f2073656e642060448201526208aa8960eb1b60648201526084016102a9565b505050506020015190565b5f7fb13bd13498ce9409b85a4287d16ff0fcdaca732822a47a97d2bdada3325aa4515c15611f0857611f03826020015161236c565b611fc2565b3415611f645760405162461bcd60e51b815260206004820152602560248201527f43726f7373636861696e46616365743a20756e6578706563746564206d73672e60448201526476616c756560d81b60648201526084016102a9565b8160200151471015611fc25760405162461bcd60e51b815260206004820152602160248201527f43726f7373636861696e46616365743a20696e73756666696369656e742045546044820152600960fb1b60648201526084016102a9565b8151806001600160a01b031663d0e30db084602001516040518263ffffffff1660e01b81526004015f604051808303818588803b158015612001575f80fd5b505af1158015612013573d5f803e3d5ffd5b505050505061203181612027856060015190565b85602001516123e3565b50506020015190565b5f805f80516020612adc83398151915260030154604051630f5427af60e41b81526001600160401b03461660048201526001600160a01b039091169063f5427af090602401602060405180830381865afa15801561209a573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120be91906128c9565b9050806001600160a01b031663c26b5bab6120d9855f015190565b6020860151604087015160c0880151611d5e565b5f805f80516020612adc8339815191526003015460405163d4f0cceb60e01b81526001600160401b03461660048201526001600160a01b039091169063d4f0cceb90602401602060405180830381865afa15801561214d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061217191906128c9565b90505f816001600160a01b0316637f9fa89f8560a00151612192875f015190565b6040516001600160e01b031960e085901b1681526001600160401b0390921660048301526001600160a01b03166024820152604401602060405180830381865afa1580156121e2573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061220691906128c9565b6001600160a01b038082168652602086015160408088015160c0890151915163550bf7db60e11b81529495509286169363aa17efb69361224c9387939092600401612a4d565b6020604051808303815f875af1158015612268573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061228c9190612814565b949350505050565b5f6122e8826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166124139092919063ffffffff16565b905080515f14806123085750808060200190518101906123089190612a78565b6123675760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016102a9565b505050565b7f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5c808211156123b95760405163161531e360e21b815260048101839052602481018290526044016102a9565b819003807f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5d5050565b6040516001600160a01b03831660248201526044810182905261236790849063a9059cbb60e01b90606401611b58565b606061228c84845f85855f80866001600160a01b031685876040516124389190612a93565b5f6040518083038185875af1925050503d805f8114612472576040519150601f19603f3d011682016040523d82523d5f602084013e612477565b606091505b509150915061248887838387612493565b979650505050505050565b606083156125015782515f036124fa576001600160a01b0385163b6124fa5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016102a9565b508161228c565b61228c83838151156125165781518083602001fd5b8060405162461bcd60e51b81526004016102a99190612aa9565b801515811461253d575f80fd5b50565b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b038111828210171561257657612576612540565b60405290565b604051608081016001600160401b038111828210171561257657612576612540565b604051601f8201601f191681016001600160401b03811182821017156125c6576125c6612540565b604052919050565b5f606082840312156125de575f80fd5b604051606081018181106001600160401b038211171561260057612600612540565b80604052508091508235815260208301356020820152604083013560408201525092915050565b5f805f805f60e0868803121561263b575f80fd5b853561264681612530565b945060208681013594506040870135935060608701356001600160401b0380821115612670575f80fd5b818901915089601f830112612683575f80fd5b81358181111561269557612695612540565b6126a7601f8201601f1916850161259e565b91508082528a848285010111156126bc575f80fd5b80848401858401375f848284010152508094505050506126df87608088016125ce565b90509295509295909350565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b634e487b7160e01b5f52602160045260245ffd5b6001600160401b038516815260c060208201525f61274e60c08301866126eb565b90508351604083015260208401516060830152604084015160808301526003831061278757634e487b7160e01b5f52602160045260245ffd5b8260a083015295945050505050565b805160ff811681146127a6575f80fd5b919050565b5f60e082840312156127bb575f80fd5b6127c3612554565b825181526020830151602082015260408301516040820152606083015160608201526127f160808401612796565b608082015260a083015160a082015260c083015160c08201528091505092915050565b5f60208284031215612824575f80fd5b5051919050565b80516001600160401b03811681146127a6575f80fd5b5f60e08284031215612851575f80fd5b612859612554565b9050815181526020820151602082015260408201516040820152606082015160608201526128896080830161282b565b608082015261289a60a0830161282b565b60a082015260c082015160c082015292915050565b5f60e082840312156128bf575f80fd5b611ca88383612841565b5f602082840312156128d9575f80fd5b81516001600160a01b0381168114611ca8575f80fd5b5f602082840312156128ff575f80fd5b611ca882612796565b6001600160a01b0395861681526020810194909452918416604084015290921660608201526001600160401b03909116608082015260a00190565b5f60208284031215612953575f80fd5b611ca88261282b565b5f60e0820190508251825260208301516020830152604083015160408301526060830151606083015260808301516001600160401b0380821660808501528060a08601511660a0850152505060c083015160c083015292915050565b5f608082840312156129c8575f80fd5b6129d061257c565b825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b5f6101408284031215612a0d575f80fd5b612a1561257c565b8251815260208301516020820152612a2f6040840161282b565b6040820152612a418460608501612841565b60608201529392505050565b6001600160a01b03948516815260208101939093529083166040830152909116606082015260800190565b5f60208284031215612a88575f80fd5b8151611ca881612530565b5f82518060208501845e5f920191825250919050565b602081525f611ca860208301846126eb56fe4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f41985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8101fa2646970667358221220ffa2eb3431141f2b6d63201138f599b0ded91f3a0360622e12c01664a44bba1064736f6c63430008190033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.