Source Code
Overview
S Balance
More Info
ContractCreator
Multichain Info
N/A
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
CoreFacet
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 {Counters} from "@openzeppelin/contracts/utils/Counters.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ops} from "./Ops.sol";
import {ReentrancyGuardFacet} from "./ReentrancyGuardFacet.sol";
import {IAccessControlFacet} from "./interfaces/IAccessControlFacet.sol";
import {IOFTFacet, ICoreFacet} from "./interfaces/IOFTFacet.sol";
import {IDiamond} from "./interfaces/IDiamond.sol";
import {IEIP712Facet} from "./interfaces/IEIP712Facet.sol";
import {IPausableFacet} from "./interfaces/IPausableFacet.sol";
import {IAddressBook} from "../interfaces/IAddressBook.sol";
import {IGateKeeper} from "../interfaces/IGateKeeper.sol";
import {CoreFacetStorage, ProcessedOps} from "./libraries/CoreFacetStorage.sol";
import {OftOps} from "./libraries/ops/OftOps.sol";
import {RequestIdLib} from "../utils/RequestIdLib.sol";
import {TypecastLib} from "../utils/TypecastLib.sol";
contract CoreFacet is ICoreFacet, Ops, ReentrancyGuardFacet {
using Counters for Counters.Counter;
using CoreFacetStorage for CoreFacetStorage.DS;
/// @notice Role identifier for the accountant, authorized to receive execution fees.
bytes32 public constant ACCOUNTANT_ROLE = keccak256("ACCOUNTANT_ROLE");
/// @notice Default admin role identifier (0x00).
bytes32 private constant DEFAULT_ADMIN_ROLE = 0x00;
/// @notice Modifier to mark that the current operation is being executed on the origin network.
/// @dev Sets `isOriginNetwork = true` before the function and resets it after execution.
modifier originNetwork() {
CoreFacetStorage.setOriginNetwork(true);
_;
CoreFacetStorage.setOriginNetwork(false);
}
/// @notice Modifier to restrict access only to the configured receiver contract.
/// @dev Reverts if `msg.sender` is not equal to the address from `addressBook.receiver()`.
modifier onlyReceiver() {
address receiver = IAddressBook(CoreFacetStorage.ds().addressBook).receiver();
require(msg.sender == receiver || msg.sender == address(this), "CoreFacet: receiver only");
_;
}
/// @notice Modifier to handle cross-chain context for the current request.
/// @param requestId The ID of the cross-chain request being processed.
/// @dev Sets `currentRequestId` and resets it after execution. Also resets `currentChainIdFrom`.
modifier crosschainHandling(bytes32 requestId) {
require(requestId != 0, "CoreFacet: requestId is zero");
CoreFacetStorage.setCurrentRequestId(requestId);
_;
CoreFacetStorage.setCurrentRequestId(0);
CoreFacetStorage.setCurrentChainIdFrom(0);
}
/// @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), "CoreFacet: missing role");
_;
}
/// @notice Restricts function execution to when the contract is not paused.
/// @dev Reverts if the contract is currently paused.
modifier whenNotPaused() {
require(!IPausableFacet(address(this)).paused(), "CoreFacet: paused");
_;
}
/// @inheritdoc ICoreFacet
function setAddressBook(address addressBook_) external onlyRole(DEFAULT_ADMIN_ROLE) {
_setAddressBook(addressBook_);
_resetWETH();
_resetTreasury();
}
/// @inheritdoc ICoreFacet
function resetWETH() external onlyRole(DEFAULT_ADMIN_ROLE) {
_resetWETH();
}
/// @inheritdoc ICoreFacet
function resetTreasury() external onlyRole(DEFAULT_ADMIN_ROLE) {
_resetTreasury();
}
/// @inheritdoc ICoreFacet
function receiveValidatedData(bytes4 selector, bytes32 from, uint64 chainIdFrom) external onlyReceiver returns (bool) {
bytes32 router = IAddressBook(CoreFacetStorage.ds().addressBook).routerV3(chainIdFrom);
require(from == router, "CoreFacet: wrong sender");
require(selector == ICoreFacet.resume.selector, "CoreFacet: wrong selector");
CoreFacetStorage.setCurrentChainIdFrom(chainIdFrom);
return true;
}
/// @inheritdoc ICoreFacet
function start(
string[] calldata operations,
bytes[] calldata params,
InvoiceV4 calldata receipt,
bytes memory bridgeOptions
) external payable nonReentrant originNetwork {
CoreFacetStorage.setMsgValueLeft(msg.value);
_start(operations, params, receipt, bridgeOptions);
CoreFacetStorage.setMsgValueLeft(0);
}
/// @inheritdoc ICoreFacet
function resume(
bytes32 requestId,
uint8 cPos,
string[] calldata operations,
bytes[] memory params,
bytes memory bridgeOptions
) external onlyReceiver nonReentrant crosschainHandling(requestId) {
_resume(requestId, cPos, operations, params, bridgeOptions);
}
/// @inheritdoc ICoreFacet
function nonces(address account) external view returns (uint256) {
return CoreFacetStorage.ds().nonces[account].current();
}
/// @inheritdoc ICoreFacet
function startedOps(bytes32 requestId) external view returns (bytes32) {
return CoreFacetStorage.ds().startedOps[requestId];
}
/// @inheritdoc ICoreFacet
function processedOps(bytes32 requestId) external view returns (ProcessedOps memory) {
return CoreFacetStorage.ds().processedOps[requestId];
}
/// @inheritdoc ICoreFacet
function addressBook() external view returns (address) {
return CoreFacetStorage.ds().addressBook;
}
/// @inheritdoc ICoreFacet
function treasury() external view returns (address) {
return CoreFacetStorage.ds().treasury;
}
/// @inheritdoc ICoreFacet
function WETH() external view returns (address) {
return CoreFacetStorage.ds().WETH;
}
/// @notice Internal logic to start execution on the origin chain.
/// @param operations List of operations to execute.
/// @param params Encoded operation parameters.
/// @param receipt Signed invoice from the user with fee and execution info.
/// @param bridgeOptions Bridge-related options for cross-chain continuation.
function _start(
string[] calldata operations,
bytes[] calldata params,
InvoiceV4 calldata receipt,
bytes memory bridgeOptions
) private {
require(operations.length < 2**8, "CoreFacet: wrong params count");
require(operations.length == params.length, "CoreFacet: wrong params");
uint256 balanceBeforeStart = address(this).balance - msg.value;
{
(bytes32 hash, bytes memory data) = _getRawData(operations, params);
address accountant = _checkSignature(msg.sender, hash, data, receipt, bridgeOptions);
_proceedFees(receipt.invoice.executionPrice, accountant, address(0));
_proceedFees(receipt.feeShare, CoreFacetStorage.ds().treasury, receipt.feeToken);
_proceedFees(receipt.feeShare, receipt.feeShareRecipient, receipt.feeToken);
}
(
bytes32 nextRequestId,
uint64 chainIdTo,
ExecutionResult result,
uint8 lastOp
) = _execute(0, operations, params, bridgeOptions);
uint256 newBalance = address(this).balance;
if (newBalance > balanceBeforeStart) {
(bool sent, ) = msg.sender.call{value: newBalance - balanceBeforeStart}("");
require(sent, "CoreFacet: failed to send ETH");
}
emit ComplexOpProcessed(uint64(block.chainid), 0, chainIdTo, nextRequestId, result, lastOp);
}
/// @notice Internal logic to resume cross-chain execution.
/// @param requestId The ID of the request being resumed.
/// @param cPos The index of the operation to resume from.
/// @param operations List of operations to perform.
/// @param params Encoded operation parameters.
/// @param bridgeOptions Encoded bridge-related options for further continuation.
function _resume(
bytes32 requestId,
uint8 cPos,
string[] calldata operations,
bytes[] memory params,
bytes memory bridgeOptions
) private {
require(operations.length < 2**8, "CoreFacet: wrong params count");
require(operations.length == params.length, "CoreFacet: wrong params");
require(cPos < params.length, "CoreFacet: wrong params");
(
bytes32 nextRequestId,
uint64 chainIdTo,
ExecutionResult result,
uint8 lastOp
) = _execute(cPos, operations, params, bridgeOptions);
emit ComplexOpProcessed(uint64(block.chainid), requestId, chainIdTo, nextRequestId, result, lastOp);
}
/// @notice Verifies and transfers execution fee to the accountant.
/// @param executionPrice Amount to be paid for execution.
/// @param feeRecipient Address receiving the fee.
/// @param feeToken Fee token address.
function _proceedFees(uint256 executionPrice, address feeRecipient, address feeToken) private {
if (executionPrice != 0) {
require(feeRecipient != address(0), "CoreFacet: invalid feeRecipient");
if (feeToken == address(0)) {
CoreFacetStorage.consumeMsgValue(executionPrice);
(bool sent, ) = feeRecipient.call{ value: executionPrice }("");
require(sent, "CoreFacet: failed to send Ether");
} else {
SafeERC20.safeTransferFrom(IERC20(feeToken), msg.sender, feeRecipient, executionPrice);
}
emit FeePaid(msg.sender, feeToken, feeRecipient, executionPrice);
}
}
/// @notice Executes a sequence of operations.
/// @param cPos Starting index in the operation list.
/// @param operations List of operation names.
/// @param params Encoded operation parameters.
/// @param bridgeOptions Bridge-related options.
/// @return nextRequestId The request ID to be used for the next chain.
/// @return chainIdTo Target chain ID for cross-chain continuation.
/// @return result Execution result (Succeeded, Failed, or Interrupted).
/// @return lastOp Index of the last executed operation.
function _execute(uint256 cPos, string[] calldata operations, bytes[] memory params, bytes memory bridgeOptions) private whenNotPaused returns (
bytes32 nextRequestId,
uint64 chainIdTo,
ExecutionResult result,
uint8 lastOp
) {
ExecutionContext memory context;
bytes32[] memory operationsCode = _getOperationsCode(operations);
require(checkOperations(cPos, operationsCode), "CoreFacet: wrong sequence of operations");
for (uint256 i = cPos; i < operations.length; ++i) {
{
bytes memory data;
if (
operationsCode[i] == OftOps.BURN_MINT_OFT_CODE ||
operationsCode[i] == OftOps.EMERGENCY_MINT_OFT_CODE ||
operationsCode[i] == OftOps.PUSH_MINT_OFT_CODE
) {
data = abi.encodeWithSelector(
getOpSelector(operationsCode[i]),
IOFTFacet.ExecuteOftParams(
i,
operationsCode,
operations,
params,
bridgeOptions,
context.maskedParams
)
);
} else {
data = abi.encodeWithSelector(
getOpSelector(operationsCode[i]),
(CoreFacetStorage.currentRequestId() != 0 && i == cPos),
operationsCode[i],
operationsCode[i + 1],
params[i],
context.maskedParams
);
}
CoreFacetStorage.setDiamondCall(true);
(bool success, bytes memory ret) = address(this).delegatecall(data);
CoreFacetStorage.setDiamondCall(false);
if (!success) {
assembly {
revert(add(ret, 32), mload(ret))
}
}
(chainIdTo, context.updatedParams, context.maskedParams, result) = abi.decode(
ret,
(
uint64,
bytes,
ICoreFacet.MaskedParams,
ICoreFacet.ExecutionResult
)
);
}
require(result != ExecutionResult.Failed, string(abi.encodePacked("CoreFacet: op ", operations[i], " is not supported")));
lastOp = uint8(i);
if (result == ExecutionResult.Interrupted) {
break;
} else if (chainIdTo != 0) {
if (operationsCode[i] != OftOps.BURN_MINT_OFT_CODE) {
if (context.updatedParams.length != 0) {
params[i] = context.updatedParams;
}
nextRequestId = _handleCrossChain(chainIdTo, IAddressBook(CoreFacetStorage.ds().addressBook).routerV3(chainIdTo), params, operations, i, bridgeOptions);
}
break;
}
}
}
/// @notice Verifies an off-chain signed receipt and recovers signer.
/// @param from Sender address.
/// @param operationHash Hash of the operations.
/// @param data Encoded data associated with the hash.
/// @param receipt Signed invoice (includes signature and deadline).
/// @param options Additional bridging options.
/// @return accountant The address that signed the invoice.
function _checkSignature(
address from,
bytes32 operationHash,
bytes memory data,
InvoiceV4 calldata receipt,
bytes memory options
) private returns (address accountant) {
uint256 nonce = _getAndUpdateNonce(from);
bytes32 accountantHash = keccak256(
abi.encodePacked(
keccak256(
"AccountantPermit(address from,uint256 nonce,bytes32 operationHash,bytes data,uint256 executionPrice,uint256 deadline,uint256 feeShare,address feeShareRecipient,address feeToken,bytes options)"
),
from,
nonce,
operationHash,
data,
receipt.invoice.executionPrice,
receipt.invoice.deadline,
receipt.feeShare,
receipt.feeShareRecipient,
receipt.feeToken,
options
)
);
bytes32 hash = ECDSA.toEthSignedMessageHash(IEIP712Facet(address(this)).hashTypedDataV4(accountantHash));
accountant = ECDSA.recover(hash, receipt.invoice.v, receipt.invoice.r, receipt.invoice.s);
require(block.timestamp <= receipt.invoice.deadline, "CoreFacet: deadline");
require(IAccessControlFacet(address(this)).hasRole(ACCOUNTANT_ROLE, accountant), "CoreFacet: invalid signature from worker");
}
/// @notice Sends encoded data to the GateKeeper for cross-chain execution.
/// @param chainIdTo Target chain ID.
/// @param router Target router address.
/// @param params Operation parameters.
/// @param operations List of operations.
/// @param pos Position from which to resume.
/// @param bridgeOptions Encoded bridge options.
/// @return nextRequestId The computed cross-chain request identifier.
function _handleCrossChain(
uint64 chainIdTo,
bytes32 router,
bytes[] memory params,
string[] calldata operations,
uint256 pos,
bytes memory bridgeOptions
)
private
returns(bytes32 nextRequestId)
{
CoreFacetStorage.DS storage ds = CoreFacetStorage.ds();
(bytes[] memory currentBridgeOptions, bytes memory nextBridgeOptions) = _popOptions(bridgeOptions);
nextRequestId = _getRequestId(
IAddressBook(ds.addressBook).routerV3(chainIdTo),
chainIdTo,
_getSaltAndUpdateCurrentOptions(currentBridgeOptions, pos)
);
require(ds.startedOps[nextRequestId] == bytes32(0), "CoreFacet: requestId already used");
bytes memory out = abi.encodeWithSelector(
ICoreFacet.resume.selector,
nextRequestId,
uint8(pos),
operations,
params,
nextBridgeOptions
);
IGateKeeper(IAddressBook(ds.addressBook).gateKeeper()).sendData(out, router, chainIdTo, currentBridgeOptions);
ds.startedOps[nextRequestId] = keccak256(params[pos]);
}
/// @notice Retrieves and increments the nonce for a specific address.
/// @param whose Address to get and increment nonce for.
/// @return nonce Current nonce value before increment.
function _getAndUpdateNonce(address whose) private returns (uint256 nonce) {
Counters.Counter storage counter = CoreFacetStorage.ds().nonces[whose];
nonce = counter.current();
counter.increment();
}
/// @notice Sets the address book and caches WETH.
/// @param addressBook_ The address of the address book contract.
/// @dev Internal version of setAddressBook; performs address check and WETH cache.
function _setAddressBook(address addressBook_) private {
_checkAddress(addressBook_);
CoreFacetStorage.ds().addressBook = addressBook_;
}
/// @notice Caches WETH from address book.
/// @dev can be reset to address(0).
function _resetWETH() private {
CoreFacetStorage.DS storage ds = CoreFacetStorage.ds();
// can be set to address(0)
ds.WETH = IAddressBook(ds.addressBook).WETH();
}
/// @notice Caches Treasury from address book.
/// @dev can be reset to address(0).
function _resetTreasury() private {
CoreFacetStorage.DS storage ds = CoreFacetStorage.ds();
// can be set to address(0)
ds.treasury = IAddressBook(ds.addressBook).treasury();
}
/// @notice Computes a cross-chain request ID using salt.
/// @param receiver The destination module address on the target chain (bytes32).
/// @param chainIdTo Target chain ID.
/// @param salt Salt for generate requestId.
/// @return requestId The computed cross-chain request identifier.
function _getRequestId(bytes32 receiver, uint64 chainIdTo, uint256 salt) private view returns (bytes32 requestId) {
requestId = RequestIdLib.prepareRequestId(
receiver,
chainIdTo,
TypecastLib.castToBytes32(address(this)),
block.chainid,
salt
);
}
/// @notice Ensures the provided address is not zero.
/// @param checkingAddress The address to validate.
/// @dev Reverts if the address is zero. Used to prevent misconfiguration.
function _checkAddress(address checkingAddress) private pure {
require(checkingAddress != address(0), "CoreFacet: zero address");
}
/// @notice Concatenates operations and parameters to create a hash and raw data blob.
/// @param operations Array of operation names as strings.
/// @param params Array of ABI-encoded parameter blobs.
/// @return hash Hash of concatenated operation names.
/// @return data Concatenated parameters as a single blob.
function _getRawData(
string[] calldata operations,
bytes[] calldata params
) private pure returns (bytes32 hash, bytes memory data) {
bytes memory op;
for (uint256 i = 0; i < operations.length; ++i) {
op = bytes.concat(op, bytes(operations[i]));
if (data.length == 0) {
data = params[i];
} else {
data = bytes.concat(data, ",", params[i]);
}
}
hash = keccak256(op);
}
/// @notice Encodes each operation name into a keccak256 code for internal routing.
/// @param operations Array of operation names as strings.
/// @return operationsCode Array of hashed operation codes plus trailing zero.
function _getOperationsCode(string[] calldata operations) private pure returns(bytes32[] memory) {
bytes32[] memory operationsCode = new bytes32[](operations.length + 1);
for (uint256 i = 0; i < operations.length; i++) {
operationsCode[i] = keccak256(bytes(operations[i]));
}
operationsCode[operationsCode.length - 1] = bytes32(0);
return operationsCode;
}
/// @notice Splits bridge options into current and next step configurations.
/// @param options_ ABI-encoded array of arrays of bytes (2D array).
/// @return currentOptions Options to be used on this chain.
/// @return nextOptions ABI-encoded remaining options for next chain(s).
function _popOptions(bytes memory options_) private pure returns (bytes[] memory, bytes memory) {
bytes[][] memory options = abi.decode(options_, (bytes[][]));
bytes[] memory currentOptions = options[options.length - 1];
bytes[][] memory nextOptions = new bytes[][](options.length - 1);
for (uint8 i; i < options.length - 1; i++) {
nextOptions[i] = options[i];
}
return (currentOptions, abi.encode(nextOptions));
}
/// @notice Obtaining salt for generating requestId and updating currentBridgeOptions as necessarys.
/// @param currentBridgeOptions Options to be used on this chain.
/// @param pos Position from which to resume.
/// @return salt Salt for generate requestId.
function _getSaltAndUpdateCurrentOptions(bytes[] memory currentBridgeOptions, uint256 pos) private view returns(uint256){
bytes32 currentRequestId = CoreFacetStorage.currentRequestId();
uint256 salt;
if(currentRequestId == bytes32(0)) {
salt = abi.decode(currentBridgeOptions[currentBridgeOptions.length - 1], (uint256));
} else {
salt = RequestIdLib.prepareSalt(pos, currentRequestId);
currentBridgeOptions[currentBridgeOptions.length -1] = abi.encode(salt);
}
return salt;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.0;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}// 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/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: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// 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 IGateKeeper {
function sendData(
bytes calldata data,
bytes32 to,
uint64 chainIdTo,
bytes[] calldata brideOptions
) external;
function estimateGasFee(
bytes calldata data,
bytes32 to,
uint64 chainIdTo,
bytes[] memory brideOptions
) external view returns(uint256);
function nonces(address protocol) external view returns (uint256);
}// 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 {IDiamondCut} from "./IDiamondCut.sol";
import {IDiamondLoupe} from "./IDiamondLoupe.sol";
interface IDiamond is IDiamondCut, IDiamondLoupe {
/// @notice Thrown when an ETH transfer is attempted from an unauthorized sender.
/// @param sender The address that tried to send ETH.
error InvalidEthSender(address sender);
/// @notice Thrown when trying to route a call with a selector that is not registered.
/// @param selector The function selector that was not found.
error FunctionNotFound(bytes4 selector);
/// @notice Thrown when the provided facet address has no code.
error InitHasNoCode();
/// @notice Thrown when a facet address is zero during add or replace operation.
error FacetNotFound();
/// @notice Thrown when attempting to add a function selector that already exists.
error SelectorAlreadyExists();
/// @notice Thrown when trying to replace or remove a selector that does not exist.
error SelectorAbsent();
/// @notice Thrown when trying to replace a selector with the same facet it already belongs to.
error SameFacet();
/// @notice Thrown when an unknown `FacetCutAction` is passed to the `diamondCut` function.
error UnknownFacetCutAction();
/// @notice Thrown when `diamondCut` is called with an empty array.
error EmptyCut();
/// @notice Thrown when an empty selector array is passed to add/replace helpers.
error EmptySelectors();
/// @notice Thrown when the caller is not authorized to perform the action.
error UnauthorizedCaller();
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
interface IDiamondCut {
/// @notice Enum describing the type of facet cut operation.
/// @dev Used in `diamondCut` to define how selectors should be applied.
enum FacetCutAction {
Add, // Add new selectors
Replace, // Replace existing selectors with new facet implementation
Remove // Remove selectors from the diamond
}
/// @notice Struct representing a single change to the diamond's function selector table.
/// @param facetAddress The address of the facet to which the selectors will be added or replaced.
/// Must be zero address if action is Remove.
/// @param action The type of operation (Add, Replace, Remove).
/// @param functionSelectors The list of function selectors to apply.
struct FacetCut {
address facetAddress;
FacetCutAction action;
bytes4[] functionSelectors;
}
/// @notice Emitted when the diamond is updated via `diamondCut`.
/// @param cut The list of facet changes applied.
/// @param init The address of the initialization contract (if any).
/// @param data The calldata passed to `init` via delegatecall after the cut.
event DiamondCut(FacetCut[] cut, address init, bytes data);
/// @notice Performs add/replace/remove operations on function selectors.
/// @dev Follows the EIP-2535 `diamondCut` standard. Can optionally delegatecall an init function.
/// @param cut The array of facet changes to apply.
/// @param init The address of a contract with an initializer function (optional).
/// @param data The calldata to pass to the initializer via delegatecall (optional).
function diamondCut(FacetCut[] calldata cut, address init, bytes calldata data) external;
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
interface IDiamondLoupe {
/// @notice Struct returned by loupe functions to inspect facets and their selectors.
/// @param facetAddress The address of the facet.
/// @param functionSelectors The list of selectors implemented by this facet.
struct Facet {
address facetAddress;
bytes4[] functionSelectors;
}
/// @notice Returns all facet addresses and the selectors they implement.
/// @dev Used by tooling and scanners to inspect the structure of the diamond.
/// @return An array of all facets with their function selectors.
function facets() external view returns (Facet[] memory);
/// @notice Returns the list of selectors for a given facet address.
/// @param facet The address of the facet to inspect.
/// @return The list of function selectors implemented by the facet.
function facetFunctionSelectors(address facet) external view returns (bytes4[] memory);
/// @notice Returns all unique facet addresses used by the diamond.
/// @return An array of all facet addresses currently active in the diamond.
function facetAddresses() external view returns (address[] memory);
/// @notice Returns the facet address responsible for a specific selector.
/// @param selector The function selector to lookup.
/// @return The address of the facet that implements the selector, or address(0) if none.
function facetAddress(bytes4 selector) external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;
import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol";
interface IEIP712Facet is IERC5267 {
/// @notice Initializes EIP712 domain data: name, version, domain separator, and chainId cache.
/// @dev Can only be called once. Meant to be invoked during diamondCut initialization.
/// @param name The user-readable name of the signing domain (e.g., "Eywa Protocol").
/// @param version The current major version of the signing domain (e.g., "1").
function initialize(string calldata name, string calldata version) external;
/// @notice Returns the EIP-712 hash of the fully encoded typed data.
/// @dev Combines the domain separator and provided struct hash using ECDSA helper.
/// @param structHash The keccak256 hash of the typed data struct.
/// @return The resulting EIP-712 hash for signing or signature verification.
function hashTypedDataV4(bytes32 structHash) external view returns (bytes32);
/// @notice Returns detailed EIP-712 domain fields.
/// @dev Follows EIP-5267 `eip712Domain()` view specification.
/// @return fields Bitmask indicating which fields are present.
/// @return name The user-readable name of the domain.
/// @return version The version of the domain.
/// @return chainId The current chain ID.
/// @return verifyingContract The contract address used for verification.
/// @return salt Always zero in this implementation.
/// @return extensions Always empty in this implementation.
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}// 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;
interface IPausableFacet {
/// @notice Emitted when the contract is paused.
/// @param account The address that triggered the pause.
event Paused(address account);
/// @notice Emitted when the contract is unpaused.
/// @param account The address that lifted the pause.
event Unpaused(address account);
/// @notice Triggers the paused state.
/// @dev Can only be called when the contract is not paused.
/// Emits a {Paused} event.
function pause() external;
/// @notice Lifts the paused state and resumes normal operations.
/// @dev Can only be called when the contract is paused.
/// Emits an {Unpaused} event.
function unpause() external;
/// @notice Returns whether the contract is currently paused.
/// @dev Reads the `paused` flag from PausableFacetStorage.
/// @return True if the contract is paused, false otherwise.
function paused() external view returns (bool);
}// 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;
library ReentrancyGuardFacetStorage {
/// @notice Fixed storage slot for the ReentrancyGuardFacet data structure.
bytes32 private constant POSITION = keccak256("crosscurve.reentrancy.guard.facet.storage");
/// @notice Layout for all persistent ReentrancyGuardFacet state.
/// @dev Every variable needed by ReentrancyGuardFacet logic is grouped here and kept
/// at the single slot defined in `POSITION`.
struct DS {
uint256 status;
}
/// @notice Accessor to the ReentrancyGuardFacet 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
}
}
}// 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.25;
import {ReentrancyGuardFacetStorage} from "./libraries/ReentrancyGuardFacetStorage.sol";
abstract contract ReentrancyGuardFacet {
using ReentrancyGuardFacetStorage for ReentrancyGuardFacetStorage.DS;
/// @dev Indicates that the function is not currently entered.
uint256 private constant NOT_ENTERED = 1;
/// @dev Indicates that the function is currently being executed (entered).
uint256 private constant ENTERED = 2;
/// @notice Modifier to protect a function from being reentered.
/// @dev Should be applied to external/public functions where reentrancy must be prevented.
/// Internally uses a shared storage flag across all facets of the diamond.
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
/// @notice Checks whether the reentrancy guard is currently entered.
/// @dev Returns true if a protected function is currently executing.
/// @return entered True if `nonReentrant` is currently active.
function _reentrancyGuardEntered() internal view returns (bool) {
return ReentrancyGuardFacetStorage.ds().status == ENTERED;
}
/// @dev Internal logic executed before a `nonReentrant`-protected function body.
/// Sets the reentrancy status to `ENTERED`.
/// Reverts if the guard is already active.
function _nonReentrantBefore() private {
ReentrancyGuardFacetStorage.DS storage ds = ReentrancyGuardFacetStorage.ds();
require(ds.status != ENTERED, "ReentrancyGuardFacet: reentrant call");
ds.status = ENTERED;
}
/// @dev Internal logic executed after a `nonReentrant`-protected function body.
/// Resets the reentrancy status to `NOT_ENTERED`.
function _nonReentrantAfter() private {
ReentrancyGuardFacetStorage.ds().status = NOT_ENTERED;
}
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2023 - all rights reserved
pragma solidity ^0.8.17;
library RequestIdLib {
/// @notice type hash for generate requestId
bytes32 private constant TYPEHASH_V1 = keccak256(
"CrosschainRequest/v1(uint256 chainIdFrom,bytes32 from,uint256 chainIdTo,bytes32 to,bytes32 salt)"
);
/// @notice type hash for generate salt
bytes32 private constant TYPEHASH_V2 = keccak256(
"CrosschainRequest/v2(uint256 i,bytes32 requestId)"
);
/**
* @dev Prepares a request ID with the given arguments.
*
* @param to receiver;
* @param chainIdTo opposite chain id;
* @param from sender;
* @param chainIdFrom current chain id;
* @param salt salt for generate requestId.
*/
function prepareRequestId(
bytes32 to,
uint256 chainIdTo,
bytes32 from,
uint256 chainIdFrom,
uint256 salt
) internal pure returns (bytes32) {
return keccak256(abi.encode(TYPEHASH_V1, chainIdFrom, from, chainIdTo, to, salt));
}
/**
* @dev Prepares a request ID with the given arguments.
*
* @param pos operation number in operations sequence;
* @param requestId current requestId;
*/
function prepareSalt(
uint256 pos,
bytes32 requestId
) internal pure returns (uint256) {
return uint256(keccak256(abi.encode(TYPEHASH_V2, pos, requestId)));
}
}// 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":"uint64","name":"currentChainId","type":"uint64"},{"indexed":true,"internalType":"bytes32","name":"currentRequestId","type":"bytes32"},{"indexed":true,"internalType":"uint64","name":"nextChainId","type":"uint64"},{"indexed":false,"internalType":"bytes32","name":"nextRequestId","type":"bytes32"},{"indexed":false,"internalType":"enum ICoreFacet.ExecutionResult","name":"result","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"lastOp","type":"uint8"}],"name":"ComplexOpProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"accountant","type":"address"},{"indexed":false,"internalType":"uint256","name":"executionPrice","type":"uint256"}],"name":"FeePaid","type":"event"},{"inputs":[],"name":"ACCOUNTANT_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"addressBook","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"}],"name":"processedOps","outputs":[{"components":[{"internalType":"bytes32","name":"hashSynthParams","type":"bytes32"},{"internalType":"uint8","name":"crossChainOpState","type":"uint8"}],"internalType":"struct ProcessedOps","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"bytes32","name":"from","type":"bytes32"},{"internalType":"uint64","name":"chainIdFrom","type":"uint64"}],"name":"receiveValidatedData","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resetTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resetWETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"uint8","name":"cPos","type":"uint8"},{"internalType":"string[]","name":"operations","type":"string[]"},{"internalType":"bytes[]","name":"params","type":"bytes[]"},{"internalType":"bytes","name":"bridgeOptions","type":"bytes"}],"name":"resume","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addressBook_","type":"address"}],"name":"setAddressBook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[]","name":"operations","type":"string[]"},{"internalType":"bytes[]","name":"params","type":"bytes[]"},{"components":[{"components":[{"internalType":"uint256","name":"executionPrice","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct ICoreFacet.Invoice","name":"invoice","type":"tuple"},{"internalType":"uint256","name":"feeShare","type":"uint256"},{"internalType":"address","name":"feeShareRecipient","type":"address"},{"internalType":"address","name":"feeToken","type":"address"}],"internalType":"struct ICoreFacet.InvoiceV4","name":"receipt","type":"tuple"},{"internalType":"bytes","name":"bridgeOptions","type":"bytes"}],"name":"start","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"}],"name":"startedOps","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6080604052348015600e575f80fd5b506141818061001c5f395ff3fe6080604052600436106100bf575f3560e01c8063788cd3031161007c578063ab1efbab11610057578063ab1efbab14610239578063ad5c46481461026c578063f5887cdd146102a8578063f87cf42b146102d1575f80fd5b8063788cd303146101af5780637ecebe00146101c2578063a785ac5a146101ef575f80fd5b80630b3448a8146100c35780632509db2b146100e45780632eba2287146101185780634dc9fb351461012c57806361d027b31461014b578063746362af1461019b575b5f80fd5b3480156100ce575f80fd5b506100e26100dd36600461342c565b610365565b005b3480156100ef575f80fd5b506101036100fe366004613462565b61040d565b60405190151581526020015b60405180910390f35b348015610123575f80fd5b506100e261063a565b348015610137575f80fd5b506100e2610146366004613685565b6106c7565b348015610156575f80fd5b507f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d81023546001600160a01b03165b6040516001600160a01b03909116815260200161010f565b3480156101a6575f80fd5b506100e2610852565b6100e26101bd36600461373a565b6108dc565b3480156101cd575f80fd5b506101e16101dc36600461342c565b610948565b60405190815260200161010f565b3480156101fa575f80fd5b506101e16102093660046137d9565b5f9081527f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d81020602052604090205490565b348015610244575f80fd5b506101e17f369da55721ba2b3acddd63aac7d6512c3e5762a78fa01c44f423f97868330c3481565b348015610277575f80fd5b507f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d81024546001600160a01b0316610183565b3480156102b3575f80fd5b505f8051602061412c833981519152546001600160a01b0316610183565b3480156102dc575f80fd5b506103476102eb3660046137d9565b6040805180820182525f80825260209182018190529283527f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d810218152918190208151808301909252805482526001015460ff169181019190915290565b604080518251815260209283015160ff16928101929092520161010f565b604051632474521560e21b81525f600482018190523360248301529030906391d1485490604401602060405180830381865afa1580156103a7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103cb91906137f0565b6103f05760405162461bcd60e51b81526004016103e79061380f565b60405180910390fd5b6103f982610973565b6104016109aa565b610409610a53565b5050565b5f805f8051602061410c8339815191526003015f9054906101000a90046001600160a01b03166001600160a01b031663f7260d3e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561046e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104929190613846565b9050336001600160a01b03821614806104aa57503330145b6104f15760405162461bcd60e51b8152602060048201526018602482015277436f726546616365743a207265636569766572206f6e6c7960401b60448201526064016103e7565b5f5f8051602061410c833981519152600301546040516397c2cd9160e01b81526001600160401b03861660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa158015610550573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105749190613861565b90508085146105c55760405162461bcd60e51b815260206004820152601760248201527f436f726546616365743a2077726f6e672073656e64657200000000000000000060448201526064016103e7565b6001600160e01b03198616634dc9fb3560e01b146106255760405162461bcd60e51b815260206004820152601960248201527f436f726546616365743a2077726f6e672073656c6563746f720000000000000060448201526064016103e7565b61062e84610afc565b50600195945050505050565b604051632474521560e21b81525f600482018190523360248301529030906391d1485490604401602060405180830381865afa15801561067c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106a091906137f0565b6106bc5760405162461bcd60e51b81526004016103e79061380f565b6106c4610a53565b50565b5f8051602061412c8339815191525460408051637b93069f60e11b815290515f926001600160a01b03169163f7260d3e9160048083019260209291908290030181865afa15801561071a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061073e9190613846565b9050336001600160a01b038216148061075657503330145b61079d5760405162461bcd60e51b8152602060048201526018602482015277436f726546616365743a207265636569766572206f6e6c7960401b60448201526064016103e7565b6107a5610b22565b865f8190036107f65760405162461bcd60e51b815260206004820152601c60248201527f436f726546616365743a20726571756573744964206973207a65726f0000000060448201526064016103e7565b6107ff81610ba7565b61080d888888888888610bba565b6108165f610ba7565b61081f5f610afc565b5061084960017f236d8745cf67013b639a9e51590705a5c1689700fe245a20e25de4883a43a50155565b50505050505050565b604051632474521560e21b81525f600482018190523360248301529030906391d1485490604401602060405180830381865afa158015610894573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108b891906137f0565b6108d45760405162461bcd60e51b81526004016103e79061380f565b6106c46109aa565b6108e4610b22565b6108ee6001610cc6565b6108f734610cec565b610905868686868686610d12565b61090e5f610cec565b6109175f610cc6565b61094060017f236d8745cf67013b639a9e51590705a5c1689700fe245a20e25de4883a43a50155565b505050505050565b6001600160a01b0381165f9081525f8051602061410c83398151915260205260408120545b92915050565b61097c81610f4e565b5f8051602061412c83398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b5f8051602061412c83398151915254604080516315ab88c960e31b815290515f8051602061410c833981519152926001600160a01b03169163ad5c46489160048083019260209291908290030181865afa158015610a0a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a2e9190613846565b60059190910180546001600160a01b0319166001600160a01b03909216919091179055565b5f8051602061412c83398151915254604080516361d027b360e01b815290515f8051602061410c833981519152926001600160a01b0316916361d027b39160048083019260209291908290030181865afa158015610ab3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ad79190613846565b60049190910180546001600160a01b0319166001600160a01b03909216919091179055565b807f67b6f0fb6bca8398a49a7966c252819542f4d36560c9c6961937957fdf20f9f45d50565b7f236d8745cf67013b639a9e51590705a5c1689700fe245a20e25de4883a43a501805460011901610ba15760405162461bcd60e51b8152602060048201526024808201527f5265656e7472616e6379477561726446616365743a207265656e7472616e742060448201526318d85b1b60e21b60648201526084016103e7565b60029055565b805f805160206140ec8339815191525d50565b6101008310610c0b5760405162461bcd60e51b815260206004820152601d60248201527f436f726546616365743a2077726f6e6720706172616d7320636f756e7400000060448201526064016103e7565b81518314610c2b5760405162461bcd60e51b81526004016103e790613878565b81518560ff1610610c4e5760405162461bcd60e51b81526004016103e790613878565b5f805f80610c628960ff1689898989610fa4565b9350935093509350826001600160401b03168a466001600160401b03167f830adbcf80ee865e0f0883ad52e813fdbf061b0216b724694a2b4e06708d243c878686604051610cb2939291906138c3565b60405180910390a450505050505050505050565b807fb13bd13498ce9409b85a4287d16ff0fcdaca732822a47a97d2bdada3325aa4515d50565b807f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5d50565b6101008510610d635760405162461bcd60e51b815260206004820152601d60248201527f436f726546616365743a2077726f6e6720706172616d7320636f756e7400000060448201526064016103e7565b848314610d825760405162461bcd60e51b81526004016103e790613878565b5f610d8d3447613911565b90505f80610d9d898989896115bd565b915091505f610daf33848489896116e0565b9050610dbd8635825f611990565b610df660a08701355f8051602061410c833981519152600401546001600160a01b0316610df16101008a0160e08b0161342c565b611990565b610e1f60a0870135610e0e60e0890160c08a0161342c565b610df16101008a0160e08b0161342c565b505f91508190508080610e3e818c8c610e388c8e613924565b8a610fa4565b929650909450925090504785811115610eee575f33610e5d8884613911565b6040515f81818185875af1925050503d805f8114610e96576040519150601f19603f3d011682016040523d82523d5f602084013e610e9b565b606091505b5050905080610eec5760405162461bcd60e51b815260206004820152601d60248201527f436f726546616365743a206661696c656420746f2073656e642045544800000060448201526064016103e7565b505b836001600160401b03165f801b466001600160401b03167f830adbcf80ee865e0f0883ad52e813fdbf061b0216b724694a2b4e06708d243c888787604051610f38939291906138c3565b60405180910390a4505050505050505050505050565b6001600160a01b0381166106c45760405162461bcd60e51b815260206004820152601760248201527f436f726546616365743a207a65726f206164647265737300000000000000000060448201526064016103e7565b5f805f80306001600160a01b0316635c975abb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fe4573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061100891906137f0565b156110495760405162461bcd60e51b815260206004820152601160248201527010dbdc99519858d95d0e881c185d5cd959607a1b60448201526064016103e7565b6040805160a0810182525f91810182815260608083018490526080830184905290825260208201529061107c8a8a611b07565b90506110888b82611bf6565b6110e45760405162461bcd60e51b815260206004820152602760248201527f436f726546616365743a2077726f6e672073657175656e6365206f66206f7065604482015266726174696f6e7360c81b60648201526084016103e7565b8a5b898110156115ae5760405162424d6f60e81b60208201526060906023016040516020818303038152906040528051906020012083838151811061112b5761112b613930565b6020026020010151148061117e575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012083838151811061117457611174613930565b6020026020010151145b806111c8575060405162504d6f60e81b6020820152602301604051602081830303815290604052805190602001208383815181106111be576111be613930565b6020026020010151145b15611274576111ef8383815181106111e2576111e2613930565b602002602001015161252a565b6040518060c001604052808481526020018581526020018e8e906112139190613944565b81526020018c81526020018b8152602001865f015181525060405160240161123b9190613a4b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152905061134c565b6112898383815181106111e2576111e2613930565b5f805160206140ec8339815191525c158015906112a557508d83145b8484815181106112b7576112b7613930565b6020026020010151858560016112cd9190613b27565b815181106112dd576112dd613930565b60200260200101518d86815181106112f7576112f7613930565b6020026020010151885f0151604051602401611317959493929190613b3a565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915290505b61135660016129e1565b5f80306001600160a01b0316836040516113709190613b9e565b5f60405180830381855af49150503d805f81146113a8576040519150601f19603f3d011682016040523d82523d5f602084013e6113ad565b606091505b50915091506113bb5f6129e1565b816113c857805160208201fd5b808060200190518101906113dc9190613bf6565b908952602089019190915290995097505f92506113f7915050565b856002811115611409576114096138af565b14158b8b8381811061141d5761141d613930565b905060200281019061142f9190613c94565b604051602001611440929190613cd6565b6040516020818303038152906040529061146d5760405162461bcd60e51b81526004016103e79190613d18565b509250826002856002811115611485576114856138af565b146115ae576001600160401b038616156115a65760405162424d6f60e81b6020820152602301604051602081830303815290604052805190602001208282815181106114d3576114d3613930565b6020026020010151146115a1576020830151511561150e57826020015189828151811061150257611502613930565b60200260200101819052505b61159e865f8051602061410c833981519152600301546040516397c2cd9160e01b81526001600160401b038a1660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa158015611570573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115949190613861565b8b8e8e868e612a07565b96505b6115ae565b6001016110e6565b50505095509550955095915050565b5f6060805f5b868110156116cc57818888838181106115de576115de613930565b90506020028101906115f09190613c94565b60405160200161160293929190613d2a565b604051602081830303815290604052915082515f0361167b5785858281811061162d5761162d613930565b905060200281019061163f9190613c94565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152509295506116c492505050565b8286868381811061168e5761168e613930565b90506020028101906116a09190613c94565b6040516020016116b293929190613d48565b60405160208183030381529060405292505b6001016115c3565b508051602090910120969095509350505050565b6001600160a01b0385165f9081525f8051602061410c833981519152602052604081208054600181019091555f7f5bcf05d83365d29aecd67f8c1840946d1e5fcdb04a77cbcebaa726e12b4355f988838989893560208b013560a08c013561174e60e08e0160c08f0161342c565b8d60e0016020810190611761919061342c565b8d60405160200161177c9b9a99989796959493929190613d73565b60408051808303601f190181529082905280516020909101206309301e5160e31b82526004820181905291505f9061182a903090634980f28890602401602060405180830381865afa1580156117d4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117f89190613861565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f908152601c91909152603c902090565b905061184f816118406060890160408a01613dee565b606089013560808a0135612c7d565b9350602086013542111561189b5760405162461bcd60e51b8152602060048201526013602482015272436f726546616365743a20646561646c696e6560681b60448201526064016103e7565b604051632474521560e21b81527f369da55721ba2b3acddd63aac7d6512c3e5762a78fa01c44f423f97868330c3460048201526001600160a01b038516602482015230906391d1485490604401602060405180830381865afa158015611903573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061192791906137f0565b6119845760405162461bcd60e51b815260206004820152602860248201527f436f726546616365743a20696e76616c6964207369676e61747572652066726f60448201526736903bb7b935b2b960c11b60648201526084016103e7565b50505095945050505050565b8215611b02576001600160a01b0382166119ec5760405162461bcd60e51b815260206004820152601f60248201527f436f726546616365743a20696e76616c696420666565526563697069656e740060448201526064016103e7565b6001600160a01b038116611aa857611a0383612ca5565b5f826001600160a01b0316846040515f6040518083038185875af1925050503d805f8114611a4c576040519150601f19603f3d011682016040523d82523d5f602084013e611a51565b606091505b5050905080611aa25760405162461bcd60e51b815260206004820152601f60248201527f436f726546616365743a206661696c656420746f2073656e642045746865720060448201526064016103e7565b50611ab4565b611ab481338486612d1c565b604080516001600160a01b0383811682528416602082015290810184905233907f1b37fcc57f4b6029ca7b3a70af0104811f67c72fe73e8043575f03a01e0566319060600160405180910390a25b505050565b60605f611b15836001613b27565b6001600160401b03811115611b2c57611b2c613509565b604051908082528060200260200182016040528015611b55578160200160208202803683370190505b5090505f5b83811015611bc157848482818110611b7457611b74613930565b9050602002810190611b869190613c94565b604051611b94929190613e07565b6040518091039020828281518110611bae57611bae613930565b6020908102919091010152600101611b5a565b505f801b8160018351611bd49190613911565b81518110611be457611be4613930565b60209081029190910101529392505050565b5f825b60018351611c079190613911565b811015612520575f838281518110611c2157611c21613930565b60200260200101519050604051602001611c44906210b13960e91b815260030190565b604051602081830303815290604052805190602001208103611cc5578351600214611c73575f9250505061096d565b8115611c83575f9250505061096d565b60028451611c919190613911565b8214611ca1575f9250505061096d565b5f805160206140ec8339815191525c15611cbf575f9250505061096d565b50612518565b5f805160206140ec8339815191525c15801590611ce157508482145b8015611e045750604051614c4d60f01b602082015260220160405160208183030381529060405280519060200120811480611d41575060405161425560f01b60208201526022016040516020818303038152906040528051906020012081145b80611d71575060405161424d60f01b60208201526022016040516020818303038152906040528051906020012081145b80611da1575060405161214d60f01b60208201526022016040516020818303038152906040528051906020012081145b80611dd1575060405161215560f01b60208201526022016040516020818303038152906040528051906020012081145b80611e02575060405162424d6f60e81b60208201526023016040516020818303038152906040528051906020012081145b155b15611e13575f9250505061096d565b604051600560fc1b602082015260210160405160208183030381529060405280519060200120811480611e6a5750604051605760f81b60208201526021016040516020818303038152906040528051906020012081145b80611e9b575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012081145b80611ecc575060405162504d6f60e81b60208201526023016040516020818303038152906040528051906020012081145b8015611ed757508115155b15611ee6575f9250505061096d565b60405161557760f01b60208201526022016040516020818303038152906040528051906020012081148015611f28575060028451611f249190613911565b8214155b15611f37575f9250505061096d565b604051614c4d60f01b60208201526022016040516020818303038152906040528051906020012081148015611fb65750604051614c4d60f01b60208201526022016040516020818303038152906040528051906020012084836001611f9c9190613b27565b81518110611fac57611fac613930565b6020026020010151145b15611fc5575f9250505061096d565b60405161425560f01b60208201526022016040516020818303038152906040528051906020012081148015612044575060405161425560f01b6020820152602201604051602081830303815290604052805190602001208483600161202a9190613b27565b8151811061203a5761203a613930565b6020026020010151145b15612053575f9250505061096d565b60405161424d60f01b602082015260220160405160208183030381529060405280519060200120811480156120d2575060405161424d60f01b602082015260220160405160208183030381529060405280519060200120848360016120b89190613b27565b815181106120c8576120c8613930565b6020026020010151145b156120e1575f9250505061096d565b60405161214d60f01b60208201526022016040516020818303038152906040528051906020012081148061213a575060405161215560f01b60208201526022016040516020818303038152906040528051906020012081145b8061216b575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012081145b8015612178575060028451115b15612187575f9250505061096d565b60405161313960f11b602082015260220160405160208183030381529060405280519060200120810361230557815f036121c5575f9250505061096d565b600284516121d39190613911565b82146121e3575f9250505061096d565b5f805160206140ec8339815191525c15612201575f9250505061096d565b5f8461220e600185613911565b8151811061221e5761221e613930565b602002602001015190506040516020016122409061525360f01b815260020190565b60405160208183030381529060405280519060200120811415801561228d5750604051632126a0a760e11b6020820152602401604051602081830303815290604052805190602001208114155b80156122c2575060405164424155544f60d81b6020820152602501604051602081830303815290604052805190602001208114155b80156122ee57507f33c4137323016cc703f06d36ae2c7f43f1a70fa030334bd76934bba665c80d6a8114155b156122fe575f935050505061096d565b5050612518565b60405161525360f01b60208201526022016040516020818303038152906040528051906020012081036123c2575f600285516123419190613911565b831490505f60405160200161235e9061313960f11b815260020190565b60405160208183030381529060405280519060200120868560016123829190613b27565b8151811061239257612392613930565b6020026020010151149050811580156123a9575080155b156123ba575f94505050505061096d565b505050612518565b604051632126a0a760e11b602082015260240160405160208183030381529060405280519060200120811480612420575060405164424155544f60d81b60208201526025016040516020818303038152906040528051906020012081145b15612434575f600285516123419190613911565b604051631094919160e21b60208201526024016040516020818303038152906040528051906020012081036124df578115612473575f9250505061096d565b600284516124819190613911565b8214612491575f9250505061096d565b5f805160206140ec8339815191525c156124af575f9250505061096d565b7fb13bd13498ce9409b85a4287d16ff0fcdaca732822a47a97d2bdada3325aa4515c611cbf575f9250505061096d565b7f33c4137323016cc703f06d36ae2c7f43f1a70fa030334bd76934bba665c80d6a8103612516578115612491575f9250505061096d565b505b600101611bf9565b5060019392505050565b604051600560fc1b60208201525f90602101604051602081830303815290604052805190602001208214806125835750604051605760f81b60208201526021016040516020818303038152906040528051906020012082145b806125b3575060405161557760f01b60208201526022016040516020818303038152906040528051906020012082145b156125c657506378b09df560e11b919050565b604051614c4d60f01b60208201526022016040516020818303038152906040528051906020012082148061261f575060405161425560f01b60208201526022016040516020818303038152906040528051906020012082145b8061264f575060405161424d60f01b60208201526022016040516020818303038152906040528051906020012082145b8061267f575060405161214d60f01b60208201526022016040516020818303038152906040528051906020012082145b806126af575060405161215560f01b60208201526022016040516020818303038152906040528051906020012082145b156126c257506378b09df560e11b919050565b604051604160f81b6020820152602101604051602081830303815290604052805190602001208214806127195750604051602960f91b60208201526021016040516020818303038152906040528051906020012082145b806127485750604051605360f81b60208201526021016040516020818303038152906040528051906020012082145b1561275b575063bae6336160e01b919050565b60405162424d6f60e81b6020820152602301604051602081830303815290604052805190602001208214806127b6575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012082145b806127e7575060405162504d6f60e81b60208201526023016040516020818303038152906040528051906020012082145b156127fa57506306c7c2df60e51b919050565b60405161525360f01b602082015260220160405160208183030381529060405280519060200120820361283557506360c09bfb60e01b919050565b604051632126a0a760e11b6020820152602401604051602081830303815290604052805190602001208203612872575063208ed10d60e01b919050565b60405164424155544f60d81b60208201526025016040516020818303038152906040528051906020012082036128b057506354fab89960e01b919050565b604051631094919160e21b60208201526024016040516020818303038152906040528051906020012082036128ed575063e86c200760e01b919050565b60405161313960f11b6020820152602201604051602081830303815290604052805190602001208203612928575063e1e0bc4760e01b919050565b6040516210b13960e91b6020820152602301604051602081830303815290604052805190602001208203612964575063603ea51160e01b919050565b7f33c4137323016cc703f06d36ae2c7f43f1a70fa030334bd76934bba665c80d6a820361299957506307daa05760e21b919050565b60405162461bcd60e51b815260206004820152601860248201527f4f70733a206f70206973206e6f7420737570706f72746564000000000000000060448201526064016103e7565b807f2e445143a211ecbb689de5812742d02a96369c482aec76832fc56490cf3cc6f25d50565b5f5f8051602061410c8339815191528180612a2185612d7c565b60038501546040516397c2cd9160e01b81526001600160401b038f166004820152929450909250612ab0916001600160a01b03909116906397c2cd9190602401602060405180830381865afa158015612a7c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612aa09190613861565b8c612aab858a612eb4565b612fad565b5f81815260018501602052604090205490945015612b1a5760405162461bcd60e51b815260206004820152602160248201527f436f726546616365743a2072657175657374496420616c7265616479207573656044820152601960fa1b60648201526084016103e7565b5f634dc9fb3560e01b85888b8b8e87604051602401612b3e96959493929190613e3e565b60408051601f19818403018152918152602080830180516001600160e01b03166001600160e01b031990951694909417909352600387015481516345d61ded60e01b815291519294506001600160a01b0316926345d61ded926004808401938290030181865afa158015612bb4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612bd89190613846565b6001600160a01b0316635ab83031828d8f876040518563ffffffff1660e01b8152600401612c099493929190613f13565b5f604051808303815f87803b158015612c20575f80fd5b505af1158015612c32573d5f803e3d5ffd5b50505050898781518110612c4857612c48613930565b602002602001015180519060200120846001015f8781526020019081526020015f208190555050505050979650505050505050565b5f805f612c8c87878787613022565b91509150612c99816130df565b5090505b949350505050565b7f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5c80821115612cf25760405163161531e360e21b815260048101839052602481018290526044016103e7565b819003807f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5d5050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052612d76908590613228565b50505050565b6060805f83806020019051810190612d949190613f4c565b90505f8160018351612da69190613911565b81518110612db657612db6613930565b602002602001015190505f60018351612dcf9190613911565b6001600160401b03811115612de657612de6613509565b604051908082528060200260200182016040528015612e1957816020015b6060815260200190600190039081612e045790505b5090505f5b60018451612e2c9190613911565b8160ff161015612e8657838160ff1681518110612e4b57612e4b613930565b6020026020010151828260ff1681518110612e6857612e68613930565b60200260200101819052508080612e7e9061406b565b915050612e1e565b508181604051602001612e999190614089565b60405160208183030381529060405294509450505050915091565b5f5f805160206140ec8339815191525c8181612f0b578460018651612ed99190613911565b81518110612ee957612ee9613930565b6020026020010151806020019051810190612f049190613861565b9050612c9d565b50604080517f06b4022e2836acc0aa7bdc5c38c002fa2eab38d450a98c495029a50f34131b7360208083019190915281830186905260608083018590528351808403909101815260808301909352825192019190912060a082018190529060c0016040516020818303038152906040528560018751612f8a9190613911565b81518110612f9a57612f9a613930565b6020026020010181905250949350505050565b604080517f59a1e8b66b6609951241aea6f047c6e9004e4e389ca42bfd172112ab74c3e24b60208083019190915246828401523060608301526001600160401b038516608083015260a0820186905260c08083018590528351808403909101815260e090920190925280519101205f90612c9d565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561305757505f905060036130d6565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156130a8573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b0381166130d0575f600192509250506130d6565b91505f90505b94509492505050565b5f8160048111156130f2576130f26138af565b036130fa5750565b600181600481111561310e5761310e6138af565b0361315b5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016103e7565b600281600481111561316f5761316f6138af565b036131bc5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016103e7565b60038160048111156131d0576131d06138af565b036106c45760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016103e7565b5f61327c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166132fb9092919063ffffffff16565b905080515f148061329c57508080602001905181019061329c91906137f0565b611b025760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016103e7565b6060612c9d84845f85855f80866001600160a01b031685876040516133209190613b9e565b5f6040518083038185875af1925050503d805f811461335a576040519150601f19603f3d011682016040523d82523d5f602084013e61335f565b606091505b50915091506133708783838761337b565b979650505050505050565b606083156133e95782515f036133e2576001600160a01b0385163b6133e25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016103e7565b5081612c9d565b612c9d83838151156133fe5781518083602001fd5b8060405162461bcd60e51b81526004016103e79190613d18565b6001600160a01b03811681146106c4575f80fd5b5f6020828403121561343c575f80fd5b813561344781613418565b9392505050565b6001600160401b03811681146106c4575f80fd5b5f805f60608486031215613474575f80fd5b83356001600160e01b03198116811461348b575f80fd5b92506020840135915060408401356134a28161344e565b809150509250925092565b803560ff811681146134bd575f80fd5b919050565b5f8083601f8401126134d2575f80fd5b5081356001600160401b038111156134e8575f80fd5b6020830191508360208260051b8501011115613502575f80fd5b9250929050565b634e487b7160e01b5f52604160045260245ffd5b604051606081016001600160401b038111828210171561353f5761353f613509565b60405290565b604051601f8201601f191681016001600160401b038111828210171561356d5761356d613509565b604052919050565b5f6001600160401b0382111561358d5761358d613509565b5060051b60200190565b5f6001600160401b038211156135af576135af613509565b50601f01601f191660200190565b5f6135cf6135ca84613597565b613545565b90508281528383830111156135e2575f80fd5b828260208301375f602084830101529392505050565b5f82601f830112613607575f80fd5b613447838335602085016135bd565b5f6136236135ca84613575565b8381529050602080820190600585901b840186811115613641575f80fd5b845b8181101561367a5780356001600160401b03811115613660575f80fd5b61366c898289016135f8565b855250928201928201613643565b505050509392505050565b5f805f805f8060a0878903121561369a575f80fd5b863595506136aa602088016134ad565b945060408701356001600160401b03808211156136c5575f80fd5b6136d18a838b016134c2565b909650945060608901359150808211156136e9575f80fd5b818901915089601f8301126136fc575f80fd5b61370b8a833560208501613616565b93506080890135915080821115613720575f80fd5b5061372d89828a016135f8565b9150509295509295509295565b5f805f805f80868803610160811215613751575f80fd5b87356001600160401b0380821115613767575f80fd5b6137738b838c016134c2565b909950975060208a013591508082111561378b575f80fd5b6137978b838c016134c2565b9097509550859150610100603f19840112156137b1575f80fd5b60408a0194506101408a01359250808311156137cb575f80fd5b505061372d89828a016135f8565b5f602082840312156137e9575f80fd5b5035919050565b5f60208284031215613800575f80fd5b81518015158114613447575f80fd5b60208082526017908201527f436f726546616365743a206d697373696e6720726f6c65000000000000000000604082015260600190565b5f60208284031215613856575f80fd5b815161344781613418565b5f60208284031215613871575f80fd5b5051919050565b60208082526017908201527f436f726546616365743a2077726f6e6720706172616d73000000000000000000604082015260600190565b634e487b7160e01b5f52602160045260245ffd5b83815260608101600384106138e657634e487b7160e01b5f52602160045260245ffd5b83602083015260ff83166040830152949350505050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561096d5761096d6138fd565b5f613447368484613616565b634e487b7160e01b5f52603260045260245ffd5b5f6139516135ca84613575565b80848252602080830192508560051b85013681111561396e575f80fd5b855b818110156139b95780356001600160401b0381111561398d575f80fd5b870136601f82011261399d575f80fd5b6139ab3682358684016135bd565b865250938201938201613970565b50919695505050505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b5f8282518085526020808601955060208260051b840101602086015f5b84811015613a3e57601f19868403018952613a2c8383516139c5565b98840198925090830190600101613a10565b5090979650505050505050565b6020808252825182820152828101516101006040840152805161012084018190525f929182019083906101408601905b80831015613a9b5783518252928401926001929092019190840190613a7b565b5060408701519350601f19925082868203016060870152613abc81856139f3565b93505050606085015181858403016080860152613ad983826139f3565b9250506080850151818584030160a0860152613af583826139c5565b9250505060a0840151613b1f60c08501828051825260208082015190830152604090810151910152565b509392505050565b8082018082111561096d5761096d6138fd565b851515815284602082015283604082015260e060608201525f613b6060e08301856139c5565b83516080840152602084015160a0840152604084015160c084015290509695505050505050565b5f81518060208401855e5f93019283525090919050565b5f6134478284613b87565b5f82601f830112613bb8575f80fd5b8151613bc66135ca82613597565b818152846020838601011115613bda575f80fd5b8160208501602083015e5f918101602001919091529392505050565b5f805f8084860360c0811215613c0a575f80fd5b8551613c158161344e565b60208701519095506001600160401b03811115613c30575f80fd5b613c3c88828901613ba9565b9450506060603f1982011215613c50575f80fd5b50613c5961351d565b6040860151815260608601516020820152608086015160408201528092505060a085015160038110613c89575f80fd5b939692955090935050565b5f808335601e19843603018112613ca9575f80fd5b8301803591506001600160401b03821115613cc2575f80fd5b602001915036819003821315613502575f80fd5b6d021b7b932a330b1b2ba1d1037b8160951b81528183600e83013770081a5cc81b9bdd081cdd5c1c1bdc9d1959607a1b9101600e810191909152601f01919050565b602081525f61344760208301846139c5565b5f613d358286613b87565b838582375f930192835250909392505050565b5f613d538286613b87565b600b60fa1b8152838560018301375f930160010192835250909392505050565b8b81525f6bffffffffffffffffffffffff19808d60601b1660208401528b60348401528a6054840152613da9607484018b613b87565b898152886020820152876040820152818760601b166060820152818660601b166074820152613ddb6088820186613b87565b9f9e505050505050505050505050505050565b5f60208284031215613dfe575f80fd5b613447826134ad565b818382375f9101908152919050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b5f60a08201888352602060ff8916602085015260a060408501528187835260c08501905060c08860051b8601019250885f5b89811015613edc5786850360bf190183528135368c9003601e19018112613e95575f80fd5b8b0184810190356001600160401b03811115613eaf575f80fd5b803603821315613ebd575f80fd5b613ec8878284613e16565b965050509183019190830190600101613e70565b505050508281036060840152613ef281866139f3565b90508281036080840152613f0681856139c5565b9998505050505050505050565b608081525f613f2560808301876139c5565b8560208401526001600160401b0385166040840152828103606084015261337081856139f3565b5f60208284031215613f5c575f80fd5b81516001600160401b0380821115613f72575f80fd5b818401915084601f830112613f85575f80fd5b8151613f936135ca82613575565b8082825260208201915060208360051b860101925087831115613fb4575f80fd5b602085015b8381101561405f57805185811115613fcf575f80fd5b8601603f81018a13613fdf575f80fd5b6020810151613ff06135ca82613575565b81815260059190911b82016040019060208101908c831115614010575f80fd5b604084015b838110156140495780518a81111561402b575f80fd5b61403a8f604083890101613ba9565b84525060209283019201614015565b5086525050602093840193919091019050613fb9565b50979650505050505050565b5f60ff821660ff8103614080576140806138fd565b60010192915050565b5f60208083016020845280855180835260408601915060408160051b8701019250602087015f5b828110156140de57603f198886030184526140cc8583516139f3565b945092850192908501906001016140b0565b509297965050505050505056fe4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f41985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8101f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d81022a2646970667358221220b8ec90dbf2c8bca0886e68288e6cf18706c97019dbca12670d45ede8ba3d9c8664736f6c63430008190033
Deployed Bytecode
0x6080604052600436106100bf575f3560e01c8063788cd3031161007c578063ab1efbab11610057578063ab1efbab14610239578063ad5c46481461026c578063f5887cdd146102a8578063f87cf42b146102d1575f80fd5b8063788cd303146101af5780637ecebe00146101c2578063a785ac5a146101ef575f80fd5b80630b3448a8146100c35780632509db2b146100e45780632eba2287146101185780634dc9fb351461012c57806361d027b31461014b578063746362af1461019b575b5f80fd5b3480156100ce575f80fd5b506100e26100dd36600461342c565b610365565b005b3480156100ef575f80fd5b506101036100fe366004613462565b61040d565b60405190151581526020015b60405180910390f35b348015610123575f80fd5b506100e261063a565b348015610137575f80fd5b506100e2610146366004613685565b6106c7565b348015610156575f80fd5b507f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d81023546001600160a01b03165b6040516001600160a01b03909116815260200161010f565b3480156101a6575f80fd5b506100e2610852565b6100e26101bd36600461373a565b6108dc565b3480156101cd575f80fd5b506101e16101dc36600461342c565b610948565b60405190815260200161010f565b3480156101fa575f80fd5b506101e16102093660046137d9565b5f9081527f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d81020602052604090205490565b348015610244575f80fd5b506101e17f369da55721ba2b3acddd63aac7d6512c3e5762a78fa01c44f423f97868330c3481565b348015610277575f80fd5b507f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d81024546001600160a01b0316610183565b3480156102b3575f80fd5b505f8051602061412c833981519152546001600160a01b0316610183565b3480156102dc575f80fd5b506103476102eb3660046137d9565b6040805180820182525f80825260209182018190529283527f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d810218152918190208151808301909252805482526001015460ff169181019190915290565b604080518251815260209283015160ff16928101929092520161010f565b604051632474521560e21b81525f600482018190523360248301529030906391d1485490604401602060405180830381865afa1580156103a7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103cb91906137f0565b6103f05760405162461bcd60e51b81526004016103e79061380f565b60405180910390fd5b6103f982610973565b6104016109aa565b610409610a53565b5050565b5f805f8051602061410c8339815191526003015f9054906101000a90046001600160a01b03166001600160a01b031663f7260d3e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561046e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104929190613846565b9050336001600160a01b03821614806104aa57503330145b6104f15760405162461bcd60e51b8152602060048201526018602482015277436f726546616365743a207265636569766572206f6e6c7960401b60448201526064016103e7565b5f5f8051602061410c833981519152600301546040516397c2cd9160e01b81526001600160401b03861660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa158015610550573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105749190613861565b90508085146105c55760405162461bcd60e51b815260206004820152601760248201527f436f726546616365743a2077726f6e672073656e64657200000000000000000060448201526064016103e7565b6001600160e01b03198616634dc9fb3560e01b146106255760405162461bcd60e51b815260206004820152601960248201527f436f726546616365743a2077726f6e672073656c6563746f720000000000000060448201526064016103e7565b61062e84610afc565b50600195945050505050565b604051632474521560e21b81525f600482018190523360248301529030906391d1485490604401602060405180830381865afa15801561067c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106a091906137f0565b6106bc5760405162461bcd60e51b81526004016103e79061380f565b6106c4610a53565b50565b5f8051602061412c8339815191525460408051637b93069f60e11b815290515f926001600160a01b03169163f7260d3e9160048083019260209291908290030181865afa15801561071a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061073e9190613846565b9050336001600160a01b038216148061075657503330145b61079d5760405162461bcd60e51b8152602060048201526018602482015277436f726546616365743a207265636569766572206f6e6c7960401b60448201526064016103e7565b6107a5610b22565b865f8190036107f65760405162461bcd60e51b815260206004820152601c60248201527f436f726546616365743a20726571756573744964206973207a65726f0000000060448201526064016103e7565b6107ff81610ba7565b61080d888888888888610bba565b6108165f610ba7565b61081f5f610afc565b5061084960017f236d8745cf67013b639a9e51590705a5c1689700fe245a20e25de4883a43a50155565b50505050505050565b604051632474521560e21b81525f600482018190523360248301529030906391d1485490604401602060405180830381865afa158015610894573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108b891906137f0565b6108d45760405162461bcd60e51b81526004016103e79061380f565b6106c46109aa565b6108e4610b22565b6108ee6001610cc6565b6108f734610cec565b610905868686868686610d12565b61090e5f610cec565b6109175f610cc6565b61094060017f236d8745cf67013b639a9e51590705a5c1689700fe245a20e25de4883a43a50155565b505050505050565b6001600160a01b0381165f9081525f8051602061410c83398151915260205260408120545b92915050565b61097c81610f4e565b5f8051602061412c83398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b5f8051602061412c83398151915254604080516315ab88c960e31b815290515f8051602061410c833981519152926001600160a01b03169163ad5c46489160048083019260209291908290030181865afa158015610a0a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a2e9190613846565b60059190910180546001600160a01b0319166001600160a01b03909216919091179055565b5f8051602061412c83398151915254604080516361d027b360e01b815290515f8051602061410c833981519152926001600160a01b0316916361d027b39160048083019260209291908290030181865afa158015610ab3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ad79190613846565b60049190910180546001600160a01b0319166001600160a01b03909216919091179055565b807f67b6f0fb6bca8398a49a7966c252819542f4d36560c9c6961937957fdf20f9f45d50565b7f236d8745cf67013b639a9e51590705a5c1689700fe245a20e25de4883a43a501805460011901610ba15760405162461bcd60e51b8152602060048201526024808201527f5265656e7472616e6379477561726446616365743a207265656e7472616e742060448201526318d85b1b60e21b60648201526084016103e7565b60029055565b805f805160206140ec8339815191525d50565b6101008310610c0b5760405162461bcd60e51b815260206004820152601d60248201527f436f726546616365743a2077726f6e6720706172616d7320636f756e7400000060448201526064016103e7565b81518314610c2b5760405162461bcd60e51b81526004016103e790613878565b81518560ff1610610c4e5760405162461bcd60e51b81526004016103e790613878565b5f805f80610c628960ff1689898989610fa4565b9350935093509350826001600160401b03168a466001600160401b03167f830adbcf80ee865e0f0883ad52e813fdbf061b0216b724694a2b4e06708d243c878686604051610cb2939291906138c3565b60405180910390a450505050505050505050565b807fb13bd13498ce9409b85a4287d16ff0fcdaca732822a47a97d2bdada3325aa4515d50565b807f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5d50565b6101008510610d635760405162461bcd60e51b815260206004820152601d60248201527f436f726546616365743a2077726f6e6720706172616d7320636f756e7400000060448201526064016103e7565b848314610d825760405162461bcd60e51b81526004016103e790613878565b5f610d8d3447613911565b90505f80610d9d898989896115bd565b915091505f610daf33848489896116e0565b9050610dbd8635825f611990565b610df660a08701355f8051602061410c833981519152600401546001600160a01b0316610df16101008a0160e08b0161342c565b611990565b610e1f60a0870135610e0e60e0890160c08a0161342c565b610df16101008a0160e08b0161342c565b505f91508190508080610e3e818c8c610e388c8e613924565b8a610fa4565b929650909450925090504785811115610eee575f33610e5d8884613911565b6040515f81818185875af1925050503d805f8114610e96576040519150601f19603f3d011682016040523d82523d5f602084013e610e9b565b606091505b5050905080610eec5760405162461bcd60e51b815260206004820152601d60248201527f436f726546616365743a206661696c656420746f2073656e642045544800000060448201526064016103e7565b505b836001600160401b03165f801b466001600160401b03167f830adbcf80ee865e0f0883ad52e813fdbf061b0216b724694a2b4e06708d243c888787604051610f38939291906138c3565b60405180910390a4505050505050505050505050565b6001600160a01b0381166106c45760405162461bcd60e51b815260206004820152601760248201527f436f726546616365743a207a65726f206164647265737300000000000000000060448201526064016103e7565b5f805f80306001600160a01b0316635c975abb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fe4573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061100891906137f0565b156110495760405162461bcd60e51b815260206004820152601160248201527010dbdc99519858d95d0e881c185d5cd959607a1b60448201526064016103e7565b6040805160a0810182525f91810182815260608083018490526080830184905290825260208201529061107c8a8a611b07565b90506110888b82611bf6565b6110e45760405162461bcd60e51b815260206004820152602760248201527f436f726546616365743a2077726f6e672073657175656e6365206f66206f7065604482015266726174696f6e7360c81b60648201526084016103e7565b8a5b898110156115ae5760405162424d6f60e81b60208201526060906023016040516020818303038152906040528051906020012083838151811061112b5761112b613930565b6020026020010151148061117e575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012083838151811061117457611174613930565b6020026020010151145b806111c8575060405162504d6f60e81b6020820152602301604051602081830303815290604052805190602001208383815181106111be576111be613930565b6020026020010151145b15611274576111ef8383815181106111e2576111e2613930565b602002602001015161252a565b6040518060c001604052808481526020018581526020018e8e906112139190613944565b81526020018c81526020018b8152602001865f015181525060405160240161123b9190613a4b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152905061134c565b6112898383815181106111e2576111e2613930565b5f805160206140ec8339815191525c158015906112a557508d83145b8484815181106112b7576112b7613930565b6020026020010151858560016112cd9190613b27565b815181106112dd576112dd613930565b60200260200101518d86815181106112f7576112f7613930565b6020026020010151885f0151604051602401611317959493929190613b3a565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915290505b61135660016129e1565b5f80306001600160a01b0316836040516113709190613b9e565b5f60405180830381855af49150503d805f81146113a8576040519150601f19603f3d011682016040523d82523d5f602084013e6113ad565b606091505b50915091506113bb5f6129e1565b816113c857805160208201fd5b808060200190518101906113dc9190613bf6565b908952602089019190915290995097505f92506113f7915050565b856002811115611409576114096138af565b14158b8b8381811061141d5761141d613930565b905060200281019061142f9190613c94565b604051602001611440929190613cd6565b6040516020818303038152906040529061146d5760405162461bcd60e51b81526004016103e79190613d18565b509250826002856002811115611485576114856138af565b146115ae576001600160401b038616156115a65760405162424d6f60e81b6020820152602301604051602081830303815290604052805190602001208282815181106114d3576114d3613930565b6020026020010151146115a1576020830151511561150e57826020015189828151811061150257611502613930565b60200260200101819052505b61159e865f8051602061410c833981519152600301546040516397c2cd9160e01b81526001600160401b038a1660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa158015611570573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115949190613861565b8b8e8e868e612a07565b96505b6115ae565b6001016110e6565b50505095509550955095915050565b5f6060805f5b868110156116cc57818888838181106115de576115de613930565b90506020028101906115f09190613c94565b60405160200161160293929190613d2a565b604051602081830303815290604052915082515f0361167b5785858281811061162d5761162d613930565b905060200281019061163f9190613c94565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152509295506116c492505050565b8286868381811061168e5761168e613930565b90506020028101906116a09190613c94565b6040516020016116b293929190613d48565b60405160208183030381529060405292505b6001016115c3565b508051602090910120969095509350505050565b6001600160a01b0385165f9081525f8051602061410c833981519152602052604081208054600181019091555f7f5bcf05d83365d29aecd67f8c1840946d1e5fcdb04a77cbcebaa726e12b4355f988838989893560208b013560a08c013561174e60e08e0160c08f0161342c565b8d60e0016020810190611761919061342c565b8d60405160200161177c9b9a99989796959493929190613d73565b60408051808303601f190181529082905280516020909101206309301e5160e31b82526004820181905291505f9061182a903090634980f28890602401602060405180830381865afa1580156117d4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117f89190613861565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f908152601c91909152603c902090565b905061184f816118406060890160408a01613dee565b606089013560808a0135612c7d565b9350602086013542111561189b5760405162461bcd60e51b8152602060048201526013602482015272436f726546616365743a20646561646c696e6560681b60448201526064016103e7565b604051632474521560e21b81527f369da55721ba2b3acddd63aac7d6512c3e5762a78fa01c44f423f97868330c3460048201526001600160a01b038516602482015230906391d1485490604401602060405180830381865afa158015611903573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061192791906137f0565b6119845760405162461bcd60e51b815260206004820152602860248201527f436f726546616365743a20696e76616c6964207369676e61747572652066726f60448201526736903bb7b935b2b960c11b60648201526084016103e7565b50505095945050505050565b8215611b02576001600160a01b0382166119ec5760405162461bcd60e51b815260206004820152601f60248201527f436f726546616365743a20696e76616c696420666565526563697069656e740060448201526064016103e7565b6001600160a01b038116611aa857611a0383612ca5565b5f826001600160a01b0316846040515f6040518083038185875af1925050503d805f8114611a4c576040519150601f19603f3d011682016040523d82523d5f602084013e611a51565b606091505b5050905080611aa25760405162461bcd60e51b815260206004820152601f60248201527f436f726546616365743a206661696c656420746f2073656e642045746865720060448201526064016103e7565b50611ab4565b611ab481338486612d1c565b604080516001600160a01b0383811682528416602082015290810184905233907f1b37fcc57f4b6029ca7b3a70af0104811f67c72fe73e8043575f03a01e0566319060600160405180910390a25b505050565b60605f611b15836001613b27565b6001600160401b03811115611b2c57611b2c613509565b604051908082528060200260200182016040528015611b55578160200160208202803683370190505b5090505f5b83811015611bc157848482818110611b7457611b74613930565b9050602002810190611b869190613c94565b604051611b94929190613e07565b6040518091039020828281518110611bae57611bae613930565b6020908102919091010152600101611b5a565b505f801b8160018351611bd49190613911565b81518110611be457611be4613930565b60209081029190910101529392505050565b5f825b60018351611c079190613911565b811015612520575f838281518110611c2157611c21613930565b60200260200101519050604051602001611c44906210b13960e91b815260030190565b604051602081830303815290604052805190602001208103611cc5578351600214611c73575f9250505061096d565b8115611c83575f9250505061096d565b60028451611c919190613911565b8214611ca1575f9250505061096d565b5f805160206140ec8339815191525c15611cbf575f9250505061096d565b50612518565b5f805160206140ec8339815191525c15801590611ce157508482145b8015611e045750604051614c4d60f01b602082015260220160405160208183030381529060405280519060200120811480611d41575060405161425560f01b60208201526022016040516020818303038152906040528051906020012081145b80611d71575060405161424d60f01b60208201526022016040516020818303038152906040528051906020012081145b80611da1575060405161214d60f01b60208201526022016040516020818303038152906040528051906020012081145b80611dd1575060405161215560f01b60208201526022016040516020818303038152906040528051906020012081145b80611e02575060405162424d6f60e81b60208201526023016040516020818303038152906040528051906020012081145b155b15611e13575f9250505061096d565b604051600560fc1b602082015260210160405160208183030381529060405280519060200120811480611e6a5750604051605760f81b60208201526021016040516020818303038152906040528051906020012081145b80611e9b575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012081145b80611ecc575060405162504d6f60e81b60208201526023016040516020818303038152906040528051906020012081145b8015611ed757508115155b15611ee6575f9250505061096d565b60405161557760f01b60208201526022016040516020818303038152906040528051906020012081148015611f28575060028451611f249190613911565b8214155b15611f37575f9250505061096d565b604051614c4d60f01b60208201526022016040516020818303038152906040528051906020012081148015611fb65750604051614c4d60f01b60208201526022016040516020818303038152906040528051906020012084836001611f9c9190613b27565b81518110611fac57611fac613930565b6020026020010151145b15611fc5575f9250505061096d565b60405161425560f01b60208201526022016040516020818303038152906040528051906020012081148015612044575060405161425560f01b6020820152602201604051602081830303815290604052805190602001208483600161202a9190613b27565b8151811061203a5761203a613930565b6020026020010151145b15612053575f9250505061096d565b60405161424d60f01b602082015260220160405160208183030381529060405280519060200120811480156120d2575060405161424d60f01b602082015260220160405160208183030381529060405280519060200120848360016120b89190613b27565b815181106120c8576120c8613930565b6020026020010151145b156120e1575f9250505061096d565b60405161214d60f01b60208201526022016040516020818303038152906040528051906020012081148061213a575060405161215560f01b60208201526022016040516020818303038152906040528051906020012081145b8061216b575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012081145b8015612178575060028451115b15612187575f9250505061096d565b60405161313960f11b602082015260220160405160208183030381529060405280519060200120810361230557815f036121c5575f9250505061096d565b600284516121d39190613911565b82146121e3575f9250505061096d565b5f805160206140ec8339815191525c15612201575f9250505061096d565b5f8461220e600185613911565b8151811061221e5761221e613930565b602002602001015190506040516020016122409061525360f01b815260020190565b60405160208183030381529060405280519060200120811415801561228d5750604051632126a0a760e11b6020820152602401604051602081830303815290604052805190602001208114155b80156122c2575060405164424155544f60d81b6020820152602501604051602081830303815290604052805190602001208114155b80156122ee57507f33c4137323016cc703f06d36ae2c7f43f1a70fa030334bd76934bba665c80d6a8114155b156122fe575f935050505061096d565b5050612518565b60405161525360f01b60208201526022016040516020818303038152906040528051906020012081036123c2575f600285516123419190613911565b831490505f60405160200161235e9061313960f11b815260020190565b60405160208183030381529060405280519060200120868560016123829190613b27565b8151811061239257612392613930565b6020026020010151149050811580156123a9575080155b156123ba575f94505050505061096d565b505050612518565b604051632126a0a760e11b602082015260240160405160208183030381529060405280519060200120811480612420575060405164424155544f60d81b60208201526025016040516020818303038152906040528051906020012081145b15612434575f600285516123419190613911565b604051631094919160e21b60208201526024016040516020818303038152906040528051906020012081036124df578115612473575f9250505061096d565b600284516124819190613911565b8214612491575f9250505061096d565b5f805160206140ec8339815191525c156124af575f9250505061096d565b7fb13bd13498ce9409b85a4287d16ff0fcdaca732822a47a97d2bdada3325aa4515c611cbf575f9250505061096d565b7f33c4137323016cc703f06d36ae2c7f43f1a70fa030334bd76934bba665c80d6a8103612516578115612491575f9250505061096d565b505b600101611bf9565b5060019392505050565b604051600560fc1b60208201525f90602101604051602081830303815290604052805190602001208214806125835750604051605760f81b60208201526021016040516020818303038152906040528051906020012082145b806125b3575060405161557760f01b60208201526022016040516020818303038152906040528051906020012082145b156125c657506378b09df560e11b919050565b604051614c4d60f01b60208201526022016040516020818303038152906040528051906020012082148061261f575060405161425560f01b60208201526022016040516020818303038152906040528051906020012082145b8061264f575060405161424d60f01b60208201526022016040516020818303038152906040528051906020012082145b8061267f575060405161214d60f01b60208201526022016040516020818303038152906040528051906020012082145b806126af575060405161215560f01b60208201526022016040516020818303038152906040528051906020012082145b156126c257506378b09df560e11b919050565b604051604160f81b6020820152602101604051602081830303815290604052805190602001208214806127195750604051602960f91b60208201526021016040516020818303038152906040528051906020012082145b806127485750604051605360f81b60208201526021016040516020818303038152906040528051906020012082145b1561275b575063bae6336160e01b919050565b60405162424d6f60e81b6020820152602301604051602081830303815290604052805190602001208214806127b6575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012082145b806127e7575060405162504d6f60e81b60208201526023016040516020818303038152906040528051906020012082145b156127fa57506306c7c2df60e51b919050565b60405161525360f01b602082015260220160405160208183030381529060405280519060200120820361283557506360c09bfb60e01b919050565b604051632126a0a760e11b6020820152602401604051602081830303815290604052805190602001208203612872575063208ed10d60e01b919050565b60405164424155544f60d81b60208201526025016040516020818303038152906040528051906020012082036128b057506354fab89960e01b919050565b604051631094919160e21b60208201526024016040516020818303038152906040528051906020012082036128ed575063e86c200760e01b919050565b60405161313960f11b6020820152602201604051602081830303815290604052805190602001208203612928575063e1e0bc4760e01b919050565b6040516210b13960e91b6020820152602301604051602081830303815290604052805190602001208203612964575063603ea51160e01b919050565b7f33c4137323016cc703f06d36ae2c7f43f1a70fa030334bd76934bba665c80d6a820361299957506307daa05760e21b919050565b60405162461bcd60e51b815260206004820152601860248201527f4f70733a206f70206973206e6f7420737570706f72746564000000000000000060448201526064016103e7565b807f2e445143a211ecbb689de5812742d02a96369c482aec76832fc56490cf3cc6f25d50565b5f5f8051602061410c8339815191528180612a2185612d7c565b60038501546040516397c2cd9160e01b81526001600160401b038f166004820152929450909250612ab0916001600160a01b03909116906397c2cd9190602401602060405180830381865afa158015612a7c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612aa09190613861565b8c612aab858a612eb4565b612fad565b5f81815260018501602052604090205490945015612b1a5760405162461bcd60e51b815260206004820152602160248201527f436f726546616365743a2072657175657374496420616c7265616479207573656044820152601960fa1b60648201526084016103e7565b5f634dc9fb3560e01b85888b8b8e87604051602401612b3e96959493929190613e3e565b60408051601f19818403018152918152602080830180516001600160e01b03166001600160e01b031990951694909417909352600387015481516345d61ded60e01b815291519294506001600160a01b0316926345d61ded926004808401938290030181865afa158015612bb4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612bd89190613846565b6001600160a01b0316635ab83031828d8f876040518563ffffffff1660e01b8152600401612c099493929190613f13565b5f604051808303815f87803b158015612c20575f80fd5b505af1158015612c32573d5f803e3d5ffd5b50505050898781518110612c4857612c48613930565b602002602001015180519060200120846001015f8781526020019081526020015f208190555050505050979650505050505050565b5f805f612c8c87878787613022565b91509150612c99816130df565b5090505b949350505050565b7f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5c80821115612cf25760405163161531e360e21b815260048101839052602481018290526044016103e7565b819003807f7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc5d5050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052612d76908590613228565b50505050565b6060805f83806020019051810190612d949190613f4c565b90505f8160018351612da69190613911565b81518110612db657612db6613930565b602002602001015190505f60018351612dcf9190613911565b6001600160401b03811115612de657612de6613509565b604051908082528060200260200182016040528015612e1957816020015b6060815260200190600190039081612e045790505b5090505f5b60018451612e2c9190613911565b8160ff161015612e8657838160ff1681518110612e4b57612e4b613930565b6020026020010151828260ff1681518110612e6857612e68613930565b60200260200101819052508080612e7e9061406b565b915050612e1e565b508181604051602001612e999190614089565b60405160208183030381529060405294509450505050915091565b5f5f805160206140ec8339815191525c8181612f0b578460018651612ed99190613911565b81518110612ee957612ee9613930565b6020026020010151806020019051810190612f049190613861565b9050612c9d565b50604080517f06b4022e2836acc0aa7bdc5c38c002fa2eab38d450a98c495029a50f34131b7360208083019190915281830186905260608083018590528351808403909101815260808301909352825192019190912060a082018190529060c0016040516020818303038152906040528560018751612f8a9190613911565b81518110612f9a57612f9a613930565b6020026020010181905250949350505050565b604080517f59a1e8b66b6609951241aea6f047c6e9004e4e389ca42bfd172112ab74c3e24b60208083019190915246828401523060608301526001600160401b038516608083015260a0820186905260c08083018590528351808403909101815260e090920190925280519101205f90612c9d565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561305757505f905060036130d6565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156130a8573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b0381166130d0575f600192509250506130d6565b91505f90505b94509492505050565b5f8160048111156130f2576130f26138af565b036130fa5750565b600181600481111561310e5761310e6138af565b0361315b5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016103e7565b600281600481111561316f5761316f6138af565b036131bc5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016103e7565b60038160048111156131d0576131d06138af565b036106c45760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016103e7565b5f61327c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166132fb9092919063ffffffff16565b905080515f148061329c57508080602001905181019061329c91906137f0565b611b025760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016103e7565b6060612c9d84845f85855f80866001600160a01b031685876040516133209190613b9e565b5f6040518083038185875af1925050503d805f811461335a576040519150601f19603f3d011682016040523d82523d5f602084013e61335f565b606091505b50915091506133708783838761337b565b979650505050505050565b606083156133e95782515f036133e2576001600160a01b0385163b6133e25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016103e7565b5081612c9d565b612c9d83838151156133fe5781518083602001fd5b8060405162461bcd60e51b81526004016103e79190613d18565b6001600160a01b03811681146106c4575f80fd5b5f6020828403121561343c575f80fd5b813561344781613418565b9392505050565b6001600160401b03811681146106c4575f80fd5b5f805f60608486031215613474575f80fd5b83356001600160e01b03198116811461348b575f80fd5b92506020840135915060408401356134a28161344e565b809150509250925092565b803560ff811681146134bd575f80fd5b919050565b5f8083601f8401126134d2575f80fd5b5081356001600160401b038111156134e8575f80fd5b6020830191508360208260051b8501011115613502575f80fd5b9250929050565b634e487b7160e01b5f52604160045260245ffd5b604051606081016001600160401b038111828210171561353f5761353f613509565b60405290565b604051601f8201601f191681016001600160401b038111828210171561356d5761356d613509565b604052919050565b5f6001600160401b0382111561358d5761358d613509565b5060051b60200190565b5f6001600160401b038211156135af576135af613509565b50601f01601f191660200190565b5f6135cf6135ca84613597565b613545565b90508281528383830111156135e2575f80fd5b828260208301375f602084830101529392505050565b5f82601f830112613607575f80fd5b613447838335602085016135bd565b5f6136236135ca84613575565b8381529050602080820190600585901b840186811115613641575f80fd5b845b8181101561367a5780356001600160401b03811115613660575f80fd5b61366c898289016135f8565b855250928201928201613643565b505050509392505050565b5f805f805f8060a0878903121561369a575f80fd5b863595506136aa602088016134ad565b945060408701356001600160401b03808211156136c5575f80fd5b6136d18a838b016134c2565b909650945060608901359150808211156136e9575f80fd5b818901915089601f8301126136fc575f80fd5b61370b8a833560208501613616565b93506080890135915080821115613720575f80fd5b5061372d89828a016135f8565b9150509295509295509295565b5f805f805f80868803610160811215613751575f80fd5b87356001600160401b0380821115613767575f80fd5b6137738b838c016134c2565b909950975060208a013591508082111561378b575f80fd5b6137978b838c016134c2565b9097509550859150610100603f19840112156137b1575f80fd5b60408a0194506101408a01359250808311156137cb575f80fd5b505061372d89828a016135f8565b5f602082840312156137e9575f80fd5b5035919050565b5f60208284031215613800575f80fd5b81518015158114613447575f80fd5b60208082526017908201527f436f726546616365743a206d697373696e6720726f6c65000000000000000000604082015260600190565b5f60208284031215613856575f80fd5b815161344781613418565b5f60208284031215613871575f80fd5b5051919050565b60208082526017908201527f436f726546616365743a2077726f6e6720706172616d73000000000000000000604082015260600190565b634e487b7160e01b5f52602160045260245ffd5b83815260608101600384106138e657634e487b7160e01b5f52602160045260245ffd5b83602083015260ff83166040830152949350505050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561096d5761096d6138fd565b5f613447368484613616565b634e487b7160e01b5f52603260045260245ffd5b5f6139516135ca84613575565b80848252602080830192508560051b85013681111561396e575f80fd5b855b818110156139b95780356001600160401b0381111561398d575f80fd5b870136601f82011261399d575f80fd5b6139ab3682358684016135bd565b865250938201938201613970565b50919695505050505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b5f8282518085526020808601955060208260051b840101602086015f5b84811015613a3e57601f19868403018952613a2c8383516139c5565b98840198925090830190600101613a10565b5090979650505050505050565b6020808252825182820152828101516101006040840152805161012084018190525f929182019083906101408601905b80831015613a9b5783518252928401926001929092019190840190613a7b565b5060408701519350601f19925082868203016060870152613abc81856139f3565b93505050606085015181858403016080860152613ad983826139f3565b9250506080850151818584030160a0860152613af583826139c5565b9250505060a0840151613b1f60c08501828051825260208082015190830152604090810151910152565b509392505050565b8082018082111561096d5761096d6138fd565b851515815284602082015283604082015260e060608201525f613b6060e08301856139c5565b83516080840152602084015160a0840152604084015160c084015290509695505050505050565b5f81518060208401855e5f93019283525090919050565b5f6134478284613b87565b5f82601f830112613bb8575f80fd5b8151613bc66135ca82613597565b818152846020838601011115613bda575f80fd5b8160208501602083015e5f918101602001919091529392505050565b5f805f8084860360c0811215613c0a575f80fd5b8551613c158161344e565b60208701519095506001600160401b03811115613c30575f80fd5b613c3c88828901613ba9565b9450506060603f1982011215613c50575f80fd5b50613c5961351d565b6040860151815260608601516020820152608086015160408201528092505060a085015160038110613c89575f80fd5b939692955090935050565b5f808335601e19843603018112613ca9575f80fd5b8301803591506001600160401b03821115613cc2575f80fd5b602001915036819003821315613502575f80fd5b6d021b7b932a330b1b2ba1d1037b8160951b81528183600e83013770081a5cc81b9bdd081cdd5c1c1bdc9d1959607a1b9101600e810191909152601f01919050565b602081525f61344760208301846139c5565b5f613d358286613b87565b838582375f930192835250909392505050565b5f613d538286613b87565b600b60fa1b8152838560018301375f930160010192835250909392505050565b8b81525f6bffffffffffffffffffffffff19808d60601b1660208401528b60348401528a6054840152613da9607484018b613b87565b898152886020820152876040820152818760601b166060820152818660601b166074820152613ddb6088820186613b87565b9f9e505050505050505050505050505050565b5f60208284031215613dfe575f80fd5b613447826134ad565b818382375f9101908152919050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b5f60a08201888352602060ff8916602085015260a060408501528187835260c08501905060c08860051b8601019250885f5b89811015613edc5786850360bf190183528135368c9003601e19018112613e95575f80fd5b8b0184810190356001600160401b03811115613eaf575f80fd5b803603821315613ebd575f80fd5b613ec8878284613e16565b965050509183019190830190600101613e70565b505050508281036060840152613ef281866139f3565b90508281036080840152613f0681856139c5565b9998505050505050505050565b608081525f613f2560808301876139c5565b8560208401526001600160401b0385166040840152828103606084015261337081856139f3565b5f60208284031215613f5c575f80fd5b81516001600160401b0380821115613f72575f80fd5b818401915084601f830112613f85575f80fd5b8151613f936135ca82613575565b8082825260208201915060208360051b860101925087831115613fb4575f80fd5b602085015b8381101561405f57805185811115613fcf575f80fd5b8601603f81018a13613fdf575f80fd5b6020810151613ff06135ca82613575565b81815260059190911b82016040019060208101908c831115614010575f80fd5b604084015b838110156140495780518a81111561402b575f80fd5b61403a8f604083890101613ba9565b84525060209283019201614015565b5086525050602093840193919091019050613fb9565b50979650505050505050565b5f60ff821660ff8103614080576140806138fd565b60010192915050565b5f60208083016020845280855180835260408601915060408160051b8701019250602087015f5b828110156140de57603f198886030184526140cc8583516139f3565b945092850192908501906001016140b0565b509297965050505050505056fe4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f41985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8101f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d81022a2646970667358221220b8ec90dbf2c8bca0886e68288e6cf18706c97019dbca12670d45ede8ba3d9c8664736f6c63430008190033
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.