Source Code
Overview
S Balance
More Info
ContractCreator
Multichain Info
N/A
Loading...
Loading
Contract Name:
OFTFacet
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 {IOFT, SendParam, MessagingReceipt, MessagingFee} from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol";
import {IOAppCore} from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol";
import {OpsOFT, Ops} from "./Ops.sol";
import {IOFTFacet, ICoreFacet} from "./interfaces/IOFTFacet.sol";
import {IAccessControlFacet} from "./interfaces/IAccessControlFacet.sol";
import {IOFTAdapter} from "./interfaces/external/IOFTAdapter.sol";
import {IAddressBook} from "../interfaces/IAddressBook.sol";
import {ITreasury} from "../interfaces/ITreasury.sol";
import {OFTFacetStorage, ResumedOps} from "./libraries/OFTFacetStorage.sol";
import {CoreFacetStorage} from "./libraries/CoreFacetStorage.sol";
import {TypecastLib} from "../utils/TypecastLib.sol";
contract OFTFacet is IOFTFacet, OpsOFT, Ops {
using OFTFacetStorage for OFTFacetStorage.DS;
using CoreFacetStorage for CoreFacetStorage.DS;
/// @notice Default admin role identifier (0x00).
/// @dev Accounts with this role can perform operational tasks like setting end point.
bytes32 private constant DEFAULT_ADMIN_ROLE = 0x00;
/// @dev Modifier that checks that the caller has a specific role.
/// @param role The role identifier to check.
modifier onlyRole(bytes32 role) {
require(IAccessControlFacet(address(this)).hasRole(role, msg.sender), "OFTFacet: missing role");
_;
}
/// @inheritdoc IOFTFacet
function executeOftOp(
ExecuteOftParams memory ep
)
external
payable
onlyDiamond
returns (
uint64 chainIdTo,
bytes memory updatedParams,
ICoreFacet.MaskedParams memory maskedParams,
ICoreFacet.ExecutionResult result
)
{
bytes32 op = ep.operationsCode[ep.cPos];
bytes32 nextOp = ep.operationsCode[ep.cPos + 1];
result = ICoreFacet.ExecutionResult.Succeeded;
if (BURN_MINT_OFT_CODE == op) {
IOFTFacet.OftParams memory p = abi.decode(ep.params[ep.cPos], (IOFTFacet.OftParams));
IERC20 token;
IOFT oft = IOFT(TypecastLib.castToAddress(p.oft));
if (OFTFacetStorage.lzComposeIsProcessed() || (ep.cPos >= 1 && ep.operationsCode[ep.cPos - 1] == PUSH_MINT_OFT_CODE)) {
if (p.to == 0) {
p.to = checkTo(p.to, p.emergencyTo, p.chainIdTo, op, nextOp);
}
token = IERC20(_getTokenAddress(TypecastLib.castToAddress(p.oft), p.oftTypeDist));
if(TypecastLib.castToAddress(p.to) != address(this)) {
SafeERC20.safeTransfer(
token,
TypecastLib.castToAddress(p.to),
p.amountIn
);
}
maskedParams = ICoreFacet.MaskedParams(p.amountIn, p.to, p.emergencyTo);
OFTFacetStorage.setLzComposeIsProcessed(false);
} else {
token = IERC20(_getTokenAddress(TypecastLib.castToAddress(p.oft), p.oftTypeSource));
chainIdTo = p.chainIdTo;
_checkAndUpdateParams(p, op, nextOp, ep.prevMaskedParams);
if (TypecastLib.castToAddress(p.from) != address(this)) {
SafeERC20.safeTransferFrom(token, TypecastLib.castToAddress(p.from), address(this), p.amountIn);
}
if (p.oftTypeSource == OftType.Adapter) {
token.approve(address(oft), p.amountIn);
}
ep.params[ep.cPos] = abi.encode(p);
SendParam memory sendParam = _getSendParam(p, ep.cPos, ep.operations, ep.params, ep.bridgeOptions);
MessagingFee memory messagingFee = oft.quoteSend(sendParam, false);
require(p.nativeFeeLimit >= messagingFee.nativeFee, "OFTFacet: native fee limit exceeded");
if (address(this).balance < messagingFee.nativeFee) {
ITreasury(IAddressBook(CoreFacetStorage.ds().addressBook).treasury()).withdraw(
"",
address(0),
messagingFee.nativeFee - address(this).balance,
address(this)
);
}
_checkMsgValue(messagingFee.nativeFee);
(
MessagingReceipt memory messagingReceipt,
) = oft.send{ value: messagingFee.nativeFee}(sendParam, messagingFee, TypecastLib.castToAddress(p.emergencyTo));
emit OFTProcessed(messagingReceipt.guid, chainIdTo, OftOpState.Unknown);
}
} else if (EMERGENCY_MINT_OFT_CODE == op) {
IOFTFacet.EmergencyOftParams memory p = abi.decode(ep.params[ep.cPos], (IOFTFacet.EmergencyOftParams));
_checkAndUpdateProcessedOps(p.guid);
require(
OFTFacetStorage.ds().resumedOps[p.guid].bmoOpHash == keccak256(abi.encode(p.oftParams)),
"OFTFacet: wrong emergency parameters"
);
_deleteCorrectedOpsInfo(p.guid);
SafeERC20.safeTransfer(
IERC20(_getTokenAddress(TypecastLib.castToAddress(p.oftParams.oft), p.oftParams.oftTypeDist)),
TypecastLib.castToAddress(p.oftParams.emergencyTo),
p.oftParams.amountIn
);
emit OFTProcessed(p.guid, 0, OftOpState.Pushed);
} else if (PUSH_MINT_OFT_CODE == op) {
IOFTFacet.pushOftParams memory p = abi.decode(ep.params[ep.cPos], (IOFTFacet.pushOftParams));
_checkAndUpdateProcessedOps(p.guid);
require(
OFTFacetStorage.ds().resumedOps[p.guid].opsHash == _getOperationsAndParamsHash(1, ep.operations, ep.params, ep.bridgeOptions),
"OFTFacet: wrong push parameters"
);
_deleteCorrectedOpsInfo(p.guid);
emit OFTProcessed(p.guid, 0, OftOpState.Pushed);
} else {
result = ICoreFacet.ExecutionResult.Failed;
}
updatedParams = bytes("");
}
/// @inheritdoc IOFTFacet
function lzCompose(
address from,
bytes32 guid,
bytes calldata message,
address /*sender*/,
bytes calldata /*extraData*/
) external payable {
OFTFacetStorage.DS storage ds = OFTFacetStorage.ds();
require(ds.endPoint == msg.sender, "OFTFacet: sender isn't end point");
require(ds.processedOps[guid] == uint8(OftOpState.Unknown), "OFTFacet: op processed");
bytes32 sourceSender = abi.decode(message[44:77], (bytes32));
(
uint64 sourceChainid,
uint8 cPos,
string[] memory operations,
bytes[] memory params,
bytes memory bridgeOptions
) = abi.decode(message[76:], (uint64, uint8, string[], bytes[], bytes));
require(sourceSender == IAddressBook(CoreFacetStorage.ds().addressBook).routerV3(sourceChainid), "OFTFacet: wrong source sender");
IOFTFacet.OftParams memory p = abi.decode(params[cPos], (IOFTFacet.OftParams));
require(from == TypecastLib.castToAddress(p.oft), "OFTFacet: from isn't current OFT");
OFTFacetStorage.setLzComposeIsProcessed(true);
_resume(guid, cPos, operations, params, bridgeOptions);
}
/// @inheritdoc IOFTFacet
function setEndPoint(address endPoint_) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(endPoint_ != address(0), "OFTFacet: zero address");
OFTFacetStorage.ds().endPoint = endPoint_;
emit SetEndPoint(endPoint_);
}
/// @inheritdoc IOFTFacet
function endPoint() external view returns(address) {
return OFTFacetStorage.ds().endPoint;
}
/// @inheritdoc IOFTFacet
function resumedOps(bytes32 guid) external view returns (ResumedOps memory) {
return OFTFacetStorage.ds().resumedOps[guid];
}
/// @inheritdoc IOFTFacet
function processedOftOps(bytes32 guid) external view returns (uint8) {
return OFTFacetStorage.ds().processedOps[guid];
}
/// @dev The function returns the token address.
/// @param oftAdress the OFT contract address
/// @param oftType the patterns of interaction between the OFT contract and the token
/// @return tokenAdress the token address
function _getTokenAddress(address oftAdress, OftType oftType) private view returns(address tokenAdress) {
if (oftType == OftType.Token) {
tokenAdress = oftAdress;
} else if (oftType == OftType.Owner || oftType == OftType.Adapter) {
tokenAdress = IOFTAdapter(oftAdress).token();
}
}
/// @notice The function performs operations transferred with LZ transfer OFT tokens.
/// @dev If the transaction fails, its hash is saved, allowing it to be retried or OFT tokens to be withdrawn.
/// @param guid Current ID being processed.
/// @param cPos Position of the last operation performed.
/// @param operations List of operation names to execute.
/// @param params Encoded parameters for each operation.
/// @param bridgeOptions Encoded bridge-related options for further continuation.
function _resume(
bytes32 guid,
uint8 cPos,
string[] memory operations,
bytes[] memory params,
bytes memory bridgeOptions
) private {
OFTFacetStorage.DS storage ds = OFTFacetStorage.ds();
OftOpState opResult;
try ICoreFacet(address(this)).resume(guid, cPos, operations, params, bridgeOptions) {
opResult = OftOpState.Succeeded;
} catch {
opResult = OftOpState.Reverted;
ds.resumedOps[guid] = ResumedOps({
bmoOpHash: keccak256(params[cPos]),
opsHash: _getOperationsAndParamsHash(cPos, operations, params, bridgeOptions)
});
}
ds.processedOps[guid] = uint8(opResult);
emit OFTProcessed(guid, 0, opResult);
}
/// @notice Check and update OFT operation status.
/// @param guid The current ID being processed.
function _checkAndUpdateProcessedOps(bytes32 guid) private {
OFTFacetStorage.DS storage ds = OFTFacetStorage.ds();
require(ds.processedOps[guid] == uint8(OftOpState.Reverted), "OFTFacet: op not reverted");
ds.processedOps[guid] = uint8(OftOpState.Pushed);
}
/// @notice Deleting data about previously reverted operations that have been corrected.
/// @param guid The current ID being processed.
function _deleteCorrectedOpsInfo(bytes32 guid) private {
delete OFTFacetStorage.ds().resumedOps[guid];
}
/// @notice The function emit an event.
/// @param guid The current ID being processed.
/// @param chainIdTo Id of the chain to which OFT tokens were sent.
/// @param oftOpState The state of OFT operation.
function _emitEventOFTProcessed(bytes32 guid, uint64 chainIdTo, OftOpState oftOpState) private {
emit OFTProcessed(guid, chainIdTo, oftOpState);
}
/// @notice Check and update OFT operation params.
/// @param p OFT operation params.
/// @param op Current operation code.
/// @param nextOp Next operation code.
/// @param prevMaskedParams Previous masked parameters to inherit state.
function _checkAndUpdateParams(
IOFTFacet.OftParams memory p,
bytes32 op,
bytes32 nextOp,
ICoreFacet.MaskedParams memory prevMaskedParams
)
private
view
{
p.oft = IOAppCore(TypecastLib.castToAddress(p.oft)).peers(p.dstEid);
require(p.oft != 0, "OFTFacet: dist peer is zero");
(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);
}
/// @notice Returns the parameters to perform a `send` operation on the OFT.
/// @param p OFT operation params.
/// @param cPos Current operation number.
/// @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.
/// @return The SendParam for `send` operation on the OFT.
function _getSendParam(
IOFTFacet.OftParams memory p,
uint256 cPos,
string[] memory operations,
bytes[] memory params,
bytes memory bridgeOptions
)
internal
view
returns(SendParam memory)
{
bytes32 to = IAddressBook(CoreFacetStorage.ds().addressBook).routerV3(p.chainIdTo);
require(to != bytes32(0), "OFTFacet: wrong to router address");
bytes memory composeMsg = abi.encode(uint64(block.chainid), cPos, operations, params, bridgeOptions);
return SendParam({
dstEid: p.dstEid,
to: to,
amountLD: p.amountIn,
minAmountLD: p.amountIn,
extraOptions: p.extraOptions,
composeMsg: composeMsg,
oftCmd: ""
});
}
/// @notice The function returns the hash from operations not performed in the second part of the OFT token transfer operation..
/// @dev This hash is used for verification when these operations are re-pushed..
/// @param cPos Position of the last operation performed.
/// @param operations List of operation names to execute.
/// @param params Encoded parameters for each operation.
/// @param bridgeOptions Encoded bridge-related options for further continuation.
/// @return The hash from operations
function _getOperationsAndParamsHash(
uint8 cPos,
string[] memory operations,
bytes[] memory params,
bytes memory bridgeOptions
)
private
pure
returns(bytes32)
{
string[] memory pushOperations = new string[](operations.length - cPos);
for (uint256 i = 0; i < pushOperations.length; i++) {
pushOperations[i] = operations[i + cPos];
}
bytes[] memory pushParams = new bytes[](params.length - cPos);
for (uint256 i = 0; i < pushParams.length; i++) {
pushParams[i] = params[i + cPos];
}
return keccak256(abi.encode(pushOperations, pushParams, bridgeOptions));
}
/// @notice Ensures the operation can forward the required ETH amount under the current execution context.
/// @dev There are two execution contexts:
/// 1) Origin start (or any context where the shared msg.value budget is enabled):
/// - `msg.value` for the whole pipeline is tracked as a per-transaction budget in transient storage.
/// - This function consumes `expected` from that budget via {CoreFacetStorage.consumeMsgValue},
/// preventing multiple delegatecalled ops from reusing the same `msg.value`.
/// 2) Resume / tail execution (break resume):
/// - No new ETH should be supplied (`msg.value` must be zero).
/// - The router must already hold enough ETH on its balance to cover `expected`
/// (e.g., because reserved funds were withdrawn from escrow beforehand).
/// @param expected The ETH amount that will be forwarded to the downstream external call.
function _checkMsgValue(uint256 expected) private {
if (CoreFacetStorage.isOriginNetwork() || CoreFacetStorage.isMsgValueBudgetEnabled()) {
CoreFacetStorage.consumeMsgValue(expected);
} else {
require(msg.value == 0, "OFTFacet: unexpected msg.value");
require(address(this).balance >= expected, "OFTFacet: insufficient ETH");
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IMessageLibManager } from "./IMessageLibManager.sol";
import { IMessagingComposer } from "./IMessagingComposer.sol";
import { IMessagingChannel } from "./IMessagingChannel.sol";
import { IMessagingContext } from "./IMessagingContext.sol";
struct MessagingParams {
uint32 dstEid;
bytes32 receiver;
bytes message;
bytes options;
bool payInLzToken;
}
struct MessagingReceipt {
bytes32 guid;
uint64 nonce;
MessagingFee fee;
}
struct MessagingFee {
uint256 nativeFee;
uint256 lzTokenFee;
}
struct Origin {
uint32 srcEid;
bytes32 sender;
uint64 nonce;
}
interface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {
event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);
event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);
event PacketDelivered(Origin origin, address receiver);
event LzReceiveAlert(
address indexed receiver,
address indexed executor,
Origin origin,
bytes32 guid,
uint256 gas,
uint256 value,
bytes message,
bytes extraData,
bytes reason
);
event LzTokenSet(address token);
event DelegateSet(address sender, address delegate);
function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);
function send(
MessagingParams calldata _params,
address _refundAddress
) external payable returns (MessagingReceipt memory);
function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;
function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);
function initializable(Origin calldata _origin, address _receiver) external view returns (bool);
function lzReceive(
Origin calldata _origin,
address _receiver,
bytes32 _guid,
bytes calldata _message,
bytes calldata _extraData
) external payable;
// oapp can burn messages partially by calling this function with its own business logic if messages are verified in order
function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;
function setLzToken(address _lzToken) external;
function lzToken() external view returns (address);
function nativeToken() external view returns (address);
function setDelegate(address _delegate) external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
struct SetConfigParam {
uint32 eid;
uint32 configType;
bytes config;
}
interface IMessageLibManager {
struct Timeout {
address lib;
uint256 expiry;
}
event LibraryRegistered(address newLib);
event DefaultSendLibrarySet(uint32 eid, address newLib);
event DefaultReceiveLibrarySet(uint32 eid, address newLib);
event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);
event SendLibrarySet(address sender, uint32 eid, address newLib);
event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);
event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);
function registerLibrary(address _lib) external;
function isRegisteredLibrary(address _lib) external view returns (bool);
function getRegisteredLibraries() external view returns (address[] memory);
function setDefaultSendLibrary(uint32 _eid, address _newLib) external;
function defaultSendLibrary(uint32 _eid) external view returns (address);
function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;
function defaultReceiveLibrary(uint32 _eid) external view returns (address);
function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;
function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);
function isSupportedEid(uint32 _eid) external view returns (bool);
function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);
/// ------------------- OApp interfaces -------------------
function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;
function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);
function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);
function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;
function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);
function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;
function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);
function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;
function getConfig(
address _oapp,
address _lib,
uint32 _eid,
uint32 _configType
) external view returns (bytes memory config);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingChannel {
event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);
event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
function eid() external view returns (uint32);
// this is an emergency function if a message cannot be verified for some reasons
// required to provide _nextNonce to avoid race condition
function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;
function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);
function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);
function inboundPayloadHash(
address _receiver,
uint32 _srcEid,
bytes32 _sender,
uint64 _nonce
) external view returns (bytes32);
function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingComposer {
event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);
event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);
event LzComposeAlert(
address indexed from,
address indexed to,
address indexed executor,
bytes32 guid,
uint16 index,
uint256 gas,
uint256 value,
bytes message,
bytes extraData,
bytes reason
);
function composeQueue(
address _from,
address _to,
bytes32 _guid,
uint16 _index
) external view returns (bytes32 messageHash);
function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;
function lzCompose(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
bytes calldata _message,
bytes calldata _extraData
) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingContext {
function isSendingMessage() external view returns (bool);
function getSendContext() external view returns (uint32 dstEid, address sender);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
/**
* @title IOAppCore
*/
interface IOAppCore {
// Custom error messages
error OnlyPeer(uint32 eid, bytes32 sender);
error NoPeer(uint32 eid);
error InvalidEndpointCall();
error InvalidDelegate();
// Event emitted when a peer (OApp) is set for a corresponding endpoint
event PeerSet(uint32 eid, bytes32 peer);
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol contract.
* @return receiverVersion The version of the OAppReceiver.sol contract.
*/
function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);
/**
* @notice Retrieves the LayerZero endpoint associated with the OApp.
* @return iEndpoint The LayerZero endpoint as an interface.
*/
function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);
/**
* @notice Retrieves the peer (OApp) associated with a corresponding endpoint.
* @param _eid The endpoint ID.
* @return peer The peer address (OApp instance) associated with the corresponding endpoint.
*/
function peers(uint32 _eid) external view returns (bytes32 peer);
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*/
function setPeer(uint32 _eid, bytes32 _peer) external;
/**
* @notice Sets the delegate address for the OApp Core.
* @param _delegate The address of the delegate to be set.
*/
function setDelegate(address _delegate) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IOAppCore, ILayerZeroEndpointV2 } from "./interfaces/IOAppCore.sol";
/**
* @title OAppCore
* @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.
*/
abstract contract OAppCore is IOAppCore, Ownable {
// The LayerZero endpoint associated with the given OApp
ILayerZeroEndpointV2 public immutable endpoint;
// Mapping to store peers associated with corresponding endpoints
mapping(uint32 eid => bytes32 peer) public peers;
/**
* @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.
* @param _endpoint The address of the LOCAL Layer Zero endpoint.
* @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
*
* @dev The delegate typically should be set as the owner of the contract.
*/
constructor(address _endpoint, address _delegate) {
endpoint = ILayerZeroEndpointV2(_endpoint);
if (_delegate == address(0)) revert InvalidDelegate();
endpoint.setDelegate(_delegate);
}
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*
* @dev Only the owner/admin of the OApp can call this function.
* @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
* @dev Set this to bytes32(0) to remove the peer address.
* @dev Peer is a bytes32 to accommodate non-evm chains.
*/
function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {
_setPeer(_eid, _peer);
}
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*
* @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
* @dev Set this to bytes32(0) to remove the peer address.
* @dev Peer is a bytes32 to accommodate non-evm chains.
*/
function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {
peers[_eid] = _peer;
emit PeerSet(_eid, _peer);
}
/**
* @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.
* ie. the peer is set to bytes32(0).
* @param _eid The endpoint ID.
* @return peer The address of the peer associated with the specified endpoint.
*/
function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {
bytes32 peer = peers[_eid];
if (peer == bytes32(0)) revert NoPeer(_eid);
return peer;
}
/**
* @notice Sets the delegate address for the OApp.
* @param _delegate The address of the delegate to be set.
*
* @dev Only the owner/admin of the OApp can call this function.
* @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.
*/
function setDelegate(address _delegate) public onlyOwner {
endpoint.setDelegate(_delegate);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { MessagingParams, MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { OAppCore } from "./OAppCore.sol";
/**
* @title OAppSender
* @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.
*/
abstract contract OAppSender is OAppCore {
using SafeERC20 for IERC20;
// Custom error messages
error NotEnoughNative(uint256 msgValue);
error LzTokenUnavailable();
// @dev The version of the OAppSender implementation.
// @dev Version is bumped when changes are made to this contract.
uint64 internal constant SENDER_VERSION = 1;
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol contract.
* @return receiverVersion The version of the OAppReceiver.sol contract.
*
* @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.
* ie. this is a SEND only OApp.
* @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions
*/
function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {
return (SENDER_VERSION, 0);
}
/**
* @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.
* @param _dstEid The destination endpoint ID.
* @param _message The message payload.
* @param _options Additional options for the message.
* @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.
* @return fee The calculated MessagingFee for the message.
* - nativeFee: The native fee for the message.
* - lzTokenFee: The LZ token fee for the message.
*/
function _quote(
uint32 _dstEid,
bytes memory _message,
bytes memory _options,
bool _payInLzToken
) internal view virtual returns (MessagingFee memory fee) {
return
endpoint.quote(
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),
address(this)
);
}
/**
* @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.
* @param _dstEid The destination endpoint ID.
* @param _message The message payload.
* @param _options Additional options for the message.
* @param _fee The calculated LayerZero fee for the message.
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
* @param _refundAddress The address to receive any excess fee values sent to the endpoint.
* @return receipt The receipt for the sent message.
* - guid: The unique identifier for the sent message.
* - nonce: The nonce of the sent message.
* - fee: The LayerZero fee incurred for the message.
*/
function _lzSend(
uint32 _dstEid,
bytes memory _message,
bytes memory _options,
MessagingFee memory _fee,
address _refundAddress
) internal virtual returns (MessagingReceipt memory receipt) {
// @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.
uint256 messageValue = _payNative(_fee.nativeFee);
if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);
return
// solhint-disable-next-line check-send-result
endpoint.send{ value: messageValue }(
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),
_refundAddress
);
}
/**
* @dev Internal function to pay the native fee associated with the message.
* @param _nativeFee The native fee to be paid.
* @return nativeFee The amount of native currency paid.
*
* @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,
* this will need to be overridden because msg.value would contain multiple lzFees.
* @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.
* @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.
* @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.
*/
function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {
if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);
return _nativeFee;
}
/**
* @dev Internal function to pay the LZ token fee associated with the message.
* @param _lzTokenFee The LZ token fee to be paid.
*
* @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.
* @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().
*/
function _payLzToken(uint256 _lzTokenFee) internal virtual {
// @dev Cannot cache the token because it is not immutable in the endpoint.
address lzToken = endpoint.lzToken();
if (lzToken == address(0)) revert LzTokenUnavailable();
// Pay LZ token fee by sending tokens to the endpoint.
IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { MessagingReceipt, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol";
/**
* @dev Struct representing token parameters for the OFT send() operation.
*/
struct SendParam {
uint32 dstEid; // Destination endpoint ID.
bytes32 to; // Recipient address.
uint256 amountLD; // Amount to send in local decimals.
uint256 minAmountLD; // Minimum amount to send in local decimals.
bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
bytes composeMsg; // The composed message for the send() operation.
bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
}
/**
* @dev Struct representing OFT limit information.
* @dev These amounts can change dynamically and are up the specific oft implementation.
*/
struct OFTLimit {
uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.
uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.
}
/**
* @dev Struct representing OFT receipt information.
*/
struct OFTReceipt {
uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.
// @dev In non-default implementations, the amountReceivedLD COULD differ from this value.
uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.
}
/**
* @dev Struct representing OFT fee details.
* @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.
*/
struct OFTFeeDetail {
int256 feeAmountLD; // Amount of the fee in local decimals.
string description; // Description of the fee.
}
/**
* @title IOFT
* @dev Interface for the OftChain (OFT) token.
* @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.
* @dev This specific interface ID is '0x02e49c2c'.
*/
interface IOFT {
// Custom error messages
error InvalidLocalDecimals();
error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);
error AmountSDOverflowed(uint256 amountSD);
// Events
event OFTSent(
bytes32 indexed guid, // GUID of the OFT message.
uint32 dstEid, // Destination Endpoint ID.
address indexed fromAddress, // Address of the sender on the src chain.
uint256 amountSentLD, // Amount of tokens sent in local decimals.
uint256 amountReceivedLD // Amount of tokens received in local decimals.
);
event OFTReceived(
bytes32 indexed guid, // GUID of the OFT message.
uint32 srcEid, // Source Endpoint ID.
address indexed toAddress, // Address of the recipient on the dst chain.
uint256 amountReceivedLD // Amount of tokens received in local decimals.
);
/**
* @notice Retrieves interfaceID and the version of the OFT.
* @return interfaceId The interface ID.
* @return version The version.
*
* @dev interfaceId: This specific interface ID is '0x02e49c2c'.
* @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.
* @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.
* ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)
*/
function oftVersion() external view returns (bytes4 interfaceId, uint64 version);
/**
* @notice Retrieves the address of the token associated with the OFT.
* @return token The address of the ERC20 token implementation.
*/
function token() external view returns (address);
/**
* @notice Indicates whether the OFT contract requires approval of the 'token()' to send.
* @return requiresApproval Needs approval of the underlying token implementation.
*
* @dev Allows things like wallet implementers to determine integration requirements,
* without understanding the underlying token implementation.
*/
function approvalRequired() external view returns (bool);
/**
* @notice Retrieves the shared decimals of the OFT.
* @return sharedDecimals The shared decimals of the OFT.
*/
function sharedDecimals() external view returns (uint8);
/**
* @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.
* @param _sendParam The parameters for the send operation.
* @return limit The OFT limit information.
* @return oftFeeDetails The details of OFT fees.
* @return receipt The OFT receipt information.
*/
function quoteOFT(
SendParam calldata _sendParam
) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);
/**
* @notice Provides a quote for the send() operation.
* @param _sendParam The parameters for the send() operation.
* @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.
* @return fee The calculated LayerZero messaging fee from the send() operation.
*
* @dev MessagingFee: LayerZero msg fee
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
*/
function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);
/**
* @notice Executes the send() operation.
* @param _sendParam The parameters for the send operation.
* @param _fee The fee information supplied by the caller.
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
* @param _refundAddress The address to receive any excess funds from fees etc. on the src.
* @return receipt The LayerZero messaging receipt from the send() operation.
* @return oftReceipt The OFT receipt information.
*
* @dev MessagingReceipt: LayerZero msg receipt
* - guid: The unique identifier for the sent message.
* - nonce: The nonce of the sent message.
* - fee: The LayerZero fee incurred for the message.
*/
function send(
SendParam calldata _sendParam,
MessagingFee calldata _fee,
address _refundAddress
) external payable returns (MessagingReceipt memory, OFTReceipt memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (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.
*/
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].
*/
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/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// 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 ITreasury {
function withdraw(
string calldata reason,
address token,
uint256 amount,
address to
) external;
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
interface IOFTAdapter {
/// @dev Retrieves the address of the underlying ERC20 implementation.
/// @return The address of the adapted ERC-20 token.
function token() external view returns(address);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
interface IAccessControlFacet {
/// @notice Emitted when a role's admin role is changed.
/// @param role The role identifier whose admin role was changed.
/// @param previousAdminRole The previous admin role.
/// @param newAdminRole The new admin role.
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/// @notice Emitted when a role is granted to an account.
/// @param role The role identifier being granted.
/// @param account The address receiving the role.
/// @param sender The address that performed the role grant.
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/// @notice Emitted when a role is revoked from an account.
/// @param role The role identifier being revoked.
/// @param account The address losing the role.
/// @param sender The address that performed the role revocation.
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/// @notice Grants a role to an account.
/// @dev Only callable by accounts that have the admin role of the given role.
/// @param role The role identifier to grant.
/// @param account The address to grant the role to.
function grantRole(bytes32 role, address account) external;
/// @notice Revokes a role from an account.
/// @dev Only callable by accounts that have the admin role of the given role.
/// @param role The role identifier to revoke.
/// @param account The address to revoke the role from.
function revokeRole(bytes32 role, address account) external;
/// @notice Allows a caller to renounce a role for themselves.
/// @dev Caller must be the same as the account being modified.
/// @param role The role identifier to renounce.
/// @param account The address renouncing the role (must be msg.sender).
function renounceRole(bytes32 role, address account) external;
/// @notice Checks whether an account has a specific role.
/// @param role The role identifier to check.
/// @param account The address to check for the role.
/// @return True if the account has the role, false otherwise.
function hasRole(bytes32 role, address account) external view returns (bool);
/// @notice Returns the admin role that controls a specific role.
/// @param role The role identifier to query.
/// @return The admin role associated with the given role.
function getRoleAdmin(bytes32 role) external view returns (bytes32);
}// 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 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 Emergency refund of native currency failed.
/// @dev Thrown when `resumeBr` attempts to refund excess ETH (any increase in router balance
/// during the resume flow, capped by `msg.value` and, for native breaks, by the reserved
/// amount) back to `emergencyTo`, but the low-level call reverts or returns `success == false`.
/// This can happen if `emergencyTo` is a contract that rejects ETH transfers.
/// When this error is raised, the resume operation reverts and state changes made in
/// the current transaction are rolled back.
error BrRefundFailed();
/// @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:
/// - 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 Slot for the transient flag that enables msg.value budget accounting.
/// @dev Stored in EIP-1153 transient storage (tstore/tload).
/// When enabled, facets must treat ETH forwarding as spending from the shared per-tx budget
/// tracked in {MSG_VALUE_LEFT_POS} via {consumeMsgValue}.
/// Used by resume/tail execution (e.g. Break.resumeBr) to prevent multiple ops from
/// reusing the same msg.value during delegatecall-based pipelines.
bytes32 private constant IS_MSG_VALUE_BUDGET_ENABLED_POS = 0xaa921bbd9582456f18c5d5c77ea6f144a03ee74848adceca2809b778dd306508;
/// @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 setMsgValueBudgetEnabled(bool enabled) internal {
assembly ("memory-safe") {
tstore(IS_MSG_VALUE_BUDGET_ENABLED_POS, enabled)
}
}
function isMsgValueBudgetEnabled() internal view returns (bool enabled) {
assembly ("memory-safe") {
enabled := tload(IS_MSG_VALUE_BUDGET_ENABLED_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 ABI
API[{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"left","type":"uint256"}],"name":"MsgValueBudgetExceeded","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"guid","type":"bytes32"},{"indexed":true,"internalType":"uint64","name":"nextChainId","type":"uint64"},{"indexed":true,"internalType":"enum IOFTFacet.OftOpState","name":"oftOpState","type":"uint8"}],"name":"OFTProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"endPoint","type":"address"}],"name":"SetEndPoint","type":"event"},{"inputs":[],"name":"BURN_MINT_OFT_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EMERGENCY_MINT_OFT_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PUSH_MINT_OFT_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endPoint","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"cPos","type":"uint256"},{"internalType":"bytes32[]","name":"operationsCode","type":"bytes32[]"},{"internalType":"string[]","name":"operations","type":"string[]"},{"internalType":"bytes[]","name":"params","type":"bytes[]"},{"internalType":"bytes","name":"bridgeOptions","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"}],"internalType":"struct IOFTFacet.ExecuteOftParams","name":"ep","type":"tuple"}],"name":"executeOftOp","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"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"bytes32","name":"guid","type":"bytes32"},{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"address","name":"","type":"address"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"lzCompose","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"guid","type":"bytes32"}],"name":"processedOftOps","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"guid","type":"bytes32"}],"name":"resumedOps","outputs":[{"components":[{"internalType":"bytes32","name":"bmoOpHash","type":"bytes32"},{"internalType":"bytes32","name":"opsHash","type":"bytes32"}],"internalType":"struct ResumedOps","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"endPoint_","type":"address"}],"name":"setEndPoint","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6080604052348015600e575f80fd5b506132538061001c5f395ff3fe608060405260043610610084575f3560e01c806399e51e1c1161005757806399e51e1c1461019f578063cb40cb49146101e5578063d0a1026014610206578063d224a71114610219578063d8f85be01461022d575f80fd5b806341f6525f146100885780635457f56f146100ec5780635afd37201461010e5780638797ef8d14610122575b5f80fd5b348015610093575f80fd5b506100d56100a236600461248a565b5f9081527fe05cae1622d95c3f6f27e10abe6d4fad6823203161610f1838f0e2d2e438708c602052604090205460ff1690565b60405160ff90911681526020015b60405180910390f35b3480156100f7575f80fd5b50610100610250565b6040519081526020016100e3565b348015610119575f80fd5b5061010061027c565b34801561012d575f80fd5b5061018461013c36600461248a565b604080518082019091525f80825260208201525f805160206131fe8339815191525f928352602090815260409283902083518085019094528054845260010154908301525090565b604080518251815260209283015192810192909252016100e3565b3480156101aa575f80fd5b507fe05cae1622d95c3f6f27e10abe6d4fad6823203161610f1838f0e2d2e438708e546040516001600160a01b0390911681526020016100e3565b3480156101f0575f80fd5b506102046101ff3660046124b5565b610293565b005b61020461021436600461251b565b610406565b348015610224575f80fd5b506101006106d5565b61024061023b3660046128ce565b6106ec565b6040516100e39493929190612a19565b60405162504d6f60e81b60208201526023015b6040516020818303038152906040528051906020012081565b60405162214d6f60e81b6020820152602301610263565b604051632474521560e21b81525f600482018190523360248301529030906391d1485490604401602060405180830381865afa1580156102d5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102f99190612a6f565b6103435760405162461bcd60e51b81526020600482015260166024820152754f465446616365743a206d697373696e6720726f6c6560501b60448201526064015b60405180910390fd5b6001600160a01b0382166103925760405162461bcd60e51b81526020600482015260166024820152754f465446616365743a207a65726f206164647265737360501b604482015260640161033a565b7fe05cae1622d95c3f6f27e10abe6d4fad6823203161610f1838f0e2d2e438708e80546001600160a01b0384166001600160a01b0319909116811790915560408051918252517fa0bf8fa846856171122fedb4801d2e0609f4de40ea10febc40978e95e2b2fc0a9181900360200190a15050565b7fe05cae1622d95c3f6f27e10abe6d4fad6823203161610f1838f0e2d2e438708e545f805160206131fe833981519152906001600160a01b0316331461048e5760405162461bcd60e51b815260206004820181905260248201527f4f465446616365743a2073656e6465722069736e277420656e6420706f696e74604482015260640161033a565b5f87815260018201602052604090205460ff16156104e75760405162461bcd60e51b815260206004820152601660248201527513d195119858d95d0e881bdc081c1c9bd8d95cdcd95960521b604482015260640161033a565b5f6104f6604d602c888a612a8e565b810190610503919061248a565b90505f808080806105178b604c818f612a8e565b8101906105249190612ac9565b945094509450945094506105557f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8101f90565b600301546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa1580156105a5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105c99190612b74565b86146106175760405162461bcd60e51b815260206004820152601d60248201527f4f465446616365743a2077726f6e6720736f757263652073656e646572000000604482015260640161033a565b5f828560ff168151811061062d5761062d612b8b565b60200260200101518060200190518101906106489190612ce7565b80519091506001600160a01b03168f6001600160a01b0316146106ad5760405162461bcd60e51b815260206004820181905260248201527f4f465446616365743a2066726f6d2069736e27742063757272656e74204f4654604482015260640161033a565b6106b760016110e5565b6106c48e8686868661110b565b505050505050505050505050505050565b60405162424d6f60e81b6020820152602301610263565b60408051606081810183525f80835260208301819052928201839052905f7f2e445143a211ecbb689de5812742d02a96369c482aec76832fc56490cf3cc6f25c6107785760405162461bcd60e51b815260206004820152601a60248201527f526f757465723a2069736e2774204469616d6f6e642063616c6c000000000000604482015260640161033a565b7f1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d5624985c156107dc5760405162461bcd60e51b8152602060048201526012602482015271526f757465723a207265656e7472616e637960701b604482015260640161033a565b6107e66001611257565b5f8560200151865f01518151811061080057610800612b8b565b602002602001015190505f8660200151875f015160016108209190612d2c565b8151811061083057610830612b8b565b6020026020010151905060019250816040516020016108589062424d6f60e81b815260030190565b6040516020818303038152906040528051906020012003610e01575f8760600151885f01518151811061088d5761088d612b8b565b60200260200101518060200190518101906108a89190612ce7565b90505f806108b6835f015190565b90507f30273a0c1aadada7e550a5e2c5e4d1944f67a058b948828ccf2622923a9aae4b5c8061094457508951600111801590610944575060405162504d6f60e81b6020820152602301604051602081830303815290604052805190602001208a6020015160018c5f015161092a9190612d3f565b8151811061093a5761093a612b8b565b6020026020010151145b156109ed5760608301515f036109745761096e83606001518461014001518560800151888861127d565b60608401525b825161098590846101200151611988565b60608401519092506001600160a01b031630146109b5576109b5826109ab856060015190565b8560200151611a47565b6040518060600160405280846020015181526020018460600151815260200184610140015181525096506109e85f6110e5565b610df9565b82516109fe90846101000151611988565b915082608001519850610a178386868d60a00151611aaf565b60408301516001600160a01b03163014610a4557610a4582610a3a856040015190565b308660200151611bca565b60028361010001516002811115610a5e57610a5e6129e9565b03610adc57602083015160405163095ea7b360e01b81526001600160a01b03838116600483015260248201929092529083169063095ea7b3906044016020604051808303815f875af1158015610ab6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ada9190612a6f565b505b82604051602001610aed9190612d5f565b60408051601f1981840301815291905260608b01518b5181518110610b1457610b14612b8b565b60200260200101819052505f610b3c848c5f01518d604001518e606001518f60800151611c08565b90505f826001600160a01b0316633b6f743b835f6040518363ffffffff1660e01b8152600401610b6d929190612e99565b6040805180830381865afa158015610b87573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bab9190612eea565b9050805f01518560e001511015610c105760405162461bcd60e51b815260206004820152602360248201527f4f465446616365743a206e617469766520666565206c696d697420657863656560448201526219195960ea1b606482015260840161033a565b8051471015610d2f577f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8102254604080516361d027b360e01b815290516001600160a01b03909216916361d027b3916004808201926020929091908290030181865afa158015610c80573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ca49190612f04565b6001600160a01b03166362a338115f47845f0151610cc29190612d3f565b6040516001600160e01b031960e085901b168152608060048201525f60848201526001600160a01b039092166024830152604482015230606482015260a4015f604051808303815f87803b158015610d18575f80fd5b505af1158015610d2a573d5f803e3d5ffd5b505050505b8051610d3a90611dbf565b5f836001600160a01b031663c7c7f5b3835f01518585610d5c8b610140015190565b6040518563ffffffff1660e01b8152600401610d7a93929190612f1f565b60c06040518083038185885af1158015610d96573d5f803e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190610dbb9190612f5d565b5090505f81516040516001600160401b038f1691907fbecb22a4bb8fd1669a29d7a8b7fd17d0bd54b6635a9ce4e6aaf0b66de5d72864905f90a45050505b5050506110c2565b60405162214d6f60e81b602082015282906023016040516020818303038152906040528051906020012003610fa0575f8760600151885f015181518110610e4a57610e4a612b8b565b6020026020010151806020019051810190610e659190612fc6565b9050610e73815f0151611eb9565b602080820151604051610e869201612d5f565b60405160208183030381529060405280519060200120610eb05f805160206131fe83398151915290565b82515f908152602091909152604090205414610f1a5760405162461bcd60e51b8152602060048201526024808201527f4f465446616365743a2077726f6e6720656d657267656e637920706172616d656044820152637465727360e01b606482015260840161033a565b80515f9081525f805160206131fe83398151915260205260408120818155600101556020810151805161012090910151610f6b91610f5791611988565b602083810151610140810151910151611a47565b600381516040515f91907fbecb22a4bb8fd1669a29d7a8b7fd17d0bd54b6635a9ce4e6aaf0b66de5d72864908390a4506110c2565b60405162504d6f60e81b6020820152829060230160405160208183030381529060405280519060200120036110be575f8760600151885f015181518110610fe957610fe9612b8b565b60200260200101518060200190518101906110049190613037565b9050611012815f0151611eb9565b61102b600189604001518a606001518b60800151611f62565b81515f9081525f805160206131fe8339815191526020526040902060010154146110975760405162461bcd60e51b815260206004820152601f60248201527f4f465446616365743a2077726f6e67207075736820706172616d657465727300604482015260640161033a565b80515f9081525f805160206131fe8339815191526020526040812081815560010155610f6b565b5f92505b60405180602001604052805f815250945050506110de5f611257565b9193509193565b807f30273a0c1aadada7e550a5e2c5e4d1944f67a058b948828ccf2622923a9aae4b5d50565b604051634dc9fb3560e01b81525f805160206131fe833981519152905f903090634dc9fb3590611147908a908a908a908a908a90600401613119565b5f604051808303815f87803b15801561115e575f80fd5b505af192505050801561116f575060015b6111d957600290506040518060400160405280858860ff168151811061119757611197612b8b565b60200260200101518051906020012081526020016111b788888888611f62565b90525f88815260208481526040909120825181559101516001909101556111dd565b5060015b8060038111156111ef576111ef6129e9565b5f8881526001840160205260409020805460ff191660ff92909216919091179055806003811115611222576112226129e9565b6040515f9089907fbecb22a4bb8fd1669a29d7a8b7fd17d0bd54b6635a9ce4e6aaf0b66de5d72864908390a450505050505050565b807f1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d5624985d50565b5f7f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8101f821580156112ac57508615155b806112bf575082158015906112bf575086155b6112fe5760405162461bcd60e51b815260206004820152601060248201526f526f757465723a2077726f6e6720746f60801b604482015260640161033a565b7f4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f45c1580156113525750604051605760f81b6020820152602101604051602081830303815290604052805190602001208414155b8015611384575060405161557760f01b6020820152602201604051602081830303815290604052805190602001208414155b156113f55733866001600160a01b0316146113f55760405162461bcd60e51b815260206004820152602b60248201527f526f757465723a20656d657267656e6379546f206973206e6f7420657175616c60448201526a103a34329039b2b73232b960a91b606482015260840161033a565b826114025786915061197e565b604051614c4d60f01b60208201526022016040516020818303038152906040528051906020012083036114ac57600381015460405163535cfbb760e11b81526001600160401b03871660048201526001600160a01b039091169063a6b9f76e906024015b602060405180830381865afa158015611481573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114a59190612b74565b915061197e565b60405161425560f01b602082015260220160405160208183030381529060405280519060200120831480611505575060405161424d60f01b60208201526022016040516020818303038152906040528051906020012083145b156115455760038101546040516374a748ff60e11b81526001600160401b03871660048201526001600160a01b039091169063e94e91fe90602401611466565b604051605760f81b6020820152839060210160405160208183030381529060405280519060200120148061159f575060405161557760f01b6020820152839060220160405160208183030381529060405280519060200120145b156115df5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401611466565b604051604160f81b6020820152602101604051602081830303815290604052805190602001208314806116365750604051602960f91b60208201526021016040516020818303038152906040528051906020012083145b806116655750604051605360f81b60208201526021016040516020818303038152906040528051906020012083145b156116a55760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401611466565b60405162424d6f60e81b602082015260230160405160208183030381529060405280519060200120831480611700575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012083145b80611731575060405162504d6f60e81b60208201526023016040516020818303038152906040528051906020012083145b156117715760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401611466565b60405161525360f01b60208201526022016040516020818303038152906040528051906020012083036117d95760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401611466565b60405161313960f11b60208201526022016040516020818303038152906040528051906020012083036118415760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401611466565b604051632126a0a760e11b60208201526024016040516020818303038152906040528051906020012083148061189f575060405164424155544f60d81b60208201526025016040516020818303038152906040528051906020012083145b156118df5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401611466565b7f33c4137323016cc703f06d36ae2c7f43f1a70fa030334bd76934bba665c80d6a830361197e5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa158015611957573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061197b9190612b74565b91505b5095945050505050565b5f8082600281111561199c5761199c6129e9565b036119a8575081611a41565b60018260028111156119bc576119bc6129e9565b14806119d9575060028260028111156119d7576119d76129e9565b145b15611a4157826001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a1a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a3e9190612f04565b90505b92915050565b6040516001600160a01b038316602482015260448101829052611aaa90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612105565b505050565b835160a085015160405163bb0b6a5360e01b815263ffffffff90911660048201526001600160a01b039091169063bb0b6a5390602401602060405180830381865afa158015611b00573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b249190612b74565b8085525f03611b755760405162461bcd60e51b815260206004820152601b60248201527f4f465446616365743a20646973742070656572206973207a65726f0000000000604482015260640161033a565b611b8e84602001518560400151866101400151846121d8565b61014087018190526040870191909152602086019190915260608501516080860151611bbc9290868661127d565b606090940193909352505050565b6040516001600160a01b0380851660248301528316604482015260648101829052611c029085906323b872dd60e01b90608401611a73565b50505050565b611c4d6040518060e001604052805f63ffffffff1681526020015f80191681526020015f81526020015f81526020016060815260200160608152602001606081525090565b5f7f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8101f6003015460808801516040516397c2cd9160e01b81526001600160401b0390911660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa158015611cc5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ce99190612b74565b905080611d425760405162461bcd60e51b815260206004820152602160248201527f4f465446616365743a2077726f6e6720746f20726f75746572206164647265736044820152607360f81b606482015260840161033a565b5f4687878787604051602001611d5c95949392919061316c565b60408051808303601f1901815260e08301825260a08b81015163ffffffff168452602080850196909652858c0180518585015251606085015260c09b8c01516080850152830152805193840190525f835297880191909152509495945050505050565b7fb13bd13498ce9409b85a4287d16ff0fcdaca732822a47a97d2bdada3325aa4515c80611e0a57507faa921bbd9582456f18c5d5c77ea6f144a03ee74848adceca2809b778dd3065085c5b15611e1b57611e18816122f6565b50565b3415611e695760405162461bcd60e51b815260206004820152601e60248201527f4f465446616365743a20756e6578706563746564206d73672e76616c75650000604482015260640161033a565b80471015611e185760405162461bcd60e51b815260206004820152601a60248201527f4f465446616365743a20696e73756666696369656e7420455448000000000000604482015260640161033a565b5f8181527fe05cae1622d95c3f6f27e10abe6d4fad6823203161610f1838f0e2d2e438708c60205260409020545f805160206131fe8339815191529060ff16600214611f475760405162461bcd60e51b815260206004820152601960248201527f4f465446616365743a206f70206e6f7420726576657274656400000000000000604482015260640161033a565b5f91825260010160205260409020805460ff19166003179055565b5f808560ff168551611f749190612d3f565b6001600160401b03811115611f8b57611f8b6125b4565b604051908082528060200260200182016040528015611fbe57816020015b6060815260200190600190039081611fa95790505b5090505f5b81518110156120175785611fda60ff891683612d2c565b81518110611fea57611fea612b8b565b602002602001015182828151811061200457612004612b8b565b6020908102919091010152600101611fc3565b505f8660ff1685516120299190612d3f565b6001600160401b03811115612040576120406125b4565b60405190808252806020026020018201604052801561207357816020015b606081526020019060019003908161205e5790505b5090505f5b81518110156120cc578561208f60ff8a1683612d2c565b8151811061209f5761209f612b8b565b60200260200101518282815181106120b9576120b9612b8b565b6020908102919091010152600101612078565b508181856040516020016120e293929190613193565b60405160208183030381529060405280519060200120925050505b949350505050565b5f612159826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661236d9092919063ffffffff16565b905080515f14806121795750808060200190518101906121799190612a6f565b611aaa5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161033a565b5f805f5f1987146121e957866121ec565b83515b9250851561224b5733866001600160a01b0316146122435760405162461bcd60e51b81526020600482015260146024820152732937baba32b91d103bb937b7339039b2b73232b960611b604482015260640161033a565b859150612253565b836020015191505b7f4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f45c15801561228157508415155b156122e55733856001600160a01b0316146122de5760405162461bcd60e51b815260206004820152601960248201527f526f757465723a2077726f6e6720656d657267656e6379546f00000000000000604482015260640161033a565b50836122ec565b5060408301515b9450945094915050565b7f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5c808211156123435760405163161531e360e21b8152600481018390526024810182905260440161033a565b819003807f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5d5050565b60606120fd84845f85855f80866001600160a01b0316858760405161239291906131d5565b5f6040518083038185875af1925050503d805f81146123cc576040519150601f19603f3d011682016040523d82523d5f602084013e6123d1565b606091505b50915091506123e2878383876123ed565b979650505050505050565b6060831561245b5782515f03612454576001600160a01b0385163b6124545760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161033a565b50816120fd565b6120fd83838151156124705781518083602001fd5b8060405162461bcd60e51b815260040161033a91906131eb565b5f6020828403121561249a575f80fd5b5035919050565b6001600160a01b0381168114611e18575f80fd5b5f602082840312156124c5575f80fd5b81356124d0816124a1565b9392505050565b5f8083601f8401126124e7575f80fd5b5081356001600160401b038111156124fd575f80fd5b602083019150836020828501011115612514575f80fd5b9250929050565b5f805f805f805f60a0888a031215612531575f80fd5b873561253c816124a1565b96506020880135955060408801356001600160401b038082111561255e575f80fd5b61256a8b838c016124d7565b909750955060608a0135915061257f826124a1565b90935060808901359080821115612594575f80fd5b506125a18a828b016124d7565b989b979a50959850939692959293505050565b634e487b7160e01b5f52604160045260245ffd5b604051606081016001600160401b03811182821017156125ea576125ea6125b4565b60405290565b60405160c081016001600160401b03811182821017156125ea576125ea6125b4565b60405161016081016001600160401b03811182821017156125ea576125ea6125b4565b604080519081016001600160401b03811182821017156125ea576125ea6125b4565b604051601f8201601f191681016001600160401b038111828210171561267f5761267f6125b4565b604052919050565b5f6001600160401b0382111561269f5761269f6125b4565b5060051b60200190565b5f82601f8301126126b8575f80fd5b813560206126cd6126c883612687565b612657565b8083825260208201915060208460051b8701019350868411156126ee575f80fd5b602086015b8481101561270a57803583529183019183016126f3565b509695505050505050565b5f6001600160401b0382111561272d5761272d6125b4565b50601f01601f191660200190565b5f6127486126c884612715565b905082815283838301111561275b575f80fd5b828260208301375f602084830101529392505050565b5f82601f830112612780575f80fd5b813560206127906126c883612687565b82815260059290921b840181019181810190868411156127ae575f80fd5b8286015b8481101561270a5780356001600160401b038111156127cf575f80fd5b8701603f810189136127df575f80fd5b6127f089868301356040840161273b565b8452509183019183016127b2565b5f82601f83011261280d575f80fd5b611a3e8383356020850161273b565b5f82601f83011261282b575f80fd5b8135602061283b6126c883612687565b82815260059290921b84018101918181019086841115612859575f80fd5b8286015b8481101561270a5780356001600160401b0381111561287a575f80fd5b6128888986838b01016127fe565b84525091830191830161285d565b5f606082840312156128a6575f80fd5b6128ae6125c8565b905081358152602082013560208201526040820135604082015292915050565b5f602082840312156128de575f80fd5b81356001600160401b03808211156128f4575f80fd5b908301906101008286031215612908575f80fd5b6129106125f0565b82358152602083013582811115612925575f80fd5b612931878286016126a9565b602083015250604083013582811115612948575f80fd5b61295487828601612771565b60408301525060608301358281111561296b575f80fd5b6129778782860161281c565b60608301525060808301358281111561298e575f80fd5b61299a878286016127fe565b6080830152506129ad8660a08501612896565b60a082015295945050505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b634e487b7160e01b5f52602160045260245ffd5b60038110611e1857634e487b7160e01b5f52602160045260245ffd5b6001600160401b038516815260c060208201525f612a3a60c08301866129bb565b9050835160408301526020840151606083015260408401516080830152612a60836129fd565b8260a083015295945050505050565b5f60208284031215612a7f575f80fd5b815180151581146124d0575f80fd5b5f8085851115612a9c575f80fd5b83861115612aa8575f80fd5b5050820193919092039150565b6001600160401b0381168114611e18575f80fd5b5f805f805f60a08688031215612add575f80fd5b8535612ae881612ab5565b9450602086013560ff81168114612afd575f80fd5b935060408601356001600160401b0380821115612b18575f80fd5b612b2489838a01612771565b94506060880135915080821115612b39575f80fd5b612b4589838a0161281c565b93506080880135915080821115612b5a575f80fd5b50612b67888289016127fe565b9150509295509295909350565b5f60208284031215612b84575f80fd5b5051919050565b634e487b7160e01b5f52603260045260245ffd5b8051612baa81612ab5565b919050565b805163ffffffff81168114612baa575f80fd5b5f82601f830112612bd1575f80fd5b8151612bdf6126c882612715565b818152846020838601011115612bf3575f80fd5b8160208501602083015e5f918101602001919091529392505050565b805160038110612baa575f80fd5b5f6101608284031215612c2e575f80fd5b612c36612612565b905081518152602082015160208201526040820151604082015260608201516060820152612c6660808301612b9f565b6080820152612c7760a08301612baf565b60a082015260c08201516001600160401b03811115612c94575f80fd5b612ca084828501612bc2565b60c08301525060e082015160e0820152610100612cbe818401612c0f565b90820152610120612cd0838201612c0f565b818301525061014080830151818301525092915050565b5f60208284031215612cf7575f80fd5b81516001600160401b03811115612d0c575f80fd5b6120fd84828501612c1d565b634e487b7160e01b5f52601160045260245ffd5b80820180821115611a4157611a41612d18565b81810381811115611a4157611a41612d18565b612d5b816129fd565b9052565b60208152815160208201526020820151604082015260408201516060820152606082015160808201525f6080830151612da360a08401826001600160401b03169052565b5060a083015163ffffffff811660c08401525060c08301516101608060e0850152612dd26101808501836129bb565b60e086015161010086810191909152860151909250610120612df681870183612d52565b8601519050610140612e0a86820183612d52565b959095015193019290925250919050565b63ffffffff81511682526020810151602083015260408101516040830152606081015160608301525f608082015160e06080850152612e5d60e08501826129bb565b905060a083015184820360a0860152612e7682826129bb565b91505060c083015184820360c0860152612e9082826129bb565b95945050505050565b604081525f612eab6040830185612e1b565b905082151560208301529392505050565b5f60408284031215612ecc575f80fd5b612ed4612635565b9050815181526020820151602082015292915050565b5f60408284031215612efa575f80fd5b611a3e8383612ebc565b5f60208284031215612f14575f80fd5b81516124d0816124a1565b608081525f612f316080830186612e1b565b8451602084810191909152909401516040830152506001600160a01b0391909116606090910152919050565b5f8082840360c0811215612f6f575f80fd5b6080811215612f7c575f80fd5b50612f856125c8565b835181526020840151612f9781612ab5565b6020820152612fa98560408601612ebc565b60408201529150612fbd8460808501612ebc565b90509250929050565b5f60208284031215612fd6575f80fd5b81516001600160401b0380821115612fec575f80fd5b9083019060408286031215612fff575f80fd5b613007612635565b8251815260208301518281111561301c575f80fd5b61302887828601612c1d565b60208301525095945050505050565b5f60208284031215613047575f80fd5b604051602081018181106001600160401b0382111715613069576130696125b4565b6040529151825250919050565b5f8282518085526020808601955060208260051b840101602086015f5b848110156130c157601f198684030189526130af8383516129bb565b98840198925090830190600101613093565b5090979650505050505050565b5f8282518085526020808601955060208260051b840101602086015f5b848110156130c157601f198684030189526131078383516129bb565b988401989250908301906001016130eb565b85815260ff8516602082015260a060408201525f61313a60a0830186613076565b828103606084015261314c81866130ce565b9050828103608084015261316081856129bb565b98975050505050505050565b6001600160401b038616815284602082015260a060408201525f61313a60a0830186613076565b606081525f6131a56060830186613076565b82810360208401526131b781866130ce565b905082810360408401526131cb81856129bb565b9695505050505050565b5f82518060208501845e5f920191825250919050565b602081525f611a3e60208301846129bb56fee05cae1622d95c3f6f27e10abe6d4fad6823203161610f1838f0e2d2e438708ba2646970667358221220effa3504b77763ad25ea1609e1bb2330249f4f4c6e066bd81936ab65dada033c64736f6c63430008190033
Deployed Bytecode
0x608060405260043610610084575f3560e01c806399e51e1c1161005757806399e51e1c1461019f578063cb40cb49146101e5578063d0a1026014610206578063d224a71114610219578063d8f85be01461022d575f80fd5b806341f6525f146100885780635457f56f146100ec5780635afd37201461010e5780638797ef8d14610122575b5f80fd5b348015610093575f80fd5b506100d56100a236600461248a565b5f9081527fe05cae1622d95c3f6f27e10abe6d4fad6823203161610f1838f0e2d2e438708c602052604090205460ff1690565b60405160ff90911681526020015b60405180910390f35b3480156100f7575f80fd5b50610100610250565b6040519081526020016100e3565b348015610119575f80fd5b5061010061027c565b34801561012d575f80fd5b5061018461013c36600461248a565b604080518082019091525f80825260208201525f805160206131fe8339815191525f928352602090815260409283902083518085019094528054845260010154908301525090565b604080518251815260209283015192810192909252016100e3565b3480156101aa575f80fd5b507fe05cae1622d95c3f6f27e10abe6d4fad6823203161610f1838f0e2d2e438708e546040516001600160a01b0390911681526020016100e3565b3480156101f0575f80fd5b506102046101ff3660046124b5565b610293565b005b61020461021436600461251b565b610406565b348015610224575f80fd5b506101006106d5565b61024061023b3660046128ce565b6106ec565b6040516100e39493929190612a19565b60405162504d6f60e81b60208201526023015b6040516020818303038152906040528051906020012081565b60405162214d6f60e81b6020820152602301610263565b604051632474521560e21b81525f600482018190523360248301529030906391d1485490604401602060405180830381865afa1580156102d5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102f99190612a6f565b6103435760405162461bcd60e51b81526020600482015260166024820152754f465446616365743a206d697373696e6720726f6c6560501b60448201526064015b60405180910390fd5b6001600160a01b0382166103925760405162461bcd60e51b81526020600482015260166024820152754f465446616365743a207a65726f206164647265737360501b604482015260640161033a565b7fe05cae1622d95c3f6f27e10abe6d4fad6823203161610f1838f0e2d2e438708e80546001600160a01b0384166001600160a01b0319909116811790915560408051918252517fa0bf8fa846856171122fedb4801d2e0609f4de40ea10febc40978e95e2b2fc0a9181900360200190a15050565b7fe05cae1622d95c3f6f27e10abe6d4fad6823203161610f1838f0e2d2e438708e545f805160206131fe833981519152906001600160a01b0316331461048e5760405162461bcd60e51b815260206004820181905260248201527f4f465446616365743a2073656e6465722069736e277420656e6420706f696e74604482015260640161033a565b5f87815260018201602052604090205460ff16156104e75760405162461bcd60e51b815260206004820152601660248201527513d195119858d95d0e881bdc081c1c9bd8d95cdcd95960521b604482015260640161033a565b5f6104f6604d602c888a612a8e565b810190610503919061248a565b90505f808080806105178b604c818f612a8e565b8101906105249190612ac9565b945094509450945094506105557f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8101f90565b600301546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa1580156105a5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105c99190612b74565b86146106175760405162461bcd60e51b815260206004820152601d60248201527f4f465446616365743a2077726f6e6720736f757263652073656e646572000000604482015260640161033a565b5f828560ff168151811061062d5761062d612b8b565b60200260200101518060200190518101906106489190612ce7565b80519091506001600160a01b03168f6001600160a01b0316146106ad5760405162461bcd60e51b815260206004820181905260248201527f4f465446616365743a2066726f6d2069736e27742063757272656e74204f4654604482015260640161033a565b6106b760016110e5565b6106c48e8686868661110b565b505050505050505050505050505050565b60405162424d6f60e81b6020820152602301610263565b60408051606081810183525f80835260208301819052928201839052905f7f2e445143a211ecbb689de5812742d02a96369c482aec76832fc56490cf3cc6f25c6107785760405162461bcd60e51b815260206004820152601a60248201527f526f757465723a2069736e2774204469616d6f6e642063616c6c000000000000604482015260640161033a565b7f1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d5624985c156107dc5760405162461bcd60e51b8152602060048201526012602482015271526f757465723a207265656e7472616e637960701b604482015260640161033a565b6107e66001611257565b5f8560200151865f01518151811061080057610800612b8b565b602002602001015190505f8660200151875f015160016108209190612d2c565b8151811061083057610830612b8b565b6020026020010151905060019250816040516020016108589062424d6f60e81b815260030190565b6040516020818303038152906040528051906020012003610e01575f8760600151885f01518151811061088d5761088d612b8b565b60200260200101518060200190518101906108a89190612ce7565b90505f806108b6835f015190565b90507f30273a0c1aadada7e550a5e2c5e4d1944f67a058b948828ccf2622923a9aae4b5c8061094457508951600111801590610944575060405162504d6f60e81b6020820152602301604051602081830303815290604052805190602001208a6020015160018c5f015161092a9190612d3f565b8151811061093a5761093a612b8b565b6020026020010151145b156109ed5760608301515f036109745761096e83606001518461014001518560800151888861127d565b60608401525b825161098590846101200151611988565b60608401519092506001600160a01b031630146109b5576109b5826109ab856060015190565b8560200151611a47565b6040518060600160405280846020015181526020018460600151815260200184610140015181525096506109e85f6110e5565b610df9565b82516109fe90846101000151611988565b915082608001519850610a178386868d60a00151611aaf565b60408301516001600160a01b03163014610a4557610a4582610a3a856040015190565b308660200151611bca565b60028361010001516002811115610a5e57610a5e6129e9565b03610adc57602083015160405163095ea7b360e01b81526001600160a01b03838116600483015260248201929092529083169063095ea7b3906044016020604051808303815f875af1158015610ab6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ada9190612a6f565b505b82604051602001610aed9190612d5f565b60408051601f1981840301815291905260608b01518b5181518110610b1457610b14612b8b565b60200260200101819052505f610b3c848c5f01518d604001518e606001518f60800151611c08565b90505f826001600160a01b0316633b6f743b835f6040518363ffffffff1660e01b8152600401610b6d929190612e99565b6040805180830381865afa158015610b87573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bab9190612eea565b9050805f01518560e001511015610c105760405162461bcd60e51b815260206004820152602360248201527f4f465446616365743a206e617469766520666565206c696d697420657863656560448201526219195960ea1b606482015260840161033a565b8051471015610d2f577f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8102254604080516361d027b360e01b815290516001600160a01b03909216916361d027b3916004808201926020929091908290030181865afa158015610c80573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ca49190612f04565b6001600160a01b03166362a338115f47845f0151610cc29190612d3f565b6040516001600160e01b031960e085901b168152608060048201525f60848201526001600160a01b039092166024830152604482015230606482015260a4015f604051808303815f87803b158015610d18575f80fd5b505af1158015610d2a573d5f803e3d5ffd5b505050505b8051610d3a90611dbf565b5f836001600160a01b031663c7c7f5b3835f01518585610d5c8b610140015190565b6040518563ffffffff1660e01b8152600401610d7a93929190612f1f565b60c06040518083038185885af1158015610d96573d5f803e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190610dbb9190612f5d565b5090505f81516040516001600160401b038f1691907fbecb22a4bb8fd1669a29d7a8b7fd17d0bd54b6635a9ce4e6aaf0b66de5d72864905f90a45050505b5050506110c2565b60405162214d6f60e81b602082015282906023016040516020818303038152906040528051906020012003610fa0575f8760600151885f015181518110610e4a57610e4a612b8b565b6020026020010151806020019051810190610e659190612fc6565b9050610e73815f0151611eb9565b602080820151604051610e869201612d5f565b60405160208183030381529060405280519060200120610eb05f805160206131fe83398151915290565b82515f908152602091909152604090205414610f1a5760405162461bcd60e51b8152602060048201526024808201527f4f465446616365743a2077726f6e6720656d657267656e637920706172616d656044820152637465727360e01b606482015260840161033a565b80515f9081525f805160206131fe83398151915260205260408120818155600101556020810151805161012090910151610f6b91610f5791611988565b602083810151610140810151910151611a47565b600381516040515f91907fbecb22a4bb8fd1669a29d7a8b7fd17d0bd54b6635a9ce4e6aaf0b66de5d72864908390a4506110c2565b60405162504d6f60e81b6020820152829060230160405160208183030381529060405280519060200120036110be575f8760600151885f015181518110610fe957610fe9612b8b565b60200260200101518060200190518101906110049190613037565b9050611012815f0151611eb9565b61102b600189604001518a606001518b60800151611f62565b81515f9081525f805160206131fe8339815191526020526040902060010154146110975760405162461bcd60e51b815260206004820152601f60248201527f4f465446616365743a2077726f6e67207075736820706172616d657465727300604482015260640161033a565b80515f9081525f805160206131fe8339815191526020526040812081815560010155610f6b565b5f92505b60405180602001604052805f815250945050506110de5f611257565b9193509193565b807f30273a0c1aadada7e550a5e2c5e4d1944f67a058b948828ccf2622923a9aae4b5d50565b604051634dc9fb3560e01b81525f805160206131fe833981519152905f903090634dc9fb3590611147908a908a908a908a908a90600401613119565b5f604051808303815f87803b15801561115e575f80fd5b505af192505050801561116f575060015b6111d957600290506040518060400160405280858860ff168151811061119757611197612b8b565b60200260200101518051906020012081526020016111b788888888611f62565b90525f88815260208481526040909120825181559101516001909101556111dd565b5060015b8060038111156111ef576111ef6129e9565b5f8881526001840160205260409020805460ff191660ff92909216919091179055806003811115611222576112226129e9565b6040515f9089907fbecb22a4bb8fd1669a29d7a8b7fd17d0bd54b6635a9ce4e6aaf0b66de5d72864908390a450505050505050565b807f1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d5624985d50565b5f7f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8101f821580156112ac57508615155b806112bf575082158015906112bf575086155b6112fe5760405162461bcd60e51b815260206004820152601060248201526f526f757465723a2077726f6e6720746f60801b604482015260640161033a565b7f4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f45c1580156113525750604051605760f81b6020820152602101604051602081830303815290604052805190602001208414155b8015611384575060405161557760f01b6020820152602201604051602081830303815290604052805190602001208414155b156113f55733866001600160a01b0316146113f55760405162461bcd60e51b815260206004820152602b60248201527f526f757465723a20656d657267656e6379546f206973206e6f7420657175616c60448201526a103a34329039b2b73232b960a91b606482015260840161033a565b826114025786915061197e565b604051614c4d60f01b60208201526022016040516020818303038152906040528051906020012083036114ac57600381015460405163535cfbb760e11b81526001600160401b03871660048201526001600160a01b039091169063a6b9f76e906024015b602060405180830381865afa158015611481573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114a59190612b74565b915061197e565b60405161425560f01b602082015260220160405160208183030381529060405280519060200120831480611505575060405161424d60f01b60208201526022016040516020818303038152906040528051906020012083145b156115455760038101546040516374a748ff60e11b81526001600160401b03871660048201526001600160a01b039091169063e94e91fe90602401611466565b604051605760f81b6020820152839060210160405160208183030381529060405280519060200120148061159f575060405161557760f01b6020820152839060220160405160208183030381529060405280519060200120145b156115df5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401611466565b604051604160f81b6020820152602101604051602081830303815290604052805190602001208314806116365750604051602960f91b60208201526021016040516020818303038152906040528051906020012083145b806116655750604051605360f81b60208201526021016040516020818303038152906040528051906020012083145b156116a55760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401611466565b60405162424d6f60e81b602082015260230160405160208183030381529060405280519060200120831480611700575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012083145b80611731575060405162504d6f60e81b60208201526023016040516020818303038152906040528051906020012083145b156117715760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401611466565b60405161525360f01b60208201526022016040516020818303038152906040528051906020012083036117d95760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401611466565b60405161313960f11b60208201526022016040516020818303038152906040528051906020012083036118415760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401611466565b604051632126a0a760e11b60208201526024016040516020818303038152906040528051906020012083148061189f575060405164424155544f60d81b60208201526025016040516020818303038152906040528051906020012083145b156118df5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401611466565b7f33c4137323016cc703f06d36ae2c7f43f1a70fa030334bd76934bba665c80d6a830361197e5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa158015611957573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061197b9190612b74565b91505b5095945050505050565b5f8082600281111561199c5761199c6129e9565b036119a8575081611a41565b60018260028111156119bc576119bc6129e9565b14806119d9575060028260028111156119d7576119d76129e9565b145b15611a4157826001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a1a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a3e9190612f04565b90505b92915050565b6040516001600160a01b038316602482015260448101829052611aaa90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612105565b505050565b835160a085015160405163bb0b6a5360e01b815263ffffffff90911660048201526001600160a01b039091169063bb0b6a5390602401602060405180830381865afa158015611b00573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b249190612b74565b8085525f03611b755760405162461bcd60e51b815260206004820152601b60248201527f4f465446616365743a20646973742070656572206973207a65726f0000000000604482015260640161033a565b611b8e84602001518560400151866101400151846121d8565b61014087018190526040870191909152602086019190915260608501516080860151611bbc9290868661127d565b606090940193909352505050565b6040516001600160a01b0380851660248301528316604482015260648101829052611c029085906323b872dd60e01b90608401611a73565b50505050565b611c4d6040518060e001604052805f63ffffffff1681526020015f80191681526020015f81526020015f81526020016060815260200160608152602001606081525090565b5f7f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8101f6003015460808801516040516397c2cd9160e01b81526001600160401b0390911660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa158015611cc5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ce99190612b74565b905080611d425760405162461bcd60e51b815260206004820152602160248201527f4f465446616365743a2077726f6e6720746f20726f75746572206164647265736044820152607360f81b606482015260840161033a565b5f4687878787604051602001611d5c95949392919061316c565b60408051808303601f1901815260e08301825260a08b81015163ffffffff168452602080850196909652858c0180518585015251606085015260c09b8c01516080850152830152805193840190525f835297880191909152509495945050505050565b7fb13bd13498ce9409b85a4287d16ff0fcdaca732822a47a97d2bdada3325aa4515c80611e0a57507faa921bbd9582456f18c5d5c77ea6f144a03ee74848adceca2809b778dd3065085c5b15611e1b57611e18816122f6565b50565b3415611e695760405162461bcd60e51b815260206004820152601e60248201527f4f465446616365743a20756e6578706563746564206d73672e76616c75650000604482015260640161033a565b80471015611e185760405162461bcd60e51b815260206004820152601a60248201527f4f465446616365743a20696e73756666696369656e7420455448000000000000604482015260640161033a565b5f8181527fe05cae1622d95c3f6f27e10abe6d4fad6823203161610f1838f0e2d2e438708c60205260409020545f805160206131fe8339815191529060ff16600214611f475760405162461bcd60e51b815260206004820152601960248201527f4f465446616365743a206f70206e6f7420726576657274656400000000000000604482015260640161033a565b5f91825260010160205260409020805460ff19166003179055565b5f808560ff168551611f749190612d3f565b6001600160401b03811115611f8b57611f8b6125b4565b604051908082528060200260200182016040528015611fbe57816020015b6060815260200190600190039081611fa95790505b5090505f5b81518110156120175785611fda60ff891683612d2c565b81518110611fea57611fea612b8b565b602002602001015182828151811061200457612004612b8b565b6020908102919091010152600101611fc3565b505f8660ff1685516120299190612d3f565b6001600160401b03811115612040576120406125b4565b60405190808252806020026020018201604052801561207357816020015b606081526020019060019003908161205e5790505b5090505f5b81518110156120cc578561208f60ff8a1683612d2c565b8151811061209f5761209f612b8b565b60200260200101518282815181106120b9576120b9612b8b565b6020908102919091010152600101612078565b508181856040516020016120e293929190613193565b60405160208183030381529060405280519060200120925050505b949350505050565b5f612159826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661236d9092919063ffffffff16565b905080515f14806121795750808060200190518101906121799190612a6f565b611aaa5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161033a565b5f805f5f1987146121e957866121ec565b83515b9250851561224b5733866001600160a01b0316146122435760405162461bcd60e51b81526020600482015260146024820152732937baba32b91d103bb937b7339039b2b73232b960611b604482015260640161033a565b859150612253565b836020015191505b7f4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f45c15801561228157508415155b156122e55733856001600160a01b0316146122de5760405162461bcd60e51b815260206004820152601960248201527f526f757465723a2077726f6e6720656d657267656e6379546f00000000000000604482015260640161033a565b50836122ec565b5060408301515b9450945094915050565b7f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5c808211156123435760405163161531e360e21b8152600481018390526024810182905260440161033a565b819003807f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5d5050565b60606120fd84845f85855f80866001600160a01b0316858760405161239291906131d5565b5f6040518083038185875af1925050503d805f81146123cc576040519150601f19603f3d011682016040523d82523d5f602084013e6123d1565b606091505b50915091506123e2878383876123ed565b979650505050505050565b6060831561245b5782515f03612454576001600160a01b0385163b6124545760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161033a565b50816120fd565b6120fd83838151156124705781518083602001fd5b8060405162461bcd60e51b815260040161033a91906131eb565b5f6020828403121561249a575f80fd5b5035919050565b6001600160a01b0381168114611e18575f80fd5b5f602082840312156124c5575f80fd5b81356124d0816124a1565b9392505050565b5f8083601f8401126124e7575f80fd5b5081356001600160401b038111156124fd575f80fd5b602083019150836020828501011115612514575f80fd5b9250929050565b5f805f805f805f60a0888a031215612531575f80fd5b873561253c816124a1565b96506020880135955060408801356001600160401b038082111561255e575f80fd5b61256a8b838c016124d7565b909750955060608a0135915061257f826124a1565b90935060808901359080821115612594575f80fd5b506125a18a828b016124d7565b989b979a50959850939692959293505050565b634e487b7160e01b5f52604160045260245ffd5b604051606081016001600160401b03811182821017156125ea576125ea6125b4565b60405290565b60405160c081016001600160401b03811182821017156125ea576125ea6125b4565b60405161016081016001600160401b03811182821017156125ea576125ea6125b4565b604080519081016001600160401b03811182821017156125ea576125ea6125b4565b604051601f8201601f191681016001600160401b038111828210171561267f5761267f6125b4565b604052919050565b5f6001600160401b0382111561269f5761269f6125b4565b5060051b60200190565b5f82601f8301126126b8575f80fd5b813560206126cd6126c883612687565b612657565b8083825260208201915060208460051b8701019350868411156126ee575f80fd5b602086015b8481101561270a57803583529183019183016126f3565b509695505050505050565b5f6001600160401b0382111561272d5761272d6125b4565b50601f01601f191660200190565b5f6127486126c884612715565b905082815283838301111561275b575f80fd5b828260208301375f602084830101529392505050565b5f82601f830112612780575f80fd5b813560206127906126c883612687565b82815260059290921b840181019181810190868411156127ae575f80fd5b8286015b8481101561270a5780356001600160401b038111156127cf575f80fd5b8701603f810189136127df575f80fd5b6127f089868301356040840161273b565b8452509183019183016127b2565b5f82601f83011261280d575f80fd5b611a3e8383356020850161273b565b5f82601f83011261282b575f80fd5b8135602061283b6126c883612687565b82815260059290921b84018101918181019086841115612859575f80fd5b8286015b8481101561270a5780356001600160401b0381111561287a575f80fd5b6128888986838b01016127fe565b84525091830191830161285d565b5f606082840312156128a6575f80fd5b6128ae6125c8565b905081358152602082013560208201526040820135604082015292915050565b5f602082840312156128de575f80fd5b81356001600160401b03808211156128f4575f80fd5b908301906101008286031215612908575f80fd5b6129106125f0565b82358152602083013582811115612925575f80fd5b612931878286016126a9565b602083015250604083013582811115612948575f80fd5b61295487828601612771565b60408301525060608301358281111561296b575f80fd5b6129778782860161281c565b60608301525060808301358281111561298e575f80fd5b61299a878286016127fe565b6080830152506129ad8660a08501612896565b60a082015295945050505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b634e487b7160e01b5f52602160045260245ffd5b60038110611e1857634e487b7160e01b5f52602160045260245ffd5b6001600160401b038516815260c060208201525f612a3a60c08301866129bb565b9050835160408301526020840151606083015260408401516080830152612a60836129fd565b8260a083015295945050505050565b5f60208284031215612a7f575f80fd5b815180151581146124d0575f80fd5b5f8085851115612a9c575f80fd5b83861115612aa8575f80fd5b5050820193919092039150565b6001600160401b0381168114611e18575f80fd5b5f805f805f60a08688031215612add575f80fd5b8535612ae881612ab5565b9450602086013560ff81168114612afd575f80fd5b935060408601356001600160401b0380821115612b18575f80fd5b612b2489838a01612771565b94506060880135915080821115612b39575f80fd5b612b4589838a0161281c565b93506080880135915080821115612b5a575f80fd5b50612b67888289016127fe565b9150509295509295909350565b5f60208284031215612b84575f80fd5b5051919050565b634e487b7160e01b5f52603260045260245ffd5b8051612baa81612ab5565b919050565b805163ffffffff81168114612baa575f80fd5b5f82601f830112612bd1575f80fd5b8151612bdf6126c882612715565b818152846020838601011115612bf3575f80fd5b8160208501602083015e5f918101602001919091529392505050565b805160038110612baa575f80fd5b5f6101608284031215612c2e575f80fd5b612c36612612565b905081518152602082015160208201526040820151604082015260608201516060820152612c6660808301612b9f565b6080820152612c7760a08301612baf565b60a082015260c08201516001600160401b03811115612c94575f80fd5b612ca084828501612bc2565b60c08301525060e082015160e0820152610100612cbe818401612c0f565b90820152610120612cd0838201612c0f565b818301525061014080830151818301525092915050565b5f60208284031215612cf7575f80fd5b81516001600160401b03811115612d0c575f80fd5b6120fd84828501612c1d565b634e487b7160e01b5f52601160045260245ffd5b80820180821115611a4157611a41612d18565b81810381811115611a4157611a41612d18565b612d5b816129fd565b9052565b60208152815160208201526020820151604082015260408201516060820152606082015160808201525f6080830151612da360a08401826001600160401b03169052565b5060a083015163ffffffff811660c08401525060c08301516101608060e0850152612dd26101808501836129bb565b60e086015161010086810191909152860151909250610120612df681870183612d52565b8601519050610140612e0a86820183612d52565b959095015193019290925250919050565b63ffffffff81511682526020810151602083015260408101516040830152606081015160608301525f608082015160e06080850152612e5d60e08501826129bb565b905060a083015184820360a0860152612e7682826129bb565b91505060c083015184820360c0860152612e9082826129bb565b95945050505050565b604081525f612eab6040830185612e1b565b905082151560208301529392505050565b5f60408284031215612ecc575f80fd5b612ed4612635565b9050815181526020820151602082015292915050565b5f60408284031215612efa575f80fd5b611a3e8383612ebc565b5f60208284031215612f14575f80fd5b81516124d0816124a1565b608081525f612f316080830186612e1b565b8451602084810191909152909401516040830152506001600160a01b0391909116606090910152919050565b5f8082840360c0811215612f6f575f80fd5b6080811215612f7c575f80fd5b50612f856125c8565b835181526020840151612f9781612ab5565b6020820152612fa98560408601612ebc565b60408201529150612fbd8460808501612ebc565b90509250929050565b5f60208284031215612fd6575f80fd5b81516001600160401b0380821115612fec575f80fd5b9083019060408286031215612fff575f80fd5b613007612635565b8251815260208301518281111561301c575f80fd5b61302887828601612c1d565b60208301525095945050505050565b5f60208284031215613047575f80fd5b604051602081018181106001600160401b0382111715613069576130696125b4565b6040529151825250919050565b5f8282518085526020808601955060208260051b840101602086015f5b848110156130c157601f198684030189526130af8383516129bb565b98840198925090830190600101613093565b5090979650505050505050565b5f8282518085526020808601955060208260051b840101602086015f5b848110156130c157601f198684030189526131078383516129bb565b988401989250908301906001016130eb565b85815260ff8516602082015260a060408201525f61313a60a0830186613076565b828103606084015261314c81866130ce565b9050828103608084015261316081856129bb565b98975050505050505050565b6001600160401b038616815284602082015260a060408201525f61313a60a0830186613076565b606081525f6131a56060830186613076565b82810360208401526131b781866130ce565b905082810360408401526131cb81856129bb565b9695505050505050565b5f82518060208501845e5f920191825250919050565b602081525f611a3e60208301846129bb56fee05cae1622d95c3f6f27e10abe6d4fad6823203161610f1838f0e2d2e438708ba2646970667358221220effa3504b77763ad25ea1609e1bb2330249f4f4c6e066bd81936ab65dada033c64736f6c63430008190033
Loading...
Loading
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.