From cd311fd493ba5f736d39fb1ffed4d8c40442dda1 Mon Sep 17 00:00:00 2001 From: Taylor Webb <84364476+tbwebb22@users.noreply.github.com> Date: Wed, 15 Oct 2025 17:39:46 -0600 Subject: [PATCH 01/47] feat: Hypercorelib (#1137) * HyperCoreLib init commit * add functions for submitting and canceling limit orders * clean up function & variable naming * add tokenInfo getter function * split HyperCoreLib into two libraries * rename helper library * add function for bridging to self on Core * Update natspec and naming * fix natspec * combine libraries into single library with MIT license * make all function camelCase * Make tif order types into enum * check tif against tif.max --- contracts/libraries/HyperCoreLib.sol | 374 +++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 contracts/libraries/HyperCoreLib.sol diff --git a/contracts/libraries/HyperCoreLib.sol b/contracts/libraries/HyperCoreLib.sol new file mode 100644 index 000000000..39f68efbe --- /dev/null +++ b/contracts/libraries/HyperCoreLib.sol @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +interface ICoreWriter { + function sendRawAction(bytes calldata data) external; +} + +library HyperCoreLib { + using SafeERC20 for IERC20; + + // Time-in-Force order types + enum Tif { + None, // invalid + ALO, // Add Liquidity Only + GTC, // Good-Till-Cancel + IOC // Immediate-or-Cancel + } + + struct HyperAssetAmount { + uint256 evm; + uint64 core; + uint64 coreBalanceAssetBridge; + } + + struct SpotBalance { + uint64 total; + uint64 hold; // Unused in this implementation + uint64 entryNtl; // Unused in this implementation + } + + struct TokenInfo { + string name; + uint64[] spots; + uint64 deployerTradingFeeShare; + address deployer; + address evmContract; + uint8 szDecimals; + uint8 weiDecimals; + int8 evmExtraWeiDecimals; + } + + struct CoreUserExists { + bool exists; + } + + // Base asset bridge addresses + address public constant BASE_ASSET_BRIDGE_ADDRESS = 0x2000000000000000000000000000000000000000; + uint256 public constant BASE_ASSET_BRIDGE_ADDRESS_UINT256 = uint256(uint160(BASE_ASSET_BRIDGE_ADDRESS)); + + // Precompile addresses + address public constant SPOT_BALANCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; + address public constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; + address constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C; + address public constant CORE_WRITER_PRECOMPILE_ADDRESS = 0x3333333333333333333333333333333333333333; + + // CoreWriter action headers + bytes4 public constant LIMIT_ORDER_HEADER = 0x01000001; // version=1, action=1 + bytes4 public constant SPOT_SEND_HEADER = 0x01000006; // version=1, action=6 + bytes4 public constant CANCEL_BY_CLOID_HEADER = 0x0100000B; // version=1, action=11 + + // Errors + error LimitPxIsZero(); + error OrderSizeIsZero(); + error InvalidTif(); + error SpotBalancePrecompileCallFailed(); + error CoreUserExistsPrecompileCallFailed(); + error TokenInfoPrecompileCallFailed(); + error TransferAmtExceedsAssetBridgeBalance(uint256 amt, uint256 maxAmt); + + /** + * @notice Transfer `amountEVM` from HyperEVM to `to` on HyperCore. + * @dev Returns the amount credited on Core in Core units (post conversion). + * @param erc20EVMAddress The address of the ERC20 token on HyperEVM + * @param erc20CoreIndex The HyperCore index id of the token to transfer + * @param to The address to receive tokens on HyperCore + * @param amountEVM The amount to transfer on HyperEVM + * @param decimalDiff The decimal difference of evmDecimals - coreDecimals + * @return amountCore The amount credited on Core in Core units (post conversion) + */ + function transferERC20EVMToCore( + address erc20EVMAddress, + uint64 erc20CoreIndex, + address to, + uint256 amountEVM, + int8 decimalDiff + ) internal returns (uint64 amountCore) { + // if the transfer amount exceeds the bridge balance, this wil revert + HyperAssetAmount memory amounts = quoteHyperCoreAmount( + erc20CoreIndex, + decimalDiff, + toAssetBridgeAddress(erc20CoreIndex), + amountEVM + ); + + if (amounts.evm != 0) { + // Transfer the tokens to this contract's address on HyperCore + IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), amounts.evm); + + // Transfer the tokens from this contract on HyperCore to the `to` address on HyperCore + transferERC20CoreToCore(erc20CoreIndex, to, amounts.core); + + return amounts.core; + } + + return 0; + } + + /** + * @notice Bridges `amountEVM` of `erc20` from this address on HyperEVM to this address on HyperCore. + * @dev Returns the amount credited on Core in Core units (post conversion). + * @dev The decimal difference is evmDecimals - coreDecimals + * @param erc20EVMAddress The address of the ERC20 token on HyperEVM + * @param erc20CoreIndex The HyperCore index id of the token to transfer + * @param amountEVM The amount to transfer on HyperEVM + * @param decimalDiff The decimal difference of evmDecimals - coreDecimals + * @return amountCore The amount credited on Core in Core units (post conversion) + */ + function transferERC20EVMToSelfOnCore( + address erc20EVMAddress, + uint64 erc20CoreIndex, + uint256 amountEVM, + int8 decimalDiff + ) internal returns (uint64 amountCore) { + // if the transfer amount exceeds the bridge balance, this wil revert + HyperAssetAmount memory amounts = quoteHyperCoreAmount( + erc20CoreIndex, + decimalDiff, + toAssetBridgeAddress(erc20CoreIndex), + amountEVM + ); + + if (amounts.evm != 0) { + // Transfer the tokens to this contract's address on HyperCore + IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), amounts.evm); + + return amounts.core; + } + + return 0; + } + + /** + * @notice Transfers tokens from this contract on HyperCore to the `to` address on HyperCore + * @param erc20CoreIndex The HyperCore index id of the token + * @param to The address to receive tokens on HyperCore + * @param amountCore The amount to transfer on HyperCore + */ + function transferERC20CoreToCore(uint64 erc20CoreIndex, address to, uint64 amountCore) internal { + bytes memory action = abi.encode(to, erc20CoreIndex, amountCore); + bytes memory payload = abi.encodePacked(SPOT_SEND_HEADER, action); + + ICoreWriter(CORE_WRITER_PRECOMPILE_ADDRESS).sendRawAction(payload); + } + + /** + * @notice Submit a limit order on HyperCore. + * @dev Expects price & size already scaled by 1e8 per HyperCore spec. + * @param asset The asset index of the order + * @param isBuy Whether the order is a buy order + * @param limitPriceX1e8 The limit price of the order scaled by 1e8 + * @param sizeX1e8 The size of the order scaled by 1e8 + * @param reduceOnly If true, only reduce existing position rather than opening a new opposing order + * @param tif Time-in-Force: ALO, GTC, IOC (None invalid) + * @param cloid The client order id of the order, 0 means no cloid + */ + function submitLimitOrder( + uint32 asset, + bool isBuy, + uint64 limitPriceX1e8, + uint64 sizeX1e8, + bool reduceOnly, + Tif tif, + uint128 cloid + ) internal { + // Basic sanity checks + if (limitPriceX1e8 == 0) revert LimitPxIsZero(); + if (sizeX1e8 == 0) revert OrderSizeIsZero(); + if (tif == Tif.None || uint8(tif) > uint8(type(Tif).max)) revert InvalidTif(); + + // Encode the action + bytes memory encodedAction = abi.encode(asset, isBuy, limitPriceX1e8, sizeX1e8, reduceOnly, uint8(tif), cloid); + + // Prefix with the limit-order header + bytes memory data = abi.encodePacked(LIMIT_ORDER_HEADER, encodedAction); + + // Enqueue limit order to HyperCore via CoreWriter precompile + ICoreWriter(CORE_WRITER_PRECOMPILE_ADDRESS).sendRawAction(data); + } + + /** + * @notice Enqueue a cancel-order-by-CLOID for a given asset. + * @param asset The asset index of the order + * @param cloid The client order id of the order + */ + function cancelOrderByCloid(uint32 asset, uint128 cloid) internal { + // Encode the action + bytes memory encodedAction = abi.encode(asset, cloid); + + // Prefix with the cancel-by-cloid header + bytes memory data = abi.encodePacked(CANCEL_BY_CLOID_HEADER, encodedAction); + + // Enqueue cancel order by CLOID to HyperCore via CoreWriter precompile + ICoreWriter(CORE_WRITER_PRECOMPILE_ADDRESS).sendRawAction(data); + } + + /** + * @notice Get the balance of the specified ERC20 for `account` on HyperCore. + * @param account The address of the account to get the balance of + * @param token The token to get the balance of + * @return balance The balance of the specified ERC20 for `account` on HyperCore + */ + function spotBalance(address account, uint64 token) internal view returns (uint64 balance) { + (bool success, bytes memory result) = SPOT_BALANCE_PRECOMPILE_ADDRESS.staticcall(abi.encode(account, token)); + if (!success) revert SpotBalancePrecompileCallFailed(); + SpotBalance memory _spotBalance = abi.decode(result, (SpotBalance)); + return _spotBalance.total; + } + + /** + * @notice Checks if the user exists / has been activated on HyperCore. + * @param user The address of the user to check if they exist on HyperCore + * @return exists True if the user exists on HyperCore, false otherwise + */ + function coreUserExists(address user) internal view returns (bool) { + (bool success, bytes memory result) = CORE_USER_EXISTS_PRECOMPILE_ADDRESS.staticcall(abi.encode(user)); + if (!success) revert CoreUserExistsPrecompileCallFailed(); + CoreUserExists memory _coreUserExists = abi.decode(result, (CoreUserExists)); + return _coreUserExists.exists; + } + + /** + * @notice Get the info of the specified token on HyperCore. + * @param erc20CoreIndex The token to get the info of + * @return tokenInfo The info of the specified token on HyperCore + */ + function tokenInfo(uint32 erc20CoreIndex) internal view returns (TokenInfo memory) { + (bool success, bytes memory result) = TOKEN_INFO_PRECOMPILE_ADDRESS.staticcall(abi.encode(erc20CoreIndex)); + if (!success) revert TokenInfoPrecompileCallFailed(); + TokenInfo memory _tokenInfo = abi.decode(result, (TokenInfo)); + return _tokenInfo; + } + + /** + * @notice Quotes the conversion of evm tokens to hypercore tokens + * @param erc20CoreIndex The HyperCore index id of the token to transfer + * @param decimalDiff The decimal difference of evmDecimals - coreDecimals + * @param bridgeAddress The asset bridge address of the token to transfer + * @param amountEVM The number of tokens that (pre-dusted) that we are trying to send + * @return HyperAssetAmount The amount of tokens to send to HyperCore (scaled on evm), + * dust (to be refunded), and the swap amount (of the tokens scaled on hypercore) + */ + function quoteHyperCoreAmount( + uint64 erc20CoreIndex, + int8 decimalDiff, + address bridgeAddress, + uint256 amountEVM + ) internal view returns (HyperAssetAmount memory) { + return toHyperAssetAmount(amountEVM, spotBalance(bridgeAddress, erc20CoreIndex), decimalDiff); + } + + /** + * @notice Converts a core index id to an asset bridge address + * @param erc20CoreIndex The core token index id to convert + * @return assetBridgeAddress The asset bridge address + */ + function toAssetBridgeAddress(uint64 erc20CoreIndex) internal pure returns (address) { + return address(uint160(BASE_ASSET_BRIDGE_ADDRESS_UINT256 + erc20CoreIndex)); + } + + /** + * @notice Converts an asset bridge address to a core index id + * @param assetBridgeAddress The asset bridge address to convert + * @return erc20CoreIndex The core token index id + */ + function toTokenId(address assetBridgeAddress) internal pure returns (uint64) { + return uint64(uint160(assetBridgeAddress) - BASE_ASSET_BRIDGE_ADDRESS_UINT256); + } + + /** + * @notice Converts an amount and an asset to a evm amount and core amount + * @param amountEVMPreDusted The amount to convert + * @param assetBridgeSupplyCore The maximum amount transferable capped by the number of tokens located on the HyperCore's side of the asset bridge + * @param decimalDiff The decimal difference of evmDecimals - coreDecimals + * @return HyperAssetAmount The evm amount and core amount + */ + function toHyperAssetAmount( + uint256 amountEVMPreDusted, + uint64 assetBridgeSupplyCore, + int8 decimalDiff + ) internal pure returns (HyperAssetAmount memory) { + uint256 amountEVM; + uint64 amountCore; + + /// @dev HyperLiquid decimal conversion: Scale EVM (u256,evmDecimals) -> Core (u64,coreDecimals) + /// @dev Core amount is guaranteed to be within u64 range. + if (decimalDiff > 0) { + (amountEVM, amountCore) = toHyperAssetAmountDecimalDifferenceGtZero( + amountEVMPreDusted, + assetBridgeSupplyCore, + uint8(decimalDiff) + ); + } else { + (amountEVM, amountCore) = toHyperAssetAmountDecimalDifferenceLeqZero( + amountEVMPreDusted, + assetBridgeSupplyCore, + uint8(-1 * decimalDiff) + ); + } + + return HyperAssetAmount({ evm: amountEVM, core: amountCore, coreBalanceAssetBridge: assetBridgeSupplyCore }); + } + + /** + * @notice Computes hyperAssetAmount when EVM decimals > Core decimals + * @notice Reverts if the transfers amount exceeds the asset bridge balance + * @param amountEVMPreDusted The amount to convert + * @param maxTransferableAmountCore The maximum transferrable amount capped by the asset bridge has range [0,u64.max] + * @param decimalDiff The decimal difference between HyperEVM and HyperCore + * @return amountEVM The EVM amount + * @return amountCore The core amount + */ + function toHyperAssetAmountDecimalDifferenceGtZero( + uint256 amountEVMPreDusted, + uint64 maxTransferableAmountCore, + uint8 decimalDiff + ) internal pure returns (uint256 amountEVM, uint64 amountCore) { + uint256 scale = 10 ** decimalDiff; + uint256 maxTransferableAmountEVM = maxTransferableAmountCore * scale; + + unchecked { + /// @dev Strip out dust from _amount so that _amount and maxEvmAmountFromCoreMax have a maximum of _decimalDiff starting 0s + amountEVM = amountEVMPreDusted - (amountEVMPreDusted % scale); // Safe: dustAmount = amountEVMPreDusted % scale, so dust <= amountEVMPreDusted + + if (amountEVM > maxTransferableAmountEVM) + revert TransferAmtExceedsAssetBridgeBalance(amountEVM, maxTransferableAmountEVM); + + /// @dev Safe: Guaranteed to be in the range of [0, u64.max] because it is upperbounded by uint64 maxAmt + amountCore = uint64(amountEVM / scale); + } + } + + /** + * @notice Computes hyperAssetAmount when EVM decimals < Core decimals and 0 + * @notice Reverts if the transfers amount exceeds the asset bridge balance + * @param amountEVMPreDusted The amount to convert + * @param maxTransferableAmountCore The maximum transferrable amount capped by the asset bridge + * @param decimalDiff The decimal difference between HyperEVM and HyperCore + * @return amountEVM The EVM amount + * @return amountCore The core amount + */ + function toHyperAssetAmountDecimalDifferenceLeqZero( + uint256 amountEVMPreDusted, + uint64 maxTransferableAmountCore, + uint8 decimalDiff + ) internal pure returns (uint256 amountEVM, uint64 amountCore) { + uint256 scale = 10 ** decimalDiff; + uint256 maxTransferableAmountEVM = maxTransferableAmountCore / scale; + + unchecked { + amountEVM = amountEVMPreDusted; + + /// @dev When `Core > EVM` there will be no opening dust to strip out since all tokens in evm can be represented on core + /// @dev Safe: Bound amountEvm to the range of [0, evmscaled u64.max] + if (amountEVMPreDusted > maxTransferableAmountEVM) + revert TransferAmtExceedsAssetBridgeBalance(amountEVM, maxTransferableAmountEVM); + + /// @dev Safe: Guaranteed to be in the range of [0, u64.max] because it is upperbounded by uint64 maxAmt + amountCore = uint64(amountEVM * scale); + } + } +} From 54607f350d2be0a59a0d39f74ab8928a0255f717 Mon Sep 17 00:00:00 2001 From: Taylor Webb <84364476+tbwebb22@users.noreply.github.com> Date: Thu, 16 Oct 2025 21:12:05 -0600 Subject: [PATCH 02/47] feat: Hypercorelib - clean up decimal conversion functions (#1139) * HyperCoreLib init commit * add functions for submitting and canceling limit orders * clean up function & variable naming * add tokenInfo getter function * split HyperCoreLib into two libraries * rename helper library * add function for bridging to self on Core * Update natspec and naming * fix natspec * combine libraries into single library with MIT license * make all function camelCase * Make tif order types into enum * check tif against tif.max * add spotPx function * add minimumCoreAmountsToAmounts * clean up decimal conversion functions --- contracts/libraries/HyperCoreLib.sol | 208 ++++++++++----------------- 1 file changed, 77 insertions(+), 131 deletions(-) diff --git a/contracts/libraries/HyperCoreLib.sol b/contracts/libraries/HyperCoreLib.sol index 39f68efbe..eecbf94b0 100644 --- a/contracts/libraries/HyperCoreLib.sol +++ b/contracts/libraries/HyperCoreLib.sol @@ -19,12 +19,6 @@ library HyperCoreLib { IOC // Immediate-or-Cancel } - struct HyperAssetAmount { - uint256 evm; - uint64 core; - uint64 coreBalanceAssetBridge; - } - struct SpotBalance { uint64 total; uint64 hold; // Unused in this implementation @@ -52,8 +46,9 @@ library HyperCoreLib { // Precompile addresses address public constant SPOT_BALANCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; + address public constant SPOT_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000808; address public constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; - address constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C; + address public constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C; address public constant CORE_WRITER_PRECOMPILE_ADDRESS = 0x3333333333333333333333333333333333333333; // CoreWriter action headers @@ -68,6 +63,7 @@ library HyperCoreLib { error SpotBalancePrecompileCallFailed(); error CoreUserExistsPrecompileCallFailed(); error TokenInfoPrecompileCallFailed(); + error SpotPxPrecompileCallFailed(); error TransferAmtExceedsAssetBridgeBalance(uint256 amt, uint256 maxAmt); /** @@ -78,7 +74,8 @@ library HyperCoreLib { * @param to The address to receive tokens on HyperCore * @param amountEVM The amount to transfer on HyperEVM * @param decimalDiff The decimal difference of evmDecimals - coreDecimals - * @return amountCore The amount credited on Core in Core units (post conversion) + * @return amountEVMSent The amount sent on HyperEVM + * @return amountCoreToReceive The amount credited on Core in Core units (post conversion) */ function transferERC20EVMToCore( address erc20EVMAddress, @@ -86,26 +83,19 @@ library HyperCoreLib { address to, uint256 amountEVM, int8 decimalDiff - ) internal returns (uint64 amountCore) { + ) internal returns (uint256 amountEVMSent, uint64 amountCoreToReceive) { // if the transfer amount exceeds the bridge balance, this wil revert - HyperAssetAmount memory amounts = quoteHyperCoreAmount( - erc20CoreIndex, - decimalDiff, - toAssetBridgeAddress(erc20CoreIndex), - amountEVM - ); - - if (amounts.evm != 0) { + (uint256 _amountEVMToSend, uint64 _amountCoreToReceive) = maximumEVMSendAmountToAmounts(amountEVM, decimalDiff); + + if (_amountEVMToSend != 0) { // Transfer the tokens to this contract's address on HyperCore - IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), amounts.evm); + IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), _amountEVMToSend); // Transfer the tokens from this contract on HyperCore to the `to` address on HyperCore - transferERC20CoreToCore(erc20CoreIndex, to, amounts.core); - - return amounts.core; + transferERC20CoreToCore(erc20CoreIndex, to, _amountCoreToReceive); } - return 0; + return (_amountEVMToSend, _amountCoreToReceive); } /** @@ -116,30 +106,23 @@ library HyperCoreLib { * @param erc20CoreIndex The HyperCore index id of the token to transfer * @param amountEVM The amount to transfer on HyperEVM * @param decimalDiff The decimal difference of evmDecimals - coreDecimals - * @return amountCore The amount credited on Core in Core units (post conversion) + * @return amountEVMSent The amount sent on HyperEVM + * @return amountCoreToReceive The amount credited on Core in Core units (post conversion) */ function transferERC20EVMToSelfOnCore( address erc20EVMAddress, uint64 erc20CoreIndex, uint256 amountEVM, int8 decimalDiff - ) internal returns (uint64 amountCore) { - // if the transfer amount exceeds the bridge balance, this wil revert - HyperAssetAmount memory amounts = quoteHyperCoreAmount( - erc20CoreIndex, - decimalDiff, - toAssetBridgeAddress(erc20CoreIndex), - amountEVM - ); - - if (amounts.evm != 0) { - // Transfer the tokens to this contract's address on HyperCore - IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), amounts.evm); + ) internal returns (uint256 amountEVMSent, uint64 amountCoreToReceive) { + (uint256 _amountEVMToSend, uint64 _amountCoreToReceive) = maximumEVMSendAmountToAmounts(amountEVM, decimalDiff); - return amounts.core; + if (_amountEVMToSend != 0) { + // Transfer the tokens to this contract's address on HyperCore + IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), _amountEVMToSend); } - return 0; + return (_amountEVMToSend, _amountCoreToReceive); } /** @@ -231,6 +214,17 @@ library HyperCoreLib { return _coreUserExists.exists; } + /** + * @notice Get the spot price of the specified asset on HyperCore. + * @param index The asset index to get the spot price of + * @return spotPx The spot price of the specified asset on HyperCore scaled by 1e8 + */ + function spotPx(uint32 index) external view returns (uint64) { + (bool success, bytes memory result) = SPOT_PX_PRECOMPILE_ADDRESS.staticcall(abi.encode(index)); + if (!success) revert SpotPxPrecompileCallFailed(); + return abi.decode(result, (uint64)); + } + /** * @notice Get the info of the specified token on HyperCore. * @param erc20CoreIndex The token to get the info of @@ -243,24 +237,6 @@ library HyperCoreLib { return _tokenInfo; } - /** - * @notice Quotes the conversion of evm tokens to hypercore tokens - * @param erc20CoreIndex The HyperCore index id of the token to transfer - * @param decimalDiff The decimal difference of evmDecimals - coreDecimals - * @param bridgeAddress The asset bridge address of the token to transfer - * @param amountEVM The number of tokens that (pre-dusted) that we are trying to send - * @return HyperAssetAmount The amount of tokens to send to HyperCore (scaled on evm), - * dust (to be refunded), and the swap amount (of the tokens scaled on hypercore) - */ - function quoteHyperCoreAmount( - uint64 erc20CoreIndex, - int8 decimalDiff, - address bridgeAddress, - uint256 amountEVM - ) internal view returns (HyperAssetAmount memory) { - return toHyperAssetAmount(amountEVM, spotBalance(bridgeAddress, erc20CoreIndex), decimalDiff); - } - /** * @notice Converts a core index id to an asset bridge address * @param erc20CoreIndex The core token index id to convert @@ -280,95 +256,65 @@ library HyperCoreLib { } /** - * @notice Converts an amount and an asset to a evm amount and core amount - * @param amountEVMPreDusted The amount to convert - * @param assetBridgeSupplyCore The maximum amount transferable capped by the number of tokens located on the HyperCore's side of the asset bridge + * @notice Returns an amount to send on HyperEVM to receive AT LEAST the minimumCoreReceiveAmount on HyperCore + * @param minimumCoreReceiveAmount The minimum amount desired to receive on HyperCore * @param decimalDiff The decimal difference of evmDecimals - coreDecimals - * @return HyperAssetAmount The evm amount and core amount + * @return amountEVMToSend The amount to send on HyperEVM to receive at least minimumCoreReceiveAmount on HyperCore + * @return amountCoreToReceive The amount that will be received on core if the amountEVMToSend is sent from HyperEVM */ - function toHyperAssetAmount( - uint256 amountEVMPreDusted, - uint64 assetBridgeSupplyCore, + function minimumCoreReceiveAmountToAmounts( + uint64 minimumCoreReceiveAmount, int8 decimalDiff - ) internal pure returns (HyperAssetAmount memory) { - uint256 amountEVM; - uint64 amountCore; - - /// @dev HyperLiquid decimal conversion: Scale EVM (u256,evmDecimals) -> Core (u64,coreDecimals) - /// @dev Core amount is guaranteed to be within u64 range. - if (decimalDiff > 0) { - (amountEVM, amountCore) = toHyperAssetAmountDecimalDifferenceGtZero( - amountEVMPreDusted, - assetBridgeSupplyCore, - uint8(decimalDiff) - ); + ) internal pure returns (uint256 amountEVMToSend, uint64 amountCoreToReceive) { + if (decimalDiff == 0) { + // Same decimals between HyperEVM and HyperCore + amountEVMToSend = uint256(minimumCoreReceiveAmount); + amountCoreToReceive = minimumCoreReceiveAmount; + } else if (decimalDiff > 0) { + // EVM token has more decimals than Core + // Scale up to represent the same value in higher-precision EVM units + amountEVMToSend = uint256(minimumCoreReceiveAmount) * (10 ** uint8(decimalDiff)); + amountCoreToReceive = minimumCoreReceiveAmount; } else { - (amountEVM, amountCore) = toHyperAssetAmountDecimalDifferenceLeqZero( - amountEVMPreDusted, - assetBridgeSupplyCore, - uint8(-1 * decimalDiff) - ); + // Core token has more decimals than EVM + // Scale down, rounding UP to avoid shortfall on Core + uint256 scaleDivisor = 10 ** uint8(-decimalDiff); + amountEVMToSend = (uint256(minimumCoreReceiveAmount) + scaleDivisor - 1) / scaleDivisor; // ceil division + amountCoreToReceive = uint64(amountEVMToSend * scaleDivisor); } - - return HyperAssetAmount({ evm: amountEVM, core: amountCore, coreBalanceAssetBridge: assetBridgeSupplyCore }); } /** - * @notice Computes hyperAssetAmount when EVM decimals > Core decimals - * @notice Reverts if the transfers amount exceeds the asset bridge balance - * @param amountEVMPreDusted The amount to convert - * @param maxTransferableAmountCore The maximum transferrable amount capped by the asset bridge has range [0,u64.max] - * @param decimalDiff The decimal difference between HyperEVM and HyperCore - * @return amountEVM The EVM amount - * @return amountCore The core amount + * @notice Converts a maximum EVM amount to send into an EVM amount to send to avoid loss to dust, + * @notice and the corresponding amount that will be recieved on Core. + * @param maximumEVMSendAmount The maximum amount to send on HyperEVM + * @param decimalDiff The decimal difference of evmDecimals - coreDecimals + * @return amountEVMToSend The amount to send on HyperEVM + * @return amountCoreToReceive The amount that will be received on HyperCore if the amountEVMToSend is sent */ - function toHyperAssetAmountDecimalDifferenceGtZero( - uint256 amountEVMPreDusted, - uint64 maxTransferableAmountCore, - uint8 decimalDiff - ) internal pure returns (uint256 amountEVM, uint64 amountCore) { - uint256 scale = 10 ** decimalDiff; - uint256 maxTransferableAmountEVM = maxTransferableAmountCore * scale; - - unchecked { - /// @dev Strip out dust from _amount so that _amount and maxEvmAmountFromCoreMax have a maximum of _decimalDiff starting 0s - amountEVM = amountEVMPreDusted - (amountEVMPreDusted % scale); // Safe: dustAmount = amountEVMPreDusted % scale, so dust <= amountEVMPreDusted - - if (amountEVM > maxTransferableAmountEVM) - revert TransferAmtExceedsAssetBridgeBalance(amountEVM, maxTransferableAmountEVM); + function maximumEVMSendAmountToAmounts( + uint256 maximumEVMSendAmount, + int8 decimalDiff + ) internal pure returns (uint256 amountEVMToSend, uint64 amountCoreToReceive) { + /// @dev HyperLiquid decimal conversion: Scale EVM (u256,evmDecimals) -> Core (u64,coreDecimals) + /// @dev Core amount is guaranteed to be within u64 range. + if (decimalDiff == 0) { + amountEVMToSend = maximumEVMSendAmount; + amountCoreToReceive = uint64(amountEVMToSend); + } else if (decimalDiff > 0) { + // EVM token has more decimals than Core + uint256 scale = 10 ** uint8(decimalDiff); + amountEVMToSend = maximumEVMSendAmount - (maximumEVMSendAmount % scale); // Safe: dustAmount = maximumEVMSendAmount % scale, so dust <= maximumEVMSendAmount /// @dev Safe: Guaranteed to be in the range of [0, u64.max] because it is upperbounded by uint64 maxAmt - amountCore = uint64(amountEVM / scale); - } - } - - /** - * @notice Computes hyperAssetAmount when EVM decimals < Core decimals and 0 - * @notice Reverts if the transfers amount exceeds the asset bridge balance - * @param amountEVMPreDusted The amount to convert - * @param maxTransferableAmountCore The maximum transferrable amount capped by the asset bridge - * @param decimalDiff The decimal difference between HyperEVM and HyperCore - * @return amountEVM The EVM amount - * @return amountCore The core amount - */ - function toHyperAssetAmountDecimalDifferenceLeqZero( - uint256 amountEVMPreDusted, - uint64 maxTransferableAmountCore, - uint8 decimalDiff - ) internal pure returns (uint256 amountEVM, uint64 amountCore) { - uint256 scale = 10 ** decimalDiff; - uint256 maxTransferableAmountEVM = maxTransferableAmountCore / scale; - - unchecked { - amountEVM = amountEVMPreDusted; - - /// @dev When `Core > EVM` there will be no opening dust to strip out since all tokens in evm can be represented on core - /// @dev Safe: Bound amountEvm to the range of [0, evmscaled u64.max] - if (amountEVMPreDusted > maxTransferableAmountEVM) - revert TransferAmtExceedsAssetBridgeBalance(amountEVM, maxTransferableAmountEVM); + amountCoreToReceive = uint64(amountEVMToSend / scale); + } else { + // Core token has more decimals than EVM + uint256 scale = 10 ** uint8(-1 * decimalDiff); + amountEVMToSend = maximumEVMSendAmount; /// @dev Safe: Guaranteed to be in the range of [0, u64.max] because it is upperbounded by uint64 maxAmt - amountCore = uint64(amountEVM * scale); + amountCoreToReceive = uint64(amountEVMToSend * scale); } } } From b4450f655df1166e73cfeeea1af8c329c179d85f Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Fri, 17 Oct 2025 16:37:07 -0400 Subject: [PATCH 03/47] feat: OP Adapter update (#1132) * feat: OP Adapter update Signed-off-by: Faisal Usmani * Undo the branch logic Signed-off-by: Faisal Usmani * revert formatting Signed-off-by: Faisal Usmani * Added tests Signed-off-by: Faisal Usmani * Fixed test Signed-off-by: Faisal Usmani --------- Signed-off-by: Faisal Usmani --- contracts/chain-adapters/OP_Adapter.sol | 35 ++++--- test/evm/foundry/local/OP_Adapter.t.sol | 95 +++++++++++++++++++ test/evm/hardhat/chain-adapters/OP_Adapter.ts | 6 +- 3 files changed, 121 insertions(+), 15 deletions(-) create mode 100644 test/evm/foundry/local/OP_Adapter.t.sol diff --git a/contracts/chain-adapters/OP_Adapter.sol b/contracts/chain-adapters/OP_Adapter.sol index d33817649..9261e07f1 100644 --- a/contracts/chain-adapters/OP_Adapter.sol +++ b/contracts/chain-adapters/OP_Adapter.sol @@ -35,31 +35,43 @@ contract OP_Adapter is CrossDomainEnabled, AdapterInterface, CircleCCTPAdapter { IL1StandardBridge public immutable L1_STANDARD_BRIDGE; IOpUSDCBridgeAdapter public immutable L1_OP_USDC_BRIDGE; + error InvalidBridgeConfig(); + /** * @notice Constructs new Adapter. * @param _l1Weth WETH address on L1. + * @param _l1Usdc USDC address on L1. * @param _crossDomainMessenger XDomainMessenger Destination chain system contract. * @param _l1StandardBridge Standard bridge contract. - * @param _l1Usdc USDC address on L1. + * @param _l1USDCBridge OP USDC bridge contract. + * @param _cctpTokenMessenger CCTP token messenger contract. + * @param _recipientCircleDomainId Circle domain ID of the destination chain. */ constructor( WETH9Interface _l1Weth, IERC20 _l1Usdc, address _crossDomainMessenger, IL1StandardBridge _l1StandardBridge, - IOpUSDCBridgeAdapter _l1USDCBridge + IOpUSDCBridgeAdapter _l1USDCBridge, + ITokenMessenger _cctpTokenMessenger, + uint32 _recipientCircleDomainId ) CrossDomainEnabled(_crossDomainMessenger) - CircleCCTPAdapter( - _l1Usdc, - // Hardcode cctp messenger to 0x0 to disable CCTP bridging. - ITokenMessenger(address(0)), - CircleDomainIds.UNINITIALIZED - ) + CircleCCTPAdapter(_l1Usdc, _cctpTokenMessenger, _recipientCircleDomainId) { L1_WETH = _l1Weth; L1_STANDARD_BRIDGE = _l1StandardBridge; L1_OP_USDC_BRIDGE = _l1USDCBridge; + + address zero = address(0); + if (address(_l1Usdc) != zero) { + bool opUSDCBridgeDisabled = address(_l1USDCBridge) == zero; + bool cctpUSDCBridgeDisabled = address(_cctpTokenMessenger) == zero; + // Bridged and Native USDC are mutually exclusive. + if (opUSDCBridgeDisabled == cctpUSDCBridgeDisabled) { + revert InvalidBridgeConfig(); + } + } } /** @@ -79,12 +91,7 @@ contract OP_Adapter is CrossDomainEnabled, AdapterInterface, CircleCCTPAdapter { * @param amount Amount of L1 tokens to deposit and L2 tokens to receive. * @param to Bridge recipient. */ - function relayTokens( - address l1Token, - address l2Token, - uint256 amount, - address to - ) external payable override { + function relayTokens(address l1Token, address l2Token, uint256 amount, address to) external payable override { // If the l1Token is weth then unwrap it to ETH then send the ETH to the standard bridge. if (l1Token == address(L1_WETH)) { L1_WETH.withdraw(amount); diff --git a/test/evm/foundry/local/OP_Adapter.t.sol b/test/evm/foundry/local/OP_Adapter.t.sol new file mode 100644 index 000000000..e2edfada9 --- /dev/null +++ b/test/evm/foundry/local/OP_Adapter.t.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; + +import { ERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { IL1StandardBridge } from "@eth-optimism/contracts/L1/messaging/IL1StandardBridge.sol"; +import { IOpUSDCBridgeAdapter } from "../../../../contracts/external/interfaces/IOpUSDCBridgeAdapter.sol"; +import { ITokenMessenger } from "../../../../contracts/external/interfaces/CCTPInterfaces.sol"; + +import { OP_Adapter } from "../../../../contracts/chain-adapters/OP_Adapter.sol"; +import { WETH9Interface } from "../../../../contracts/external/interfaces/WETH9Interface.sol"; +import { WETH9 } from "../../../../contracts/external/WETH9.sol"; + +contract OP_AdapterTest is Test { + ERC20 l1Usdc; + WETH9 l1Weth; + + IL1StandardBridge standardBridge; + IOpUSDCBridgeAdapter opUSDCBridge; + ITokenMessenger cctpMessenger; + + uint32 constant RECIPIENT_CIRCLE_DOMAIN_ID = 1; + + function setUp() public { + l1Usdc = new ERC20("l1Usdc", "l1Usdc"); + l1Weth = new WETH9(); + + standardBridge = IL1StandardBridge(makeAddr("standardBridge")); + opUSDCBridge = IOpUSDCBridgeAdapter(makeAddr("opUSDCBridge")); + cctpMessenger = ITokenMessenger(makeAddr("cctpMessenger")); + } + + function testUSDCNotSet() public { + new OP_Adapter( + WETH9Interface(address(l1Weth)), + IERC20(address(0)), + address(0), + standardBridge, + IOpUSDCBridgeAdapter(address(0)), + ITokenMessenger(address(0)), + RECIPIENT_CIRCLE_DOMAIN_ID + ); + } + + function testL1UsdcBridgeSet() public { + new OP_Adapter( + WETH9Interface(address(l1Weth)), + IERC20(address(l1Usdc)), + address(0), + standardBridge, + opUSDCBridge, + ITokenMessenger(address(0)), + RECIPIENT_CIRCLE_DOMAIN_ID + ); + } + + function testCctpMessengerSet() public { + new OP_Adapter( + WETH9Interface(address(l1Weth)), + IERC20(address(0)), + address(0), + standardBridge, + IOpUSDCBridgeAdapter(address(0)), + cctpMessenger, + RECIPIENT_CIRCLE_DOMAIN_ID + ); + } + + function testNeitherSet() public { + vm.expectRevert(OP_Adapter.InvalidBridgeConfig.selector); + new OP_Adapter( + WETH9Interface(address(l1Weth)), + IERC20(address(l1Usdc)), + address(0), + standardBridge, + IOpUSDCBridgeAdapter(address(0)), + ITokenMessenger(address(0)), + RECIPIENT_CIRCLE_DOMAIN_ID + ); + } + + function testBothSet() public { + vm.expectRevert(OP_Adapter.InvalidBridgeConfig.selector); + new OP_Adapter( + WETH9Interface(address(l1Weth)), + IERC20(address(l1Usdc)), + address(0), + standardBridge, + opUSDCBridge, + cctpMessenger, + RECIPIENT_CIRCLE_DOMAIN_ID + ); + } +} diff --git a/test/evm/hardhat/chain-adapters/OP_Adapter.ts b/test/evm/hardhat/chain-adapters/OP_Adapter.ts index c5e2778bd..e3b612ed5 100644 --- a/test/evm/hardhat/chain-adapters/OP_Adapter.ts +++ b/test/evm/hardhat/chain-adapters/OP_Adapter.ts @@ -12,9 +12,11 @@ import { toWei, getContractFactory, seedWallet, + toBN, } from "../../../../utils/utils"; import { hubPoolFixture, enableTokensForLP } from "../fixtures/HubPool.Fixture"; import { constructSingleChainTree } from "../MerkleLib.utils"; +import { ZERO_ADDRESS } from "@uma/common"; let hubPool: Contract, adapter: Contract, weth: Contract, usdc: Contract, mockSpoke: Contract, timer: Contract; let l2Weth: string, l2Usdc: string; @@ -63,7 +65,9 @@ describe("OP Adapter", function () { usdc.address, l1CrossDomainMessenger.address, l1StandardBridge.address, - opUSDCBridge.address + opUSDCBridge.address, + ZERO_ADDRESS, + toBN("322") ); // Seed the HubPool some funds so it can send L1->L2 messages. await hubPool.connect(liquidityProvider).loadEthForL2Calls({ value: toWei("1") }); From 7ae992f464d8b5ac9d912930931da6444fa00900 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Fri, 17 Oct 2025 16:37:14 -0400 Subject: [PATCH 04/47] feat: eraVM Spoke Pool 7702 Handling (#1122) * feat: eraVM Spoke Pool upgrade Signed-off-by: Faisal Usmani * Added tests Signed-off-by: Faisal Usmani --------- Signed-off-by: Faisal Usmani --- contracts/SpokePool.sol | 2 +- contracts/ZkSync_SpokePool.sol | 14 +- foundry.toml | 3 + hardhat.config.ts | 24 ++- test/evm/foundry/local/eraVM_EIP7702.sol | 208 +++++++++++++++++++++++ 5 files changed, 236 insertions(+), 15 deletions(-) create mode 100644 test/evm/foundry/local/eraVM_EIP7702.sol diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index b9656ddb2..6c7f99fde 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -1637,7 +1637,7 @@ abstract contract SpokePool is * @param account The address to check. * @return True if the address is a 7702 delegated wallet, false otherwise. */ - function _is7702DelegatedWallet(address account) internal view returns (bool) { + function _is7702DelegatedWallet(address account) internal view virtual returns (bool) { return bytes3(account.code) == EIP7702_PREFIX; } diff --git a/contracts/ZkSync_SpokePool.sol b/contracts/ZkSync_SpokePool.sol index 83c51a633..5b6ec56aa 100644 --- a/contracts/ZkSync_SpokePool.sol +++ b/contracts/ZkSync_SpokePool.sol @@ -8,11 +8,7 @@ import "./SpokePool.sol"; // https://github.com/matter-labs/era-contracts/blob/6391c0d7bf6184d7f6718060e3991ba6f0efe4a7/zksync/contracts/bridge/L2ERC20Bridge.sol#L104 interface ZkBridgeLike { - function withdraw( - address _l1Receiver, - address _l2Token, - uint256 _amount - ) external; + function withdraw(address _l1Receiver, address _l2Token, uint256 _amount) external; } interface IL2ETH { @@ -131,6 +127,14 @@ contract ZkSync_SpokePool is SpokePool, CircleCCTPAdapter { * INTERNAL FUNCTIONS * **************************************/ + /** + * @notice Checks if an address is a 7702 delegated wallet (EOA with delegated code). + * @return False Since eraVM does not support 7702 delegated wallets, this function always returns false. + */ + function _is7702DelegatedWallet(address) internal pure override returns (bool) { + return false; + } + /** * @notice Wraps any ETH into WETH before executing base function. This is necessary because SpokePool receives * ETH over the canonical token bridge instead of WETH. diff --git a/foundry.toml b/foundry.toml index d5d2b5e6c..6a8bfe380 100644 --- a/foundry.toml +++ b/foundry.toml @@ -35,6 +35,9 @@ fs_permissions = [{ access = "read", path = "./"}] solc = "0.8.23" evm_version = "prague" +[profile.zksync] +src = "contracts/Lens_SpokePool.sol" + [profile.zksync.zksync] compile = true fallback_oz = true diff --git a/hardhat.config.ts b/hardhat.config.ts index 52d3f89be..64e30d745 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -124,7 +124,13 @@ const config: HardhatUserConfig = { enabled: true, }, suppressedErrors: ["sendtransfer"], - contractsToCompile: ["SpokePoolPeriphery", "MulticallHandler", "SpokePoolVerifier"], + contractsToCompile: [ + "SpokePoolPeriphery", + "MulticallHandler", + "SpokePoolVerifier", + "ZkSync_SpokePool", + "Lens_SpokePool", + ], }, }, networks: { @@ -240,6 +246,14 @@ const config: HardhatUserConfig = { browserURL: "https://era.zksync.network/", }, }, + { + network: "lens", + chainId: CHAIN_IDs.LENS, + urls: { + apiURL: "https://verify.lens.xyz/contract_verification", + browserURL: "https://explorer.lens.xyz/", + }, + }, ], }, blockscout: { @@ -261,14 +275,6 @@ const config: HardhatUserConfig = { browserURL: "https://explorer.inkonchain.com", }, }, - { - network: "lens", - chainId: CHAIN_IDs.LENS, - urls: { - apiURL: "https://verify.lens.xyz/contract_verification", - browserURL: "https://explorer.lens.xyz/", - }, - }, { network: "lisk", chainId: CHAIN_IDs.LISK, diff --git a/test/evm/foundry/local/eraVM_EIP7702.sol b/test/evm/foundry/local/eraVM_EIP7702.sol new file mode 100644 index 000000000..26017f0a5 --- /dev/null +++ b/test/evm/foundry/local/eraVM_EIP7702.sol @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; +import { ZkSync_SpokePool, ZkBridgeLike, IERC20, ITokenMessenger } from "../../../../contracts/ZkSync_SpokePool.sol"; +import { WETH9 } from "../../../../contracts/external/WETH9.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import { AddressToBytes32, Bytes32ToAddress } from "../../../../contracts/libraries/AddressConverters.sol"; +import { V3SpokePoolInterface } from "../../../../contracts/interfaces/V3SpokePoolInterface.sol"; + +// Simple mock contracts for testing +contract SimpleContract { + function doNothing() external pure returns (uint256) { + return 42; + } +} + +// Extension of ZkSync_SpokePool to expose internal functions for testing +contract TestableMockSpokePool is ZkSync_SpokePool { + constructor( + address _wrappedNativeTokenAddress + ) + ZkSync_SpokePool( + _wrappedNativeTokenAddress, + IERC20(address(0)), + ZkBridgeLike(address(0)), + ITokenMessenger(address(0)), + 1 hours, + 9 hours + ) + {} + + function test_unwrapwrappedNativeTokenTo(address payable to, uint256 amount) external { + _unwrapwrappedNativeTokenTo(to, amount); + } + + function test_is7702DelegatedWallet(address account) external view returns (bool) { + return _is7702DelegatedWallet(account); + } + + function test_fillRelayV3(V3RelayExecutionParams memory relayExecution, bytes32 relayer, bool isSlowFill) external { + _fillRelayV3(relayExecution, relayer, isSlowFill); + } +} + +/** + * @title SpokePool EIP-7702 Delegation Tests + * @notice Tests EIP-7702 delegation functionality in SpokePool contract + */ +contract SpokePoolEIP7702Test is Test { + using AddressToBytes32 for address; + using Bytes32ToAddress for bytes32; + + TestableMockSpokePool spokePool; + WETH9 weth; + + address owner; + address relayer; + address recipient; + + uint256 constant WETH_AMOUNT = 1 ether; + uint256 constant CHAIN_ID = 1; + address mockImplementation; + + function setUp() public { + weth = new WETH9(); + owner = vm.addr(1); + relayer = vm.addr(2); + recipient = makeAddr("recipient"); + mockImplementation = makeAddr("mockImplementation"); + + // Deploy SpokePool + vm.startPrank(owner); + ERC1967Proxy proxy = new ERC1967Proxy( + address(new TestableMockSpokePool(address(weth))), + abi.encodeCall(ZkSync_SpokePool.initialize, (0, ZkBridgeLike(address(0)), owner, makeAddr("hubPool"))) + ); + spokePool = TestableMockSpokePool(payable(proxy)); + vm.stopPrank(); + + // Fund contracts and accounts + // First give SpokePool some ETH, then deposit it as WETH + deal(address(spokePool), WETH_AMOUNT * 10); + vm.prank(address(spokePool)); + weth.deposit{ value: WETH_AMOUNT * 5 }(); // Deposit some of the ETH as WETH for testing + + deal(relayer, 10 ether); + deal(recipient, 1 ether); + } + + /** + * @dev Creates a test contract to simulate EIP-7702 delegated wallet + * EIP-7702 delegation code must be exactly 23 bytes: 0xef0100 + 20-byte address + */ + function createMockDelegatedWallet() internal returns (address) { + // Create bytecode that starts with EIP-7702 prefix (0xef0100) followed by implementation address + // This creates exactly 23 bytes: 3 bytes prefix + 20 bytes address = 23 bytes + bytes memory delegationCode = abi.encodePacked(bytes3(0xef0100), mockImplementation); + + address delegatedWallet = makeAddr("delegatedWallet"); + vm.etch(delegatedWallet, delegationCode); + return delegatedWallet; + } + + /** + * @dev Creates a regular contract (not delegated) + */ + function createRegularContract() internal returns (address) { + SimpleContract regularContract = new SimpleContract(); + return address(regularContract); + } + + // Test 1: Verify _is7702DelegatedWallet returns false for EIP-7702 delegated wallets and EOA + function test_is7702DelegatedWallet_ReturnsFalseForDelegatedWallet() public { + address delegatedWallet = createMockDelegatedWallet(); + address regularContract = createRegularContract(); + address eoa = makeAddr("eoa"); + + // + assertFalse( + spokePool.test_is7702DelegatedWallet(delegatedWallet), + "Should not detect EIP-7702 delegated wallet" + ); + assertFalse( + spokePool.test_is7702DelegatedWallet(regularContract), + "Should not detect regular contract as delegated" + ); + assertFalse(spokePool.test_is7702DelegatedWallet(eoa), "Should not detect EOA as delegated"); + } + + // Test 2: Verify _unwrapwrappedNativeTokenTo sends WETH to regular contracts + function test_unwrapToRegularContract() public { + address regularContract = createRegularContract(); + uint256 initialEthBalance = regularContract.balance; + uint256 initialWethBalance = weth.balanceOf(regularContract); + + // Sending to regular contract + spokePool.test_unwrapwrappedNativeTokenTo(payable(regularContract), WETH_AMOUNT); + + // Should receive WETH, not ETH + assertEq(regularContract.balance, initialEthBalance, "Regular contract should not receive ETH"); + assertEq( + weth.balanceOf(regularContract), + initialWethBalance + WETH_AMOUNT, + "Regular contract should receive WETH" + ); + } + + // Test 3: Verify _unwrapwrappedNativeTokenTo sends ETH to EOAs + function test_unwrapToEOA() public { + address eoa = makeAddr("eoa"); + uint256 initialBalance = eoa.balance; + + // Send to EOA + spokePool.test_unwrapwrappedNativeTokenTo(payable(eoa), WETH_AMOUNT); + + // Should receive ETH, not WETH + assertEq(eoa.balance, initialBalance + WETH_AMOUNT, "EOA should receive ETH"); + assertEq(weth.balanceOf(eoa), 0, "EOA should not receive WETH"); + } + + // Test 4: Test the functionality in context of fill relay operations with mock delegated wallet + // should not receive ETH from fill + function test_fillRelayWithDelegatedRecipient() public { + // Create a mock delegated wallet for the recipient + address delegatedRecipient = createMockDelegatedWallet(); + deal(delegatedRecipient, 1 ether); // Give it some initial ETH + + uint256 initialEthBalance = delegatedRecipient.balance; + uint256 fillAmount = 0.5 ether; + + // Setup a mock relay with delegated recipient + V3SpokePoolInterface.V3RelayExecutionParams memory relayExecution = V3SpokePoolInterface + .V3RelayExecutionParams({ + relay: V3SpokePoolInterface.V3RelayData({ + depositor: relayer.toBytes32(), + recipient: delegatedRecipient.toBytes32(), + exclusiveRelayer: bytes32(0), + inputToken: address(weth).toBytes32(), + outputToken: address(weth).toBytes32(), + inputAmount: fillAmount, + outputAmount: fillAmount, + originChainId: CHAIN_ID, + depositId: 1, + fillDeadline: uint32(block.timestamp + 1 hours), + exclusivityDeadline: 0, + message: "" + }), + relayHash: keccak256("test"), + updatedOutputAmount: fillAmount, + updatedRecipient: delegatedRecipient.toBytes32(), + updatedMessage: "", + repaymentChainId: CHAIN_ID + }); + + // Fund the SpokePool with WETH for the slow fill + deal(address(weth), address(spokePool), fillAmount); + + // Execute the fill + vm.startPrank(relayer); + spokePool.test_fillRelayV3(relayExecution, relayer.toBytes32(), true); + vm.stopPrank(); + + // Verify delegated recipient received ETH + assertEq(delegatedRecipient.balance, initialEthBalance, "Delegated recipient should not receive ETH from fill"); + assertEq(weth.balanceOf(delegatedRecipient), 0.5 ether, "Delegated recipient should receive WETH"); + } +} From 21f22b656d15c35325556321bc8c3e884ca5c67f Mon Sep 17 00:00:00 2001 From: Taylor Webb <84364476+tbwebb22@users.noreply.github.com> Date: Fri, 17 Oct 2025 14:58:42 -0600 Subject: [PATCH 05/47] feat: add safe bridge check (#1140) * Add function for checking if bridge amount is safe * fix function natspec --- contracts/libraries/HyperCoreLib.sol | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/contracts/libraries/HyperCoreLib.sol b/contracts/libraries/HyperCoreLib.sol index eecbf94b0..ec9b47f61 100644 --- a/contracts/libraries/HyperCoreLib.sol +++ b/contracts/libraries/HyperCoreLib.sol @@ -219,7 +219,7 @@ library HyperCoreLib { * @param index The asset index to get the spot price of * @return spotPx The spot price of the specified asset on HyperCore scaled by 1e8 */ - function spotPx(uint32 index) external view returns (uint64) { + function spotPx(uint32 index) internal view returns (uint64) { (bool success, bytes memory result) = SPOT_PX_PRECOMPILE_ADDRESS.staticcall(abi.encode(index)); if (!success) revert SpotPxPrecompileCallFailed(); return abi.decode(result, (uint64)); @@ -237,6 +237,26 @@ library HyperCoreLib { return _tokenInfo; } + /** + * @notice Checks if an amount is safe to bridge from HyperEVM to HyperCore + * @dev Verifies that the asset bridge has sufficient balance to cover the amount plus a buffer + * @param erc20CoreIndex The HyperCore index id of the token + * @param coreAmount The amount that the bridging should result in on HyperCore + * @param coreBufferAmount The minimum buffer amount that should remain on HyperCore after bridging + * @return True if the bridge has enough balance to safely bridge the amount, false otherwise + */ + function isCoreAmountSafeToBridge( + uint64 erc20CoreIndex, + uint64 coreAmount, + uint64 coreBufferAmount + ) internal view returns (bool) { + address bridgeAddress = toAssetBridgeAddress(erc20CoreIndex); + uint64 currentBridgeBalance = spotBalance(bridgeAddress, erc20CoreIndex); + + // Return true if currentBridgeBalance >= coreAmount + coreBufferAmount + return currentBridgeBalance >= coreAmount + coreBufferAmount; + } + /** * @notice Converts a core index id to an asset bridge address * @param erc20CoreIndex The core token index id to convert From 4e954db6f9823e3ba3b275802af55b80d45ec6bc Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Sun, 19 Oct 2025 23:05:03 -0400 Subject: [PATCH 06/47] feat: Sponsored Bridging - CCTP (#1135) * feat: SponsoredCCTPLib Signed-off-by: Faisal Usmani * Updated stuct hash for sig validation Signed-off-by: Faisal Usmani * Added src/dst periphery contracts Signed-off-by: Faisal Usmani * updates const Signed-off-by: Faisal Usmani * Updated event names and params Signed-off-by: Faisal Usmani * Updated receive event Signed-off-by: Faisal Usmani * Added hypercore lib Signed-off-by: Faisal Usmani * Added safe guards Signed-off-by: Faisal Usmani * Added wip swap handler Signed-off-by: Faisal Usmani * Added missing calls Signed-off-by: Faisal Usmani * Added limit order queue Signed-off-by: Faisal Usmani * updated simple transfer flow Signed-off-by: Faisal Usmani * Added hyper core forwarder Signed-off-by: Faisal Usmani * init hyper core forwarder swap func Signed-off-by: Faisal Usmani * progress Signed-off-by: Ihor Farion * complete _initiateSwapFlow2 Signed-off-by: Ihor Farion * Added finalize pending swaps function Signed-off-by: Faisal Usmani * removed finalTokenHCoreId Signed-off-by: Faisal Usmani * correct the limit price, size calculation, and send to SwapHandler logic Signed-off-by: Ihor Farion * added access control Signed-off-by: Faisal Usmani * first draft of cancelLimitOrderByCloid + submitNewLimitOrder Signed-off-by: Ihor Farion * complete the newMinCoreAmountFromLO calculation Signed-off-by: Ihor Farion * add a check to submitNewLimitOrder that safeguards new params against old calculated token amounts Signed-off-by: Ihor Farion * add fixes wrt new HyperCoreLib Signed-off-by: Ihor Farion * adjust functionality using new HyperCoreLib fns Signed-off-by: Ihor Farion * add _executeFlow + multiple random improvements Signed-off-by: Ihor Farion * some renamings for consistency Signed-off-by: Ihor Farion * try to improve donationBox interactions Signed-off-by: Ihor Farion * added bridge balance check before transfer Signed-off-by: Faisal Usmani * added fallback to send on evm Signed-off-by: Faisal Usmani * Updated dst periphery to use executeflow func Signed-off-by: Faisal Usmani * unified fallback logic Signed-off-by: Faisal Usmani * updated account activation logic Signed-off-by: Faisal Usmani * Removed ownable Signed-off-by: Faisal Usmani * Removed maxBpsToSponsor from sig check Signed-off-by: Faisal Usmani * check for mint recipient in message validation Signed-off-by: Faisal Usmani * make the bridge safety buffer configurable; use new isCoreAmountSafeToBridge function Signed-off-by: Ihor Farion * improve fallback to hyperevm emitted event and logic Signed-off-by: Ihor Farion * added sweep functions Signed-off-by: Faisal Usmani * Added min delay between finalizations Signed-off-by: Faisal Usmani * added commulative funcs Signed-off-by: Faisal Usmani * rewrite _initiateSwapFlow to support non-sponsored flow Signed-off-by: Ihor Farion * fallback flows and events Signed-off-by: Ihor Farion * misc todos Signed-off-by: Ihor Farion * misc improvements Signed-off-by: Ihor Farion * misc todos and fixes Signed-off-by: Ihor Farion * rough draft of correct size calculations Signed-off-by: Ihor Farion * new calc functions, hook up to swap flow Signed-off-by: Ihor Farion * fix math in submitUpdatedLimitOrder Signed-off-by: Ihor Farion * correct the amt calc Signed-off-by: Ihor Farion * add updated comments Signed-off-by: Ihor Farion * misc todos Signed-off-by: Ihor Farion * add _getSuggestedPriceX1e8 and comments Signed-off-by: Ihor Farion * comments + misc fixes Signed-off-by: Ihor Farion * improve fallback hyperEVM flow + fix donationBox interactions Signed-off-by: Ihor Farion * update account activation logic for SwapHandler Signed-off-by: Ihor Farion * add a comment Signed-off-by: Ihor Farion * add PX_D to _calcLOAmountsSell Signed-off-by: Ihor Farion * add maxUserSlippage for CCTP flow; add quote deadline buffer Signed-off-by: Ihor Farion * feedback Signed-off-by: Faisal Usmani * Stack too deep, amount less fee & removal of isFinalized Signed-off-by: Faisal Usmani * fix incorrect calculation of non-sponsored token amount Signed-off-by: Ihor Farion * internal => extrenal in HyperCoreLib Signed-off-by: Faisal Usmani * fix incorrect bridge safety check Signed-off-by: Ihor Farion * change from time-based buffer between fund pulls to block-based buffer Signed-off-by: Ihor Farion * Update quote lib Signed-off-by: Faisal Usmani * use safeErc20 in src Signed-off-by: Faisal Usmani --------- Signed-off-by: Faisal Usmani Signed-off-by: Ihor Farion Co-authored-by: Ihor Farion --- .gitignore | 1 + .../external/interfaces/CCTPInterfaces.sol | 70 +- .../interfaces/SponsoredCCTPInterface.sol | 84 ++ contracts/libraries/AddressConverters.sol | 6 +- contracts/libraries/BytesLib.sol | 106 ++ contracts/libraries/HyperCoreLib.sol | 16 + contracts/libraries/SponsoredCCTPQuoteLib.sol | 138 +++ .../mintburn/HyperCoreFlowExecutor.sol | 1075 +++++++++++++++++ contracts/periphery/mintburn/Structs.sol | 34 + contracts/periphery/mintburn/SwapHandler.sol | 69 ++ .../SponsoredCCTPDstPeriphery.sol | 109 ++ .../SponsoredCCTPSrcPeriphery.sol | 79 ++ deploy/consts.ts | 7 + foundry.toml | 6 + generated/constants.json | 7 + hardhat.config.ts | 1 + script/utils/Constants.sol | 30 +- 17 files changed, 1811 insertions(+), 27 deletions(-) create mode 100644 contracts/interfaces/SponsoredCCTPInterface.sol create mode 100644 contracts/libraries/BytesLib.sol create mode 100644 contracts/libraries/SponsoredCCTPQuoteLib.sol create mode 100644 contracts/periphery/mintburn/HyperCoreFlowExecutor.sol create mode 100644 contracts/periphery/mintburn/Structs.sol create mode 100644 contracts/periphery/mintburn/SwapHandler.sol create mode 100644 contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol create mode 100644 contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol diff --git a/.gitignore b/.gitignore index 7d281cd72..147e874f2 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ artifacts-zk # Foundry files out zkout +cache-foundry # Upgradeability files .openzeppelin diff --git a/contracts/external/interfaces/CCTPInterfaces.sol b/contracts/external/interfaces/CCTPInterfaces.sol index 4eb89740b..9ac256f90 100644 --- a/contracts/external/interfaces/CCTPInterfaces.sol +++ b/contracts/external/interfaces/CCTPInterfaces.sol @@ -57,8 +57,8 @@ interface ITokenMessenger { function localMinter() external view returns (ITokenMinter minter); } -// Source: https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/TokenMessengerV2.sol#L138C1-L166C15 interface ITokenMessengerV2 { + // Source: https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/TokenMessengerV2.sol#L138C1-L166C15 /** * @notice Deposits and burns tokens from sender to be minted on destination domain. * Emits a `DepositForBurn` event. @@ -88,6 +88,39 @@ interface ITokenMessengerV2 { uint256 maxFee, uint32 minFinalityThreshold ) external; + + // Source: https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/TokenMessengerV2.sol#L180C1-L210C15 + /** + * @notice Deposits and burns tokens from sender to be minted on destination domain. + * Emits a `DepositForBurn` event. + * @dev reverts if: + * - `hookData` is zero-length + * - `burnToken` is not supported + * - `destinationDomain` has no TokenMessenger registered + * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance + * to this contract is less than `amount`. + * - burn() reverts. For example, if `amount` is 0. + * - maxFee is greater than or equal to `amount`. + * - MessageTransmitterV2#sendMessage reverts. + * @param amount amount of tokens to burn + * @param destinationDomain destination domain to receive message on + * @param mintRecipient address of mint recipient on destination domain, as bytes32 + * @param burnToken token to burn `amount` of, on local domain + * @param destinationCaller authorized caller on the destination domain, as bytes32. If equal to bytes32(0), + * any address can broadcast the message. + * @param maxFee maximum fee to pay on the destination domain, specified in units of burnToken + * @param hookData hook data to append to burn message for interpretation on destination domain + */ + function depositForBurnWithHook( + uint256 amount, + uint32 destinationDomain, + bytes32 mintRecipient, + address burnToken, + bytes32 destinationCaller, + uint256 maxFee, + uint32 minFinalityThreshold, + bytes calldata hookData + ) external; } /** @@ -128,3 +161,38 @@ interface IMessageTransmitter { bytes calldata messageBody ) external returns (uint64); } + +interface IMessageTransmitterV2 { + // Source: https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/MessageTransmitterV2.sol#L176C1-L209C61 + /** + * @notice Receive a message. Messages can only be broadcast once for a given nonce. + * The message body of a valid message is passed to the specified recipient for further processing. + * + * @dev Attestation format: + * A valid attestation is the concatenated 65-byte signature(s) of exactly + * `thresholdSignature` signatures, in increasing order of attester address. + * ***If the attester addresses recovered from signatures are not in + * increasing order, signature verification will fail.*** + * If incorrect number of signatures or duplicate signatures are supplied, + * signature verification will fail. + * + * Message Format: + * + * Field Bytes Type Index + * version 4 uint32 0 + * sourceDomain 4 uint32 4 + * destinationDomain 4 uint32 8 + * nonce 32 bytes32 12 + * sender 32 bytes32 44 + * recipient 32 bytes32 76 + * destinationCaller 32 bytes32 108 + * minFinalityThreshold 4 uint32 140 + * finalityThresholdExecuted 4 uint32 144 + * messageBody dynamic bytes 148 + * @param message Message bytes + * @param attestation Concatenated 65-byte signature(s) of `message`, in increasing order + * of the attester address recovered from signatures. + * @return success True, if successful; false, if not + */ + function receiveMessage(bytes calldata message, bytes calldata attestation) external returns (bool success); +} diff --git a/contracts/interfaces/SponsoredCCTPInterface.sol b/contracts/interfaces/SponsoredCCTPInterface.sol new file mode 100644 index 000000000..06173cbfc --- /dev/null +++ b/contracts/interfaces/SponsoredCCTPInterface.sol @@ -0,0 +1,84 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +/** + * @title SponsoredCCTPInterface + * @notice Interface for the SponsoredCCTP contract + * @custom:security-contact bugs@across.to + */ +interface SponsoredCCTPInterface { + // Error thrown when the signature is invalid. + error InvalidSignature(); + + // Error thrown when the nonce is invalid. + error InvalidNonce(); + + // Error thrown when the deadline is invalid. + error InvalidDeadline(); + + // Error thrown when the source domain is invalid. + error InvalidSourceDomain(); + + event SponsoredDepositForBurn( + bytes32 indexed quoteNonce, + address indexed originSender, + bytes32 indexed finalRecipient, + uint256 quoteDeadline, + uint256 maxBpsToSponsor, + uint256 maxUserSlippageBps, + bytes32 finalToken, + bytes signature + ); + + event SponsoredMintAndWithdraw( + bytes32 indexed quoteNonce, + bytes32 indexed finalRecipient, + bytes32 indexed finalToken, + uint256 finalAmount, + uint256 quoteDeadline, + uint256 maxBpsToSponsor, + uint256 maxUserSlippageBps + ); + + event SimpleTansferToCore( + address indexed finalToken, + address indexed finalRecipient, + uint256 finalAmount, + uint256 maxAmountToSponsor + ); + + // Params that will be used to create a sponsored CCTP quote and deposit for burn. + struct SponsoredCCTPQuote { + // The domain ID of the source chain. + uint32 sourceDomain; + // The domain ID of the destination chain. + uint32 destinationDomain; + // The recipient of the minted USDC on the destination chain. + bytes32 mintRecipient; + // The amount that the user pays on the source chain. + // TODO: rename this to indicate source amount + uint256 amount; + // The token that will be burned on the source chain. + bytes32 burnToken; + // The caller of the destination chain. + bytes32 destinationCaller; + // Maximum fee to pay on the destination domain, specified in units of burnToken + uint256 maxFee; + // Minimum finality threshold before allowed to attest + uint32 minFinalityThreshold; + // Nonce is used to prevent replay attacks. + bytes32 nonce; + // Timestamp of the quote after which it can no longer be used. + uint256 deadline; + // The maximum basis points of the amount that can be sponsored. + uint256 maxBpsToSponsor; + // Slippage tolerance for the fees on the destination. Used in swap flow, enforced on destination + uint256 maxUserSlippageBps; + // The final recipient of the sponsored deposit. This is needed as the mintRecipient will be the + // handler contract address instead of the final recipient. + bytes32 finalRecipient; + // The final token that final recipient will receive. This is needed as it can be different from the burnToken + // in which case we perform a swap on the destination chain. + bytes32 finalToken; + } +} diff --git a/contracts/libraries/AddressConverters.sol b/contracts/libraries/AddressConverters.sol index 6b52ff479..d2f9ac2c4 100644 --- a/contracts/libraries/AddressConverters.sol +++ b/contracts/libraries/AddressConverters.sol @@ -17,10 +17,14 @@ library Bytes32ToAddress { } function checkAddress(bytes32 _bytes32) internal pure { - if (uint256(_bytes32) >> 160 != 0) { + if (!isValidAddress(_bytes32)) { revert InvalidBytes32(); } } + + function isValidAddress(bytes32 _bytes32) internal pure returns (bool) { + return uint256(_bytes32) >> 160 == 0; + } } library AddressToBytes32 { diff --git a/contracts/libraries/BytesLib.sol b/contracts/libraries/BytesLib.sol new file mode 100644 index 000000000..451118641 --- /dev/null +++ b/contracts/libraries/BytesLib.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +library BytesLib { + /************************************** + * ERRORS * + **************************************/ + error OutOfBounds(); + error InvalidBytes(); + error InvalidStart(); + + /************************************** + * FUNCTIONS * + **************************************/ + + /** + * @notice Reads a uint32 from a bytes array at a given start index + * @param _bytes The bytes array to convert + * @param _start The start index of the uint32 + * @return result The uint32 result + */ + function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result) { + if (_bytes.length < _start + 4) { + revert OutOfBounds(); + } + + // solhint-disable-next-line no-inline-assembly + assembly { + result := mload(add(add(_bytes, 0x4), _start)) + } + } + + /** + * @notice Reads a uint256 from a bytes array at a given start index + * @param _bytes The bytes array to convert + * @param _start The start index of the uint256 + * @return result The uint256 result + */ + function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result) { + if (_bytes.length < _start + 32) { + revert OutOfBounds(); + } + + // solhint-disable-next-line no-inline-assembly + assembly { + result := mload(add(add(_bytes, 0x20), _start)) + } + } + + /** + * @notice Reads a bytes32 from a bytes array at a given start index + * @param _bytes The bytes array to convert + * @param _start The start index of the bytes32 + * @return result The bytes32 result + */ + function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result) { + if (_bytes.length < _start + 32) { + revert OutOfBounds(); + } + + // solhint-disable-next-line no-inline-assembly + assembly { + result := mload(add(add(_bytes, 0x20), _start)) + } + } + + /** + * @notice Reads a bytes array from a bytes array at a given start index and length + * @param _bytes The bytes array to convert + * @param _start The start index of the bytes array + * @param _end The end index of the bytes array + * @return result The bytes array result + */ + function slice(bytes memory _bytes, uint256 _start, uint256 _end) internal pure returns (bytes memory result) { + // solhint-disable-next-line no-inline-assembly + assembly { + let l := mload(_bytes) // _bytes length. + if iszero(gt(l, _end)) { + _end := l + } + if iszero(gt(l, _start)) { + _start := l + } + if lt(_start, _end) { + result := mload(0x40) + let n := sub(_end, _start) + let i := add(_bytes, _start) + let w := not(0x1f) + // Copy the `_bytes` one word at a time, backwards. + for { + let j := and(add(n, 0x1f), w) + } 1 {} { + mstore(add(result, j), mload(add(i, j))) + j := add(j, w) // `sub(j, 0x20)`. + if iszero(j) { + break + } + } + let o := add(add(result, 0x20), n) + mstore(o, 0) // Zeroize the slot after the bytes. + mstore(0x40, add(o, 0x20)) // Allocate memory. + mstore(result, n) // Store the length. + } + } + } +} diff --git a/contracts/libraries/HyperCoreLib.sol b/contracts/libraries/HyperCoreLib.sol index ec9b47f61..908ccf04d 100644 --- a/contracts/libraries/HyperCoreLib.sol +++ b/contracts/libraries/HyperCoreLib.sol @@ -45,6 +45,7 @@ library HyperCoreLib { uint256 public constant BASE_ASSET_BRIDGE_ADDRESS_UINT256 = uint256(uint160(BASE_ASSET_BRIDGE_ADDRESS)); // Precompile addresses + // TODO: maybe we should be using https://github.com/hyperliquid-dev/hyper-evm-lib instead? address public constant SPOT_BALANCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; address public constant SPOT_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000808; address public constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; @@ -337,4 +338,19 @@ library HyperCoreLib { amountCoreToReceive = uint64(amountEVMToSend * scale); } } + + function convertCoreDecimalsSimple( + uint64 amountDecimalsFrom, + uint8 decimalsFrom, + uint8 decimalsTo + ) internal pure returns (uint64) { + if (decimalsFrom == decimalsTo) { + return amountDecimalsFrom; + } else if (decimalsFrom < decimalsTo) { + return uint64(amountDecimalsFrom * 10 ** (decimalsTo - decimalsFrom)); + } else { + // round down + return uint64(amountDecimalsFrom / 10 ** (decimalsFrom - decimalsTo)); + } + } } diff --git a/contracts/libraries/SponsoredCCTPQuoteLib.sol b/contracts/libraries/SponsoredCCTPQuoteLib.sol new file mode 100644 index 000000000..8fe36e3d2 --- /dev/null +++ b/contracts/libraries/SponsoredCCTPQuoteLib.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; + +import { SponsoredCCTPInterface } from "../interfaces/SponsoredCCTPInterface.sol"; +import { BytesLib } from "./BytesLib.sol"; +import { Bytes32ToAddress } from "./AddressConverters.sol"; + +library SponsoredCCTPQuoteLib { + using BytesLib for bytes; + using Bytes32ToAddress for bytes32; + + // Indices of each field in message + uint256 private constant VERSION_INDEX = 0; + uint256 private constant SOURCE_DOMAIN_INDEX = 4; + uint256 private constant DESTINATION_DOMAIN_INDEX = 8; + uint256 private constant NONCE_INDEX = 12; + uint256 private constant SENDER_INDEX = 44; + uint256 private constant RECIPIENT_INDEX = 76; + uint256 private constant DESTINATION_CALLER_INDEX = 108; + uint256 private constant MIN_FINALITY_THRESHOLD_INDEX = 140; + uint256 private constant FINALITY_THRESHOLD_EXECUTED_INDEX = 144; + uint256 private constant MESSAGE_BODY_INDEX = 148; + + // Field indices in message body + uint256 private constant BURN_TOKEN_INDEX = 4; + uint256 private constant MINT_RECIPIENT_INDEX = 36; + uint256 private constant AMOUNT_INDEX = 68; + uint256 private constant MAX_FEE_INDEX = 132; + uint256 private constant FEE_EXECUTED_INDEX = 164; + uint256 private constant HOOK_DATA_INDEX = 228; + + // Total length of the message body (message body + hook data + 6 32-byte fields in hook data) + uint256 private constant MSG_BYTES_LENGTH = 568; + + function getDespoitForBurnData( + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote + ) + internal + pure + returns ( + uint256 amount, + uint32 destinationDomain, + bytes32 mintRecipient, + address burnToken, + bytes32 destinationCaller, + uint256 maxFee, + uint32 minFinalityThreshold, + bytes memory hookData + ) + { + amount = quote.amount; + destinationDomain = quote.destinationDomain; + mintRecipient = quote.mintRecipient; + burnToken = quote.burnToken.toAddress(); + destinationCaller = quote.destinationCaller; + maxFee = quote.maxFee; + minFinalityThreshold = quote.minFinalityThreshold; + hookData = abi.encode( + quote.nonce, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalRecipient, + quote.finalToken + ); + } + + function validateMessage(bytes memory message) internal view returns (bool) { + return + message.length == MSG_BYTES_LENGTH && + message.toBytes32(MESSAGE_BODY_INDEX + MINT_RECIPIENT_INDEX).toAddress() == address(this) && + // 4 & 5 here are the indices of the finalRecipient and finalToken in the hook data + message.toBytes32(MESSAGE_BODY_INDEX + HOOK_DATA_INDEX + 32 * 4).isValidAddress() && + message.toBytes32(MESSAGE_BODY_INDEX + HOOK_DATA_INDEX + 32 * 5).isValidAddress(); + } + + function getSponsoredCCTPQuoteData( + bytes memory message + ) internal pure returns (SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, uint256 feeExecuted) { + quote.sourceDomain = message.toUint32(SOURCE_DOMAIN_INDEX); + quote.destinationDomain = message.toUint32(DESTINATION_DOMAIN_INDEX); + quote.destinationCaller = message.toBytes32(DESTINATION_CALLER_INDEX); + quote.minFinalityThreshold = message.toUint32(MIN_FINALITY_THRESHOLD_INDEX); + + bytes memory messageBody = message.slice(MESSAGE_BODY_INDEX, message.length); + quote.mintRecipient = messageBody.toBytes32(MINT_RECIPIENT_INDEX); + quote.amount = messageBody.toUint256(AMOUNT_INDEX); + quote.burnToken = messageBody.toBytes32(BURN_TOKEN_INDEX); + quote.maxFee = messageBody.toUint256(MAX_FEE_INDEX); + feeExecuted = messageBody.toUint256(FEE_EXECUTED_INDEX); + + bytes memory hookData = messageBody.slice(HOOK_DATA_INDEX, messageBody.length); + ( + quote.nonce, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalRecipient, + quote.finalToken + ) = abi.decode(hookData, (bytes32, uint256, uint256, uint256, bytes32, bytes32)); + } + + function validateSignature( + address signer, + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, + bytes memory signature + ) internal view returns (bool) { + // Need to split the hash into two parts to avoid stack too deep error + bytes32 hash1 = keccak256( + abi.encode( + quote.sourceDomain, + quote.destinationDomain, + quote.mintRecipient, + quote.amount, + quote.burnToken, + quote.destinationCaller, + quote.maxFee, + quote.minFinalityThreshold + ) + ); + + bytes32 hash2 = keccak256( + abi.encode( + quote.nonce, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalRecipient, + quote.finalToken + ) + ); + + bytes32 typedDataHash = keccak256(abi.encode(hash1, hash2)); + return SignatureChecker.isValidSignatureNow(signer, typedDataHash, signature); + } +} diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol new file mode 100644 index 000000000..396827284 --- /dev/null +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -0,0 +1,1075 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; +import { IERC20Metadata, IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { DonationBox } from "../../chain-adapters/DonationBox.sol"; +import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol"; +import { CoreTokenInfo } from "./Structs.sol"; +import { FinalTokenInfo } from "./Structs.sol"; +import { SwapHandler } from "./SwapHandler.sol"; + +/** + * @title HyperCoreFlowExecutor + * @notice Contract handling HyperCore interactions for trasnfer-to-core or swap-with-core actions after stablecoin bridge transactions + * @dev This contract is designed to work with stablecoins. baseToken and every finalToken should all be stablecoins. + * @custom:security-contact bugs@across.to + */ +contract HyperCoreFlowExecutor is AccessControl { + using SafeERC20 for IERC20; + + // Common decimals scalars + uint256 public constant BPS_DECIMALS = 4; + uint256 public constant PPM_DECIMALS = 6; + uint256 public constant BPS_SCALAR = 10 ** BPS_DECIMALS; + uint256 public constant PPM_SCALAR = 10 ** PPM_DECIMALS; + // Decimals to use for Price calculations in limit order-related calculation functions + uint8 public constant PX_D = 8; + + // Roles + bytes32 public constant PERMISSIONED_BOT_ROLE = keccak256("PERMISSIONED_BOT_ROLE"); + bytes32 public constant FUNDS_SWEEPER_ROLE = keccak256("FUNDS_SWEEPER_ROLE"); + + /// @notice The donation box contract. + DonationBox public immutable donationBox; + + /// @notice A mapping of token addresses to their core token info. + mapping(address => CoreTokenInfo) public coreTokenInfos; + + /// @notice A mapping of token address to additional relevan info for final tokens, like Hyperliquid market params + mapping(address => FinalTokenInfo) public finalTokenInfos; + + /// @notice All operations performed in this contract are relative to this baseToken + address immutable baseToken; + + /// @notice The block number of the last funds pull action per final token: either as a part of finalizing pending swaps, + /// or an admin funds pull + mapping(address finalToken => uint256 lastPullFundsBlock) lastPullFundsBlock; + + /// @notice A struct used for storing state of a swap flow that has been initialized, but not yet finished + struct PendingSwap { + address finalRecipient; + address finalToken; + /// @notice totalCoreAmountToForwardToUser = minCoreAmountFromLO + sponsoredCoreAmountPreFunded always. + uint64 minCoreAmountFromLO; + uint64 sponsoredCoreAmountPreFunded; + uint128 limitOrderCloid; + } + + /// @notice A mapping containing the pending state between initializing the swap flow and finalizing it + mapping(bytes32 quoteNonce => PendingSwap pendingSwap) public pendingSwaps; + /// @notice A FCFS queue of pending swap flows to be executed. Per finalToken + mapping(address finalToken => bytes32[] quoteNonces) public pendingQueue; + /// @notice An index of the first unexecuted pending swap flow. Or equal to pendingQueue length if currently empty + mapping(address => uint256) public pendingQueueHead; + + /// @notice The cumulative amount of funds sponsored for each final token. + mapping(address => uint256) public cumulativeSponsoredAmount; + /// @notice The cumulative amount of activation fees sponsored for each final token. + mapping(address => uint256) public cumulativeSponsoredActivationFee; + + /// @notice Used for uniquely identifying Limit Orders this contract submits. Monotonically increasing + uint128 public nextCloid; + + /// @notice A mapping from limit order cliod to the quoteNonce (user order id) responsible for submitting the LO + mapping(uint128 => bytes32) public cloidToQuoteNonce; + + /// @notice Emitted when the donation box is insufficient funds. + event DonationBoxInsufficientFunds(address token, uint256 amount); + + /// @notice Emitted when the donation box is insufficient funds and we can't proceed. + error DonationBoxInsufficientFundsError(address token, uint256 amount); + + /// @notice Emitted when a simple transfer to core is executed. + event SimpleTransferFlowCompleted( + bytes32 indexed quoteNonce, + address indexed finalRecipient, + address indexed finalToken, + // All amounts are in finalToken + uint256 evmAmountReceived, + uint256 evmAmountSponsored, + uint256 evmAmountTransferred, + uint64 coreAmountTransferred + ); + + /// @notice Emitted upon successful completion of fallback HyperEVM flow + event FallbackHyperEVMFlowCompleted( + bytes32 indexed quoteNonce, + address indexed finalRecipient, + address indexed finalToken, + // All amounts are in finalToken + uint256 evmAmountReceived, + uint256 evmAmountSponsored, + uint256 evmAmountTransferred + ); + + /// @notice Emitted when a swap flow is initialized + event SwapFlowInitialized( + bytes32 indexed quoteNonce, + address indexed finalRecipient, + address indexed finalToken, + // In baseToken + uint256 evmAmountReceived, + // Two below in finalToken + uint256 evmAmountSponsored, + uint64 targetCoreAmountToTransfer, + uint32 asset, + uint128 cloid + ); + + /// @notice Emitted upon successful completion of swap flow + event SwapFlowCompleted( + bytes32 indexed quoteNonce, + address indexed finalRecipient, + address indexed finalToken, + // Two below in finalToken + uint256 coreAmountSponsored, + uint64 coreAmountTransferred + ); + + /// @notice Emitted upon cancelling a Limit order associated with an active swap flow + event CancelledLimitOrder(bytes32 indexed quoteNonce, uint32 indexed asset, uint128 indexed cloid); + + /// @notice Emitted upon submitting a new Limit order in place of a cancelled one + event ReplacedOldLimitOrder( + bytes32 indexed quoteNonce, + uint128 indexed cloid, + uint64 priceX1e8, + uint64 sizeX1e8, + uint64 oldPriceX1e8, + uint64 oldSizeX1e8Left + ); + + /// @notice Emitted when the replacing Limit order has better price than the old one + event BetterPricedLOSubmitted(bytes32 indexed quoteNonce, uint64 oldPriceX1e8, uint64 priceX1e8); + + /// @notice Emitted from the swap flow when falling back to the other flow becase the cost is to high compared to sponsored settings + event SwapFlowFallbackTooExpensive( + bytes32 indexed quoteNonce, + // Based on minimum out requirements for sponsored / non-sponsored flows + uint64 requiredCoreAmountToSponsor, + // Based on maxBpsToSponsor + uint64 maxCoreAmountToSponsor + ); + + /// @notice Emitted from the swap flow when falling back to the other flow becase donation box doesn't have enough funds to sponsor the flow + event SwapFlowFallbackDonationBox( + bytes32 indexed quoteNonce, + address indexed finalToken, + uint256 totalEVMAmountToSponsor + ); + + /// @notice Emitted from the swap flow when falling back to the other flow becase bridging to core was unsafe (spot bridge didn't have enough funds) + event SwapFlowFallbackUnsafeToBridge( + bytes32 indexed quoteNonce, + bool initTokenUnsafe, + address indexed finalToken, + bool finalTokenUnsafe + ); + + /// @notice Emitted from the swap flow when falling back to the other flow becase it's impossible to pay account activation fee in final token + event SwapFlowFallbackAccountActivation(bytes32 indexed quoteNonce, address finalToken); + + /// @notice Emitted from the simple transfer flow if either bridging is unsafe, or we couldn't pay for account activation in final token + event SimpleTransferFallback( + bytes32 indexed quoteNonce, + bool isBridgeSafe, + bool haveToPayForCoreAccountActivation, + bool tokenCanBeUsedForAccountActivation + ); + + /************************************** + * MODIFIERS * + **************************************/ + + modifier onlyDefaultAdmin() { + require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not default admin"); + _; + } + + modifier onlyPermissionedBot() { + require(hasRole(PERMISSIONED_BOT_ROLE, msg.sender), "Not limit order updater"); + _; + } + + modifier onlyFundsSweeper() { + require(hasRole(FUNDS_SWEEPER_ROLE, msg.sender), "Not funds sweeper"); + _; + } + + modifier onlyExistingToken(address evmTokenAddress) { + require(coreTokenInfos[evmTokenAddress].tokenInfo.evmContract != address(0), "Unknown token"); + _; + } + + /** + * + * @param _donationBox Sponsorship funds live here + * @param _baseToken Main token used with this Forwarder + * @param _coreIndex HCore index of baseToken + * @param _canBeUsedForAccountActivation Whether or not baseToken can be used for account activation fee on HCore + * @param _accountActivationFeeCore Fee amount to pay for account activation + * @param _bridgeSafetyBufferCore Buffer to use the availability of Bridge funds on core side when bridging this token + */ + constructor( + address _donationBox, + address _baseToken, + uint32 _coreIndex, + bool _canBeUsedForAccountActivation, + uint64 _accountActivationFeeCore, + uint64 _bridgeSafetyBufferCore + ) { + donationBox = DonationBox(_donationBox); + // @dev initialize this to 1 as to save 0 for special events when "no cloid is set" = no associated limit order + nextCloid = 1; + + _setCoreTokenInfo( + _baseToken, + _coreIndex, + _canBeUsedForAccountActivation, + _accountActivationFeeCore, + _bridgeSafetyBufferCore + ); + baseToken = _baseToken; + + // AccessControl setup + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _setRoleAdmin(PERMISSIONED_BOT_ROLE, DEFAULT_ADMIN_ROLE); + _setRoleAdmin(FUNDS_SWEEPER_ROLE, DEFAULT_ADMIN_ROLE); + } + + /************************************** + * CONFIGURATION FUNCTIONS * + **************************************/ + + /** + * @notice Set or update information for the token to use it in this contract + * @dev To be able to use the token in the swap flow, FinalTokenInfo has to be set as well + * @dev Setting core token info to incorrect values can lead to loss of funds. Should NEVER be unset while the + * finalTokenParams are not unset + */ + function setCoreTokenInfo( + address token, + uint32 coreIndex, + bool canBeUsedForAccountActivation, + uint64 accountActivationFeeCore, + uint64 bridgeSafetyBufferCore + ) external onlyDefaultAdmin { + _setCoreTokenInfo( + token, + coreIndex, + canBeUsedForAccountActivation, + accountActivationFeeCore, + bridgeSafetyBufferCore + ); + } + + /** + * @notice Sets the parameters for a final token. + * @dev This function deploys a new SwapHandler contract if one is not already set. If the final token + * can't be used for account activation, the handler will be left unactivated and would need to be activated by the caller. + * @param finalToken The address of the final token. + * @param assetIndex The index of the asset in the Hyperliquid market. + * @param isBuy Whether the final token is a buy or a sell. + * @param feePpm The fee in parts per million. + * @param suggestedDiscountBps The suggested slippage in basis points. + * @param accountActivationFeeToken A token to pay account activation fee in. Only used if adding a new final token + */ + function setFinalTokenInfo( + address finalToken, + uint32 assetIndex, + bool isBuy, + uint32 feePpm, + uint32 suggestedDiscountBps, + address accountActivationFeeToken + ) external onlyExistingToken(finalToken) onlyExistingToken(accountActivationFeeToken) onlyDefaultAdmin { + SwapHandler swapHandler = finalTokenInfos[finalToken].swapHandler; + if (address(swapHandler) == address(0)) { + swapHandler = new SwapHandler(); + } + + finalTokenInfos[finalToken] = FinalTokenInfo({ + assetIndex: assetIndex, + isBuy: isBuy, + feePpm: feePpm, + swapHandler: swapHandler, + suggestedDiscountBps: suggestedDiscountBps + }); + + // TODO: this activation flow seems broken. Can a smart contract activate its core account by doing an EVM -> Core transfer? + uint256 accountActivationFee = _getAccountActivationFeeEVM(accountActivationFeeToken, address(swapHandler)); + if (accountActivationFee > 0) { + CoreTokenInfo memory accountActivationTokenInfo = coreTokenInfos[accountActivationFeeToken]; + require(accountActivationTokenInfo.canBeUsedForAccountActivation, "account activation fee token error"); + + _getFromDonationBox(accountActivationFeeToken, accountActivationFee); + IERC20(accountActivationFeeToken).safeTransfer(address(swapHandler), accountActivationFee); + swapHandler.activateCoreAccount( + accountActivationFeeToken, + accountActivationTokenInfo.coreIndex, + accountActivationFee, + accountActivationTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + } + } + + /************************************** + * FLOW FUNCTIONS * + **************************************/ + + /** + * @notice This function is to be called by an inheriting contract. It is to be called after the child contract + * checked the API signature and made sure that the params passed here have been verified by either the underlying + * bridge mechanics, or API signaure, or both. + */ + function _executeFlow( + uint256 amount, + bytes32 quoteNonce, + uint256 maxBpsToSponsor, + uint256 maxUserSlippageBps, + address finalRecipient, + address finalToken, + uint256 extraFeesToSponsor + ) internal { + if (finalToken == baseToken) { + _executeSimpleTransferFlow(amount, quoteNonce, maxBpsToSponsor, finalRecipient, extraFeesToSponsor); + } else { + _initiateSwapFlow( + amount, + quoteNonce, + finalRecipient, + finalToken, + maxBpsToSponsor, + maxUserSlippageBps, + extraFeesToSponsor + ); + } + } + + /// @notice Execute a simple transfer flow in which we transfer `baseToken` to the user on HyperCore after receiving + /// an amount of baseToken from the user on HyperEVM + function _executeSimpleTransferFlow( + uint256 amount, + bytes32 quoteNonce, + uint256 maxBpsToSponsor, + address finalRecipient, + uint256 extraFeesToSponsor + ) internal { + address finalToken = baseToken; + CoreTokenInfo storage coreTokenInfo = coreTokenInfos[finalToken]; + + bool coreUserAccountExists = HyperCoreLib.coreUserExists(finalRecipient); + bool impossibleToForwardToCore = !coreUserAccountExists && !coreTokenInfo.canBeUsedForAccountActivation; + + // If the user has no HyperCore account and we can't sponsor its creation, + // fall back to sending user funds on HyperEVM + if (impossibleToForwardToCore) { + _fallbackHyperEVMFlow(amount, quoteNonce, maxBpsToSponsor, finalRecipient, extraFeesToSponsor); + return; + } + + // Record `accountCreationFee` as zero if we can't use final token for account activation + uint256 accountCreationFee = coreUserAccountExists ? 0 : coreTokenInfo.accountActivationFeeEVM; + uint256 maxEvmAmountToSponsor = (amount * maxBpsToSponsor) / BPS_SCALAR; + uint256 totalUserFeesEvm = extraFeesToSponsor + accountCreationFee; + uint256 amountToSponsor = totalUserFeesEvm; + if (amountToSponsor > maxEvmAmountToSponsor) { + amountToSponsor = maxEvmAmountToSponsor; + } + + if (amountToSponsor > 0) { + if (!_availableInDonationBox(coreTokenInfo.tokenInfo.evmContract, amountToSponsor)) { + amountToSponsor = 0; + } + } + + uint256 finalAmount = amount + amountToSponsor; + + (uint256 quotedEvmAmount, uint64 quotedCoreAmount) = HyperCoreLib.maximumEVMSendAmountToAmounts( + finalAmount, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + // If there are no funds left on the destination side of the bridge, the funds will be lost in the + // bridge. We check send safety via `isCoreAmountSafeToBridge` + bool isSafe = HyperCoreLib.isCoreAmountSafeToBridge( + coreTokenInfo.coreIndex, + quotedCoreAmount, + coreTokenInfo.bridgeSafetyBufferCore + ); + + // If the amount is not safe to bridge because the bridge doesn't have enough liquidity, + // fall back to sending user funds on HyperEVM. + if (!isSafe) { + _fallbackHyperEVMFlow(amount, quoteNonce, maxBpsToSponsor, finalRecipient, extraFeesToSponsor); + return; + } + + if (amountToSponsor > 0) { + // This will succeed because we checked the balance earlier + _getFromDonationBox(coreTokenInfo.tokenInfo.evmContract, amountToSponsor); + } + + cumulativeSponsoredAmount[finalToken] += amountToSponsor; + // Record the amount used to sponsor account creation + cumulativeSponsoredActivationFee[finalToken] += amountToSponsor < accountCreationFee + ? amountToSponsor + : accountCreationFee; + + // There is a very slim chance that by the time we get here, the balance of the bridge changes + // and the funds are lost. + HyperCoreLib.transferERC20EVMToCore( + finalToken, + coreTokenInfo.coreIndex, + finalRecipient, + quotedEvmAmount, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + + emit SimpleTransferFlowCompleted( + quoteNonce, + finalRecipient, + finalToken, + amount, + amountToSponsor, + quotedEvmAmount, + quotedCoreAmount + ); + } + + /// @notice initialized swap flow to eventually forward `finalToken` to the user, starting from `baseToken` (received + /// from a user bridge transaction) + function _initiateSwapFlow( + // In initialToken + uint256 amountInEVM, + bytes32 quoteNonce, + address finalUser, + address finalToken, + uint256 maxBpsToSponsor, + // `maxUserSlippageBps` here means how much token user receives compared to a 1 to 1 + uint256 maxUserSlippageBps, + // In initialToken + uint256 extraBridgingFeesEVM + ) internal { + FinalTokenInfo memory finalTokenInfo = finalTokenInfos[finalToken]; + require(address(finalTokenInfo.swapHandler) != address(0), "Final token not registered"); + + address initialToken = baseToken; + CoreTokenInfo memory initialCoreTokenInfo = coreTokenInfos[initialToken]; + CoreTokenInfo memory finalCoreTokenInfo = coreTokenInfos[finalToken]; + + bool isSponsoredFlow = maxBpsToSponsor > 0; + // In initialToken + (, uint64 amountInEquivalentCore) = HyperCoreLib.maximumEVMSendAmountToAmounts( + amountInEVM, + initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + + bool coreUserAccountExists = HyperCoreLib.coreUserExists(finalUser); + bool impossibleToForwardToCore = !coreUserAccountExists && !finalCoreTokenInfo.canBeUsedForAccountActivation; + + if (impossibleToForwardToCore) { + _fallbackHyperEVMFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalUser, extraBridgingFeesEVM); + emit SwapFlowFallbackAccountActivation(quoteNonce, finalToken); + return; + } + + // In initialToken + uint256 totalAmountBridgedEVM = amountInEVM + extraBridgingFeesEVM; + // In finalToken + uint64 accountActivationFeeCore = coreUserAccountExists ? 0 : finalCoreTokenInfo.accountActivationFeeCore; + // In finalToken + (uint64 minAllowableAmountToForwardCore, uint64 maxAmountToSponsorCore) = _calculateAllowableAmountsForFlow( + totalAmountBridgedEVM, + initialCoreTokenInfo, + finalCoreTokenInfo, + isSponsoredFlow, + maxBpsToSponsor, + maxUserSlippageBps, + accountActivationFeeCore + ); + + uint64 limitPriceX1e8 = _getSuggestedPriceX1e8(finalTokenInfo); + (uint64 sizeX1e8, uint64 tokensToSendCore, uint64 guaranteedLOOut) = _calcLOAmounts( + amountInEquivalentCore, + limitPriceX1e8, + finalTokenInfo.isBuy, + finalTokenInfo.feePpm, + initialCoreTokenInfo, + finalCoreTokenInfo + ); + + if ( + minAllowableAmountToForwardCore > guaranteedLOOut && + minAllowableAmountToForwardCore - guaranteedLOOut > maxAmountToSponsorCore + ) { + // We can't provide the required slippage in a swap flow, try simple transfer flow instead + _executeSimpleTransferFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalUser, extraBridgingFeesEVM); + emit SwapFlowFallbackTooExpensive( + quoteNonce, + minAllowableAmountToForwardCore - guaranteedLOOut, + maxAmountToSponsorCore + ); + return; + } + + uint64 finalCoreSendAmount = isSponsoredFlow ? minAllowableAmountToForwardCore : guaranteedLOOut; + uint64 totalCoreAmountToSponsor = finalCoreSendAmount > guaranteedLOOut + ? finalCoreSendAmount - guaranteedLOOut + : 0; + + uint256 totalEVMAmountToSponsor = 0; + if (totalCoreAmountToSponsor > 0) { + (totalEVMAmountToSponsor, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + totalCoreAmountToSponsor, + finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + } + + if (totalEVMAmountToSponsor > 0) { + if (!_availableInDonationBox(finalToken, totalEVMAmountToSponsor)) { + // We can't provide the required `totalEVMAmountToSponsor` in a swap flow, try simple transfer flow instead + _executeSimpleTransferFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalUser, extraBridgingFeesEVM); + emit SwapFlowFallbackDonationBox(quoteNonce, finalToken, totalEVMAmountToSponsor); + return; + } + } + + // Check that we can safely bridge to HCore (for the trade amount actually needed) + bool isSafeToBridgeMainToken = HyperCoreLib.isCoreAmountSafeToBridge( + initialCoreTokenInfo.coreIndex, + tokensToSendCore, + initialCoreTokenInfo.bridgeSafetyBufferCore + ); + bool isSafeTobridgeSponsorshipFunds = HyperCoreLib.isCoreAmountSafeToBridge( + finalCoreTokenInfo.coreIndex, + totalCoreAmountToSponsor, + finalCoreTokenInfo.bridgeSafetyBufferCore + ); + + if (!isSafeToBridgeMainToken || !isSafeTobridgeSponsorshipFunds) { + _fallbackHyperEVMFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalUser, extraBridgingFeesEVM); + emit SwapFlowFallbackUnsafeToBridge( + quoteNonce, + isSafeToBridgeMainToken, + finalToken, + isSafeTobridgeSponsorshipFunds + ); + return; + } + + // Transfer funds to SwapHandler @ core + SwapHandler swapHandler = finalTokenInfo.swapHandler; + + // 1. Fund SwapHandler @ core with `initialToken`: use it for the trade + (uint256 evmToSendForTrade, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + tokensToSendCore, + initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + // Here, we're sending amount that is <= amountInEVM (came from user's bridge transaction) + IERC20(initialToken).safeTransfer(address(swapHandler), evmToSendForTrade); + swapHandler.transferFundsToSelfOnCore( + initialToken, + initialCoreTokenInfo.coreIndex, + evmToSendForTrade, + initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + + // 2. Fund SwapHandler @ core with `finalToken`: use that for sponsorship + if (totalEVMAmountToSponsor > 0) { + // We checked that this amount is in donationBox before + _getFromDonationBox(finalToken, totalEVMAmountToSponsor); + // These funds just came from donationBox + IERC20(finalToken).safeTransfer(address(swapHandler), totalEVMAmountToSponsor); + swapHandler.transferFundsToSelfOnCore( + finalToken, + finalCoreTokenInfo.coreIndex, + totalEVMAmountToSponsor, + finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + cumulativeSponsoredActivationFee[finalToken] += accountActivationFeeCore > 0 + ? finalCoreTokenInfo.accountActivationFeeEVM + : 0; + cumulativeSponsoredAmount[finalToken] += totalEVMAmountToSponsor; + } + + uint128 cloid = ++nextCloid; + swapHandler.submitLimitOrder(finalTokenInfo, limitPriceX1e8, sizeX1e8, cloid); + + pendingSwaps[quoteNonce] = PendingSwap({ + finalRecipient: finalUser, + finalToken: finalToken, + minCoreAmountFromLO: guaranteedLOOut, + sponsoredCoreAmountPreFunded: totalCoreAmountToSponsor, + limitOrderCloid: cloid + }); + pendingQueue[finalToken].push(quoteNonce); + cloidToQuoteNonce[cloid] = quoteNonce; + + emit SwapFlowInitialized( + quoteNonce, + finalUser, + finalToken, + amountInEVM, + totalEVMAmountToSponsor, + finalCoreSendAmount, + finalTokenInfo.assetIndex, + cloid + ); + } + + /// @notice Finalizes pending queue of swaps for `finalToken` if a corresponsing SwapHandler has enough balance + function finalizePendingSwaps(address finalToken, uint256 maxToProcess) external { + CoreTokenInfo memory coreTokenInfo = coreTokenInfos[finalToken]; + FinalTokenInfo memory finalTokenInfo = finalTokenInfos[finalToken]; + + require(address(finalTokenInfo.swapHandler) != address(0), "Final token not registered"); + require(lastPullFundsBlock[finalToken] < block.number, "Can't finalize twice in the same block"); + lastPullFundsBlock[finalToken] = block.number; + + uint256 head = pendingQueueHead[finalToken]; + bytes32[] storage queue = pendingQueue[finalToken]; + if (head >= queue.length || maxToProcess == 0) return; + + // Note: `availableCore` is the SwapHandler's Core balance for `finalToken`, which monotonically increases + uint64 availableCore = HyperCoreLib.spotBalance(address(finalTokenInfo.swapHandler), coreTokenInfo.coreIndex); + uint256 processed = 0; + + while (head < queue.length && processed < maxToProcess) { + bytes32 nonce = queue[head]; + + PendingSwap storage pendingSwap = pendingSwaps[nonce]; + uint64 totalAmountToForwardToUser = pendingSwap.minCoreAmountFromLO + + pendingSwap.sponsoredCoreAmountPreFunded; + if (availableCore < totalAmountToForwardToUser) { + break; + } + + finalTokenInfo.swapHandler.transferFundsToUserOnCore( + finalTokenInfo.assetIndex, + pendingSwap.finalRecipient, + totalAmountToForwardToUser + ); + + emit SwapFlowCompleted( + nonce, + pendingSwap.finalRecipient, + pendingSwap.finalToken, + pendingSwap.sponsoredCoreAmountPreFunded, + totalAmountToForwardToUser + ); + + availableCore -= totalAmountToForwardToUser; + + // We don't delete `pendingSwaps` state, because we might require it for accounting purposes if we need to + // update the associated limit order + head += 1; + processed += 1; + } + + pendingQueueHead[finalToken] = head; + } + + /// @notice Cancells a pending limit order by `cloid` with an intention to submit a new limit order in its place. To + /// be used for stale limit orders to speed up executing user transactions + function cancelLimitOrderByCloid(uint128 cloid) external onlyPermissionedBot returns (bytes32 quoteNonce) { + quoteNonce = cloidToQuoteNonce[cloid]; + PendingSwap storage pendingSwap = pendingSwaps[quoteNonce]; + FinalTokenInfo memory finalTokenInfo = finalTokenInfos[pendingSwap.finalToken]; + + // Here, cloid == pendingSwap.limitOrderCloid + finalTokenInfo.swapHandler.cancelOrderByCloid(finalTokenInfo.assetIndex, cloid); + // Clear out the cloid. `submitUpdatedLimitOrder` function requires that this is empty. Means that no tracked + // associated limit order is present + delete pendingSwap.limitOrderCloid; + + emit CancelledLimitOrder(quoteNonce, finalTokenInfo.assetIndex, cloid); + } + + /** + * @notice This function is to be used in situations when the limit order that's on the books has become stale and + * we want to speed up the execution. + * @dev This function should be called as a second step after the `cancelLimitOrderByCloid` was already called and + * the order was fully cancelled. It is the responsibility of this function's caller to supply oldPriceX1e8 and + * oldSizeX1e8Left associated with the previous cancelled order. They act as a safeguarding policy that don't allow + * to spend more tokens then the previous limit order wanted to spend to protect the accounting assumptions of the + * current contract. Although the values provided as still fully trusted. + * @dev This functions chooses to ignore the `maxBpsToSponsor` param as it is supposed to be + * used only in rare cases. We choose to pay for the adjusted limit order price completely. + * @param quoteNonce quote nonce is used to uniquely identify user order (PendingSwap) + * @param priceX1e8 price to set for new limit order + * @param oldPriceX1e8 price that was set for the cancelled order + * @param oldSizeX1e8Left size that was remaining on the order that was cancelled + */ + function submitUpdatedLimitOrder( + // old order with some price and some out amount expectations: pendingSwaps (minAmountOutCore, totalSponsoredCore) + // new order with some price and new amount expectations. minAmountOutCore2, totalSponsoredCore + (minAmountOutCore2 - minAmountOutCore) + // oldPriceX1e8, oldSizeX1e8Left -> partial Limit order that we cancelled + // how much tokens that we sent in are still there for us to trade? + // priceX1e8: sz ? + // partialBudgetRemaining + bytes32 quoteNonce, + uint64 priceX1e8, + uint64 oldPriceX1e8, + uint64 oldSizeX1e8Left + ) external onlyPermissionedBot { + PendingSwap storage pendingSwap = pendingSwaps[quoteNonce]; + require(pendingSwap.limitOrderCloid == 0, "Cannot resubmit LO for non-empty cloid"); + + address finalToken = pendingSwap.finalToken; + FinalTokenInfo memory finalTokenInfo = finalTokenInfos[finalToken]; + CoreTokenInfo memory initialTokenInfo = coreTokenInfos[finalToken]; + CoreTokenInfo memory finalTokenCoreInfo = coreTokenInfos[finalToken]; + + // Remaining budget of tokens attributable to the "old limit order" (now cancelled) + uint64 coreBudgetRemaining = _calcRemainingLOBudget( + oldPriceX1e8, + oldSizeX1e8Left, + finalTokenInfo.isBuy, + finalTokenInfo.feePpm, + initialTokenInfo, + finalTokenCoreInfo + ); + (uint64 szX1e8, , uint64 guaranteedCoreOut) = _calcLOAmounts( + coreBudgetRemaining, + priceX1e8, + finalTokenInfo.isBuy, + finalTokenInfo.feePpm, + initialTokenInfo, + finalTokenCoreInfo + ); + + (, , uint64 guaranteedCoreOutOld) = _calcLOAmounts( + coreBudgetRemaining, + oldPriceX1e8, + finalTokenInfo.isBuy, + finalTokenInfo.feePpm, + initialTokenInfo, + finalTokenCoreInfo + ); + + uint64 sponsorDeltaCore; + if (guaranteedCoreOut < guaranteedCoreOutOld) { + sponsorDeltaCore = guaranteedCoreOutOld - guaranteedCoreOut; + } else { + sponsorDeltaCore = 0; + emit BetterPricedLOSubmitted(quoteNonce, oldPriceX1e8, priceX1e8); + } + + // Submit new Limit Order + uint128 cloid = ++nextCloid; + SwapHandler swapHandler = finalTokenInfo.swapHandler; + swapHandler.submitLimitOrder(finalTokenInfo, priceX1e8, szX1e8, cloid); + pendingSwap.limitOrderCloid = cloid; + + // Send extra sponsorship money to cover for the guaranteed amount out difference + if (sponsorDeltaCore > 0) { + uint256 sponsorDeltaEvm; + (sponsorDeltaEvm, sponsorDeltaCore) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + sponsorDeltaCore, + finalTokenCoreInfo.tokenInfo.evmExtraWeiDecimals + ); + + _getFromDonationBox(finalToken, sponsorDeltaEvm); + IERC20(finalToken).safeTransfer(address(swapHandler), sponsorDeltaEvm); + cumulativeSponsoredAmount[finalToken] += sponsorDeltaEvm; + if ( + !HyperCoreLib.isCoreAmountSafeToBridge( + finalTokenCoreInfo.coreIndex, + sponsorDeltaCore, + finalTokenCoreInfo.bridgeSafetyBufferCore + ) + ) { + // Can't add required sponsored funds to balance out the accounting. Have to revert + revert("Bridging is unsafe"); + } + swapHandler.transferFundsToSelfOnCore( + finalToken, + finalTokenCoreInfo.coreIndex, + sponsorDeltaEvm, + finalTokenCoreInfo.tokenInfo.evmExtraWeiDecimals + ); + + uint64 fullOldGuranteedOut = pendingSwap.minCoreAmountFromLO; + pendingSwap.minCoreAmountFromLO = fullOldGuranteedOut + guaranteedCoreOut - guaranteedCoreOutOld; + pendingSwap.sponsoredCoreAmountPreFunded = pendingSwap.sponsoredCoreAmountPreFunded + sponsorDeltaCore; + } + + emit ReplacedOldLimitOrder(quoteNonce, cloid, priceX1e8, szX1e8, oldPriceX1e8, oldSizeX1e8Left); + } + + /// @notice Forwards `amount` plus potential sponsorship funds (for bridging fee) to user on HyperEVM + function _fallbackHyperEVMFlow( + uint256 amount, + bytes32 quoteNonce, + uint256 maxBpsToSponsor, + address finalRecipient, + uint256 extraFeesToSponsor + ) internal { + address finalToken = baseToken; + uint256 maxEvmAmountToSponsor = ((amount + extraFeesToSponsor) * maxBpsToSponsor) / BPS_SCALAR; + uint256 sponsorshipFundsToForward = extraFeesToSponsor > maxEvmAmountToSponsor + ? maxEvmAmountToSponsor + : extraFeesToSponsor; + + if (!_availableInDonationBox(baseToken, sponsorshipFundsToForward)) { + sponsorshipFundsToForward = 0; + } + if (sponsorshipFundsToForward > 0) { + _getFromDonationBox(baseToken, sponsorshipFundsToForward); + } + uint256 totalAmountToForward = amount + sponsorshipFundsToForward; + IERC20(finalToken).safeTransfer(finalRecipient, totalAmountToForward); + cumulativeSponsoredAmount[finalToken] += sponsorshipFundsToForward; + emit FallbackHyperEVMFlowCompleted( + quoteNonce, + finalRecipient, + finalToken, + amount, + sponsorshipFundsToForward, + totalAmountToForward + ); + } + + function _setCoreTokenInfo( + address token, + uint32 coreIndex, + bool canBeUsedForAccountActivation, + uint64 accountActivationFeeCore, + uint64 bridgeSafetyBufferCore + ) internal { + HyperCoreLib.TokenInfo memory tokenInfo = HyperCoreLib.tokenInfo(coreIndex); + require(tokenInfo.evmContract == token, "Token mismatch"); + + (uint256 accountActivationFeeEVM, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + accountActivationFeeCore, + tokenInfo.evmExtraWeiDecimals + ); + + coreTokenInfos[token] = CoreTokenInfo({ + tokenInfo: tokenInfo, + coreIndex: coreIndex, + canBeUsedForAccountActivation: canBeUsedForAccountActivation, + accountActivationFeeEVM: accountActivationFeeEVM, + accountActivationFeeCore: accountActivationFeeCore, + bridgeSafetyBufferCore: bridgeSafetyBufferCore + }); + + // TODO: emit event? maybe not if we are over limit + } + + /// @notice Gets `amount` of `token` from donationBox. Reverts if unsuccessful + function _getFromDonationBox(address token, uint256 amount) internal { + if (!_availableInDonationBox(token, amount)) { + revert DonationBoxInsufficientFundsError(token, amount); + } + donationBox.withdraw(IERC20(token), amount); + } + + /// @notice Checks if `amount` of `token` is available to withdraw from donationBox + function _availableInDonationBox(address token, uint256 amount) internal returns (bool available) { + available = IERC20(token).balanceOf(address(donationBox)) >= amount; + if (!available) { + emit DonationBoxInsufficientFunds(token, amount); + } + } + + function _getAccountActivationFeeEVM(address token, address recipient) internal view returns (uint256) { + bool accountActivated = HyperCoreLib.coreUserExists(recipient); + + return accountActivated ? 0 : coreTokenInfos[token].accountActivationFeeEVM; + } + + function _calculateAllowableAmountsForFlow( + uint256 totalAmountBridgedEVM, + CoreTokenInfo memory initialCoreTokenInfo, + CoreTokenInfo memory finalCoreTokenInfo, + bool isSponsoredFlow, + uint256 maxBpsToSponsor, + uint256 maxUserSlippageBps, + uint64 accountActivationFeeCore + ) internal pure returns (uint64 minAllowableAmountToForwardCore, uint64 maxAmountToSponsorCore) { + (, uint64 feelessAmountCoreInitialToken) = HyperCoreLib.maximumEVMSendAmountToAmounts( + totalAmountBridgedEVM, + initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + uint64 feelessAmountCoreFinalToken = HyperCoreLib.convertCoreDecimalsSimple( + feelessAmountCoreInitialToken, + initialCoreTokenInfo.tokenInfo.weiDecimals, + finalCoreTokenInfo.tokenInfo.weiDecimals + ); + if (isSponsoredFlow) { + // toCore(totalEvmBridgedAmount) + coreAccountActivationFee + maxAmountToSponsorCore = uint64((feelessAmountCoreFinalToken * maxBpsToSponsor) / BPS_SCALAR); + minAllowableAmountToForwardCore = feelessAmountCoreFinalToken + accountActivationFeeCore; + } else { + // toCore(amountInEquivalentCore) - slippage + coreAccountActivationFee + maxAmountToSponsorCore = 0; + minAllowableAmountToForwardCore = + uint64((feelessAmountCoreFinalToken * (BPS_SCALAR - maxUserSlippageBps)) / BPS_SCALAR) + + accountActivationFeeCore; + } + } + + /************************************** + * SWEEP FUNCTIONS * + **************************************/ + + function sweepErc20(address token, uint256 amount) external onlyFundsSweeper { + IERC20(token).safeTransfer(msg.sender, amount); + } + + function sweepErc20FromDonationBox(address token, uint256 amount) external onlyFundsSweeper { + _getFromDonationBox(token, amount); + IERC20(token).safeTransfer(msg.sender, amount); + } + + function sweepERC20FromSwapHandler(address token, uint256 amount) external onlyFundsSweeper { + SwapHandler swapHandler = finalTokenInfos[token].swapHandler; + swapHandler.sweepErc20(token, amount); + IERC20(token).safeTransfer(msg.sender, amount); + } + + function sweepOnCore(address token, uint64 amount) external onlyFundsSweeper { + HyperCoreLib.transferERC20CoreToCore(coreTokenInfos[token].coreIndex, msg.sender, amount); + } + + // TODO? Alternative flow: make this permissionless, send money SwapHandler @ core -> DonationBox @ core => DonationBox pulls money from Core to Self (needs DonationBox code change) + function sweepOnCoreFromSwapHandler(address token, uint64 amount) external onlyPermissionedBot { + // We first want to make sure there are not pending limit orders for this token + uint256 head = pendingQueueHead[token]; + if (head < pendingQueue[token].length) { + revert("Cannot sweep on core if there are pending limit orders"); + } + + // Prevent pulling fantom funds (e.g. if finalizePendingSwaps reads stale balance because of this fund pull) + require(lastPullFundsBlock[token] < block.number, "Can't pull funds twice in the same block"); + lastPullFundsBlock[token] = block.number; + + SwapHandler swapHandler = finalTokenInfos[token].swapHandler; + swapHandler.transferFundsToUserOnCore(finalTokenInfos[token].assetIndex, msg.sender, amount); + } + + /// @notice Reads the current spot price from HyperLiquid and applies a configured suggested discount for faster execution + function _getSuggestedPriceX1e8( + FinalTokenInfo memory finalTokenInfo + ) internal view returns (uint64 limitPriceX1e8) { + uint64 spotX1e8 = HyperCoreLib.spotPx(finalTokenInfo.assetIndex); + // Buy above spot, sell below spot + uint256 adjBps = finalTokenInfo.isBuy + ? (BPS_SCALAR + finalTokenInfo.suggestedDiscountBps) + : (BPS_SCALAR - finalTokenInfo.suggestedDiscountBps); + limitPriceX1e8 = uint64((uint256(spotX1e8) * adjBps) / BPS_SCALAR); + } + + /************************************** + * LIMIT ORDER CALCULATION UTILS * + **************************************/ + + /// @notice Given the size and price of a limit order, returns the remaining `budget` that Limit order expects to spend + function _calcRemainingLOBudget( + uint64 pxX1e8, + uint64 szX1e8, + bool isBuy, + uint64 feePpm, + CoreTokenInfo memory tokenHave, + CoreTokenInfo memory tokenWant + ) internal pure returns (uint64 budget) { + CoreTokenInfo memory _quoteToken = isBuy ? tokenHave : tokenWant; + CoreTokenInfo memory _baseToken = isBuy ? tokenWant : tokenHave; + + if (isBuy) { + // We have quoteTokens. Estimate how many quoteTokens we are GUARANTEED to have had to enqueue the LO in the first place (proportional) + // qTR is quote tokens real. qTD quote token decimals. + // szX1e8 * pxX1e8 / 10 ** 8 = qTX1e8Net + // qTR * 10 ** 8 * (10 ** 6 - feePpm) / (10 ** 6 * 10 ** qTD) = qTX1e8Net + // qTR = szX1e8 * pxX1e8 * 10 ** 6 * 10 ** qTD / (10 ** 8 * 10 ** 8 * (10 ** 6 - feePpm)) + budget = uint64( + (uint256(szX1e8) * uint256(pxX1e8) * PPM_SCALAR * 10 ** (_quoteToken.tokenInfo.weiDecimals)) / + (10 ** 16 * (PPM_SCALAR - feePpm)) + ); + } else { + // We have baseTokens. Convert `szX1e8` to base token budget. A simple decimals conversion here + budget = uint64((szX1e8 * 10 ** (_baseToken.tokenInfo.weiDecimals)) / 10 ** 8); + } + } + + /** + * @notice The purpose of this function is best described by its return params. Given a budget and a price, determines + * size to set, tokens to send, and min amount received. + * @return szX1e8 size value to supply when sending a limit order to HyperCore + * @return coreToSend the number of tokens to send for this trade to suceed; <= coreBudget + * @return guaranteedCoreOut the ABSOLUTE MINIMUM that we're guaranteed to receive when the limit order fully settles + */ + function _calcLOAmounts( + uint64 coreBudget, + uint64 pxX1e8, + bool isBuy, + uint64 feePpm, + CoreTokenInfo memory tokenHave, + CoreTokenInfo memory tokenWant + ) internal pure returns (uint64 szX1e8, uint64 coreToSend, uint64 guaranteedCoreOut) { + if (isBuy) { + return + _calcLOAmountsBuy( + coreBudget, + pxX1e8, + tokenHave.tokenInfo.weiDecimals, + tokenHave.tokenInfo.szDecimals, + tokenWant.tokenInfo.weiDecimals, + tokenWant.tokenInfo.szDecimals, + feePpm + ); + } else { + return + _calcLOAmountsSell( + coreBudget, + pxX1e8, + tokenWant.tokenInfo.weiDecimals, + tokenWant.tokenInfo.szDecimals, + tokenHave.tokenInfo.weiDecimals, + tokenHave.tokenInfo.szDecimals, + feePpm + ); + } + } + + function _calcLOAmountsBuy( + uint64 quoteBudget, + uint64 pxX1e8, + uint8 quoteD, + uint8 quoteSz, + uint8 baseD, + uint8 baseSz, + uint64 feePpm + ) internal pure returns (uint64 szX1e8, uint64 tokensToSendCore, uint64 minAmountOutCore) { + uint256 px = (pxX1e8 * 10 ** (PX_D + quoteSz)) / 10 ** (8 + baseSz); + // quoteD >= quoteSz always + uint256 sz = (quoteBudget * (PPM_SCALAR - feePpm) * 10 ** PX_D) / (PPM_SCALAR * px * 10 ** (quoteD - quoteSz)); + // baseD >= baseSz always + uint64 outBaseNet = uint64(sz * 10 ** (baseD - baseSz)); + szX1e8 = uint64((uint256(outBaseNet) * 10 ** 8) / 10 ** baseD); + tokensToSendCore = quoteBudget; + minAmountOutCore = outBaseNet; + } + + function _calcLOAmountsSell( + uint64 baseBudget, + uint64 pxX1e8, + uint8 quoteD, + uint8 quoteSz, + uint8 baseD, + uint8 baseSz, + uint64 feePpm + ) internal pure returns (uint64 szX1e8, uint64 tokensToSendCore, uint64 minAmountOutCore) { + uint64 sz = uint64(baseBudget / 10 ** (baseD - baseSz)); + uint256 px = (pxX1e8 * 10 ** (PX_D + quoteSz)) / 10 ** (8 + baseSz); + + // quoteD >= quoteSz always + uint64 outQuoteGross = uint64((px * sz * 10 ** (quoteD - quoteSz)) / 10 ** PX_D); + uint64 outQuoteNet = uint64((outQuoteGross * (PPM_SCALAR - feePpm)) / PPM_SCALAR); + szX1e8 = uint64((sz * 10 ** 8) / 10 ** baseSz); + tokensToSendCore = baseBudget; + minAmountOutCore = outQuoteNet; + } +} diff --git a/contracts/periphery/mintburn/Structs.sol b/contracts/periphery/mintburn/Structs.sol new file mode 100644 index 000000000..7dd372091 --- /dev/null +++ b/contracts/periphery/mintburn/Structs.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol"; +import { SwapHandler } from "./SwapHandler.sol"; + +// Info about the token on HyperCore. +struct CoreTokenInfo { + // The token info on HyperCore. + HyperCoreLib.TokenInfo tokenInfo; + // The HyperCore index id of the token. + uint64 coreIndex; + // Whether the token can be used for account activation fee. + bool canBeUsedForAccountActivation; + // The account activation fee for the token. + uint256 accountActivationFeeEVM; + // The account activation fee for the token on Core. + uint64 accountActivationFeeCore; + // Bridge buffer to use when checking safety of bridging evm -> core. In core units + uint64 bridgeSafetyBufferCore; +} + +struct FinalTokenInfo { + // The index of the market where we're going to swap baseToken -> finalToken + uint32 assetIndex; + // To go baseToken -> finalToken, do we have to enqueue a buy or a sell? + bool isBuy; + // The fee Hyperliquid charges for Limit orders in the market; in parts per million, e.g. 1.4 bps = 140 ppm + uint32 feePpm; + // When enqueuing a limit order, use this to set a price "a bit worse than market" for faster execution + uint32 suggestedDiscountBps; + // Contract where the accounting for all baseToken -> finalToken accounting happens. One pre finalToken + SwapHandler swapHandler; +} diff --git a/contracts/periphery/mintburn/SwapHandler.sol b/contracts/periphery/mintburn/SwapHandler.sol new file mode 100644 index 000000000..0b237c370 --- /dev/null +++ b/contracts/periphery/mintburn/SwapHandler.sol @@ -0,0 +1,69 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol"; +import { FinalTokenInfo } from "./Structs.sol"; + +contract SwapHandler { + address public immutable parentHandler; + + constructor() { + parentHandler = msg.sender; + } + + modifier onlyParentHandler() { + require(msg.sender == parentHandler, "Not parent handler"); + _; + } + + function activateCoreAccount( + address erc20EVMAddress, + uint64 erc20CoreIndex, + uint256 amountEVM, + int8 decimalDiff + ) external onlyParentHandler { + HyperCoreLib.transferERC20EVMToSelfOnCore(erc20EVMAddress, erc20CoreIndex, amountEVM, decimalDiff); + } + + function transferFundsToSelfOnCore( + address erc20EVMAddress, + uint64 erc20CoreIndex, + uint256 amountEVM, + int8 decimalDiff + ) external onlyParentHandler { + HyperCoreLib.transferERC20EVMToSelfOnCore(erc20EVMAddress, erc20CoreIndex, amountEVM, decimalDiff); + } + + function transferFundsToUserOnCore( + uint64 erc20CoreIndex, + address to, + uint64 amountCore + ) external onlyParentHandler { + HyperCoreLib.transferERC20CoreToCore(erc20CoreIndex, to, amountCore); + } + + function submitLimitOrder( + FinalTokenInfo memory finalTokenInfo, + uint64 limitPriceX1e8, + uint64 sizeX1e8, + uint128 cloid + ) external onlyParentHandler { + HyperCoreLib.submitLimitOrder( + finalTokenInfo.assetIndex, + finalTokenInfo.isBuy, + limitPriceX1e8, + sizeX1e8, + false, + HyperCoreLib.Tif.GTC, + cloid + ); + } + + function cancelOrderByCloid(uint32 assetIndex, uint128 cloid) external onlyParentHandler { + HyperCoreLib.cancelOrderByCloid(assetIndex, cloid); + } + + function sweepErc20(address token, uint256 amount) external onlyParentHandler { + IERC20(token).transfer(msg.sender, amount); + } +} diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol new file mode 100644 index 000000000..6b91bf295 --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol @@ -0,0 +1,109 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { IMessageTransmitterV2 } from "../../../external/interfaces/CCTPInterfaces.sol"; +import { SponsoredCCTPQuoteLib } from "../../../libraries/SponsoredCCTPQuoteLib.sol"; +import { SponsoredCCTPInterface } from "../../../interfaces/SponsoredCCTPInterface.sol"; +import { Bytes32ToAddress } from "../../../libraries/AddressConverters.sol"; +import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; + +contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecutor { + using SafeERC20 for IERC20Metadata; + using Bytes32ToAddress for bytes32; + + /// @notice The CCTP message transmitter contract. + IMessageTransmitterV2 public immutable cctpMessageTransmitter; + + /// @notice The public key of the signer that was used to sign the quotes. + address public signer; + + /// @notice Allow a buffer for quote deadline validation. CCTP transfer might have taken a while to finalize + uint256 public quoteDeadlineBuffer = 30 minutes; + + /// @notice A mapping of used nonces to prevent replay attacks. + mapping(bytes32 => bool) public usedNonces; + + constructor( + address _cctpMessageTransmitter, + address _signer, + address _donationBox, + address _baseToken, + uint32 _coreIndex, + bool _canBeUsedForAccountActivation, + uint64 _accountActivationFeeCore, + uint64 _bridgeSafetyBufferCore + ) + HyperCoreFlowExecutor( + _donationBox, + _baseToken, + _coreIndex, + _canBeUsedForAccountActivation, + _accountActivationFeeCore, + _bridgeSafetyBufferCore + ) + { + cctpMessageTransmitter = IMessageTransmitterV2(_cctpMessageTransmitter); + signer = _signer; + } + + function setSigner(address _signer) external onlyDefaultAdmin { + signer = _signer; + } + + function setQuoteDeadlineBuffer(uint256 _quoteDeadlineBuffer) external onlyDefaultAdmin { + quoteDeadlineBuffer = _quoteDeadlineBuffer; + } + + function receiveMessage(bytes memory message, bytes memory attestation, bytes memory signature) external { + cctpMessageTransmitter.receiveMessage(message, attestation); + + // If the hook data is invalid or the mint recipient is not this contract we cannot process the message and therefore we return. + // In this case the funds will be kept in this contract + if (!SponsoredCCTPQuoteLib.validateMessage(message)) { + return; + } + + (SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, uint256 feeExecuted) = SponsoredCCTPQuoteLib + .getSponsoredCCTPQuoteData(message); + + bool isQuoteValid = _isQuoteValid(quote, signature); + if (isQuoteValid) { + usedNonces[quote.nonce] = true; + } + + _executeFlow( + // We subtract the feeExecuted from the `amount` as the `amount` is the what the user pays on the source chain. + quote.amount - feeExecuted, + quote.nonce, + // If the quote is invalid we don't sponsor the flow or the extra fees + isQuoteValid ? quote.maxBpsToSponsor : 0, + quote.maxUserSlippageBps, + quote.finalRecipient.toAddress(), + // If the quote is invalid we don't want to swap, so we use the base token as the final token + isQuoteValid ? quote.finalToken.toAddress() : baseToken, + isQuoteValid ? feeExecuted : 0 + ); + + emit SponsoredMintAndWithdraw( + quote.nonce, + quote.finalRecipient, + quote.finalToken, + quote.amount, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps + ); + } + + function _isQuoteValid( + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, + bytes memory signature + ) internal view returns (bool) { + return + SponsoredCCTPQuoteLib.validateSignature(signer, quote, signature) && + !usedNonces[quote.nonce] && + quote.deadline + quoteDeadlineBuffer >= block.timestamp; + } +} diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol new file mode 100644 index 000000000..3df0494cd --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol @@ -0,0 +1,79 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { ITokenMessengerV2 } from "../../../external/interfaces/CCTPInterfaces.sol"; + +import { SponsoredCCTPQuoteLib } from "../../../libraries/SponsoredCCTPQuoteLib.sol"; +import { SponsoredCCTPInterface } from "../../../interfaces/SponsoredCCTPInterface.sol"; + +contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { + using SafeERC20 for IERC20; + + ITokenMessengerV2 public immutable cctpTokenMessenger; + + uint32 public immutable sourceDomain; + + address public signer; + + mapping(bytes32 => bool) public usedNonces; + + constructor(address _cctpTokenMessenger, uint32 _sourceDomain, address _signer) { + cctpTokenMessenger = ITokenMessengerV2(_cctpTokenMessenger); + sourceDomain = _sourceDomain; + signer = _signer; + } + + using SponsoredCCTPQuoteLib for bytes; + + function depositForBurn(SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, bytes memory signature) external { + if (!SponsoredCCTPQuoteLib.validateSignature(signer, quote, signature)) revert InvalidSignature(); + if (usedNonces[quote.nonce]) revert InvalidNonce(); + if (quote.deadline < block.timestamp) revert InvalidDeadline(); + if (quote.sourceDomain != sourceDomain) revert InvalidSourceDomain(); + + ( + uint256 amount, + uint32 destinationDomain, + bytes32 mintRecipient, + address burnToken, + bytes32 destinationCaller, + uint256 maxFee, + uint32 minFinalityThreshold, + bytes memory hookData + ) = SponsoredCCTPQuoteLib.getDespoitForBurnData(quote); + + IERC20(burnToken).safeTransferFrom(msg.sender, address(this), amount); + IERC20(burnToken).safeApprove(address(cctpTokenMessenger), amount); + + usedNonces[quote.nonce] = true; + + cctpTokenMessenger.depositForBurnWithHook( + amount, + destinationDomain, + mintRecipient, + burnToken, + destinationCaller, + maxFee, + minFinalityThreshold, + hookData + ); + + emit SponsoredDepositForBurn( + quote.nonce, + msg.sender, + quote.finalRecipient, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalToken, + signature + ); + } + + function setSigner(address _signer) external onlyOwner { + signer = _signer; + } +} diff --git a/deploy/consts.ts b/deploy/consts.ts index 8e5535379..15d00e33f 100644 --- a/deploy/consts.ts +++ b/deploy/consts.ts @@ -184,6 +184,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } [CHAIN_IDs.ARBITRUM]: { l2GatewayRouter: "0x5288c571Fd7aD117beA99bF60FE0846C4E84F933", cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", uniswapV3SwapRouter: "0xE592427A0AEce92De3Edee1F18E0157C05861564", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", @@ -193,6 +194,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } }, [CHAIN_IDs.HYPEREVM]: { cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", }, [CHAIN_IDs.PLASMA]: { @@ -201,6 +203,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } [CHAIN_IDs.POLYGON]: { fxChild: "0x8397259c983751DAf40400790063935a11afa28a", cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", uniswapV3SwapRouter: "0xE592427A0AEce92De3Edee1F18E0157C05861564", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", @@ -218,6 +221,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } }, [CHAIN_IDs.OPTIMISM]: { cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", uniswapV3SwapRouter: "0xE592427A0AEce92De3Edee1F18E0157C05861564", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", @@ -229,6 +233,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } }, [CHAIN_IDs.BASE]: { cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", uniswapV3SwapRouter: "0x2626664c2603336E57B271c5C0b26F421741e481", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", @@ -256,6 +261,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } [CHAIN_IDs.LINEA]: { lineaMessageService: "0x508Ca82Df566dCD1B0DE8296e70a96332cD644ec", cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", lineaTokenBridge: "0x353012dc4a9A6cF55c941bADC267f82004A8ceB9", permit2: "0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768", // PancakeSwap Permit2 }, @@ -277,6 +283,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } }, [CHAIN_IDs.UNICHAIN]: { cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", }, [CHAIN_IDs.UNICHAIN_SEPOLIA]: { diff --git a/foundry.toml b/foundry.toml index 6a8bfe380..5bc8bcd9a 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,6 +7,7 @@ src = "contracts" out = "out" test = "test/evm/foundry" libs = ["node_modules", "lib"] +cache_path = "cache-foundry" remappings = [ "@across-protocol/=node_modules/@across-protocol/", "@ensdomains/=node_modules/@ensdomains/", @@ -62,6 +63,11 @@ soneium = "${NODE_URL_1868}" unichain = "${NODE_URL_130}" world-chain = "${NODE_URL_480}" zksync = "${NODE_URL_324}" +hyperevm = "${NODE_URL_999}" + +# testnet +arbitrum_testnet = "${NODE_URL_421614}" +hyperevm_testnet = "${NODE_URL_998}" [etherscan] diff --git a/generated/constants.json b/generated/constants.json index 9a3eb5b42..50ae54d74 100644 --- a/generated/constants.json +++ b/generated/constants.json @@ -566,6 +566,7 @@ "L2_ADDRESS_MAP": { "10": { "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "uniswapV3SwapRouter": "0xE592427A0AEce92De3Edee1F18E0157C05861564", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" @@ -575,11 +576,13 @@ }, "130": { "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "137": { "fxChild": "0x8397259c983751DAf40400790063935a11afa28a", "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "uniswapV3SwapRouter": "0xE592427A0AEce92De3Edee1F18E0157C05861564", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" @@ -608,6 +611,7 @@ }, "999": { "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "1135": { @@ -626,6 +630,7 @@ }, "8453": { "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "uniswapV3SwapRouter": "0x2626664c2603336E57B271c5C0b26F421741e481", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" @@ -646,6 +651,7 @@ "42161": { "l2GatewayRouter": "0x5288c571Fd7aD117beA99bF60FE0846C4E84F933", "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "uniswapV3SwapRouter": "0xE592427A0AEce92De3Edee1F18E0157C05861564", "1inchV6Router": "0x111111125421cA6dc452d289314280a0f8842A65", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" @@ -656,6 +662,7 @@ "59144": { "lineaMessageService": "0x508Ca82Df566dCD1B0DE8296e70a96332cD644ec", "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "lineaTokenBridge": "0x353012dc4a9A6cF55c941bADC267f82004A8ceB9", "permit2": "0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768" }, diff --git a/hardhat.config.ts b/hardhat.config.ts index 64e30d745..62a0ddd1e 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -162,6 +162,7 @@ const config: HardhatUserConfig = { // gasPrice: 3e8, // 0.3 GWEI // gasMultiplier: 4.0, hyperevm: getDefaultHardhatConfig(CHAIN_IDs.HYPEREVM), + "hyperevm-testnet": getDefaultHardhatConfig(CHAIN_IDs.HYPEREVM_TESTNET, true), "polygon-amoy": getDefaultHardhatConfig(CHAIN_IDs.POLYGON_AMOY), base: getDefaultHardhatConfig(CHAIN_IDs.BASE), "base-sepolia": getDefaultHardhatConfig(CHAIN_IDs.BASE_SEPOLIA, true), diff --git a/script/utils/Constants.sol b/script/utils/Constants.sol index 1d4b3551b..1b1d65de4 100644 --- a/script/utils/Constants.sol +++ b/script/utils/Constants.sol @@ -61,26 +61,6 @@ contract Constants is Script { address zkUsdcSharedBridge_324; } - // L2 Address Map - struct L2Addresses { - address l2GatewayRouter; - address fxChild; - address cctpTokenMessenger; - address cctpMessageTransmitter; - address uniswapV3SwapRouter; - address helios; - address zkErc20Bridge; - address zkUSDCBridge; - address lineaMessageService; - address cctpV2TokenMessenger; - address lineaTokenBridge; - address scrollERC20GatewayRouter; - address scrollGasPriceOracle; - address scrollMessenger; - address l2Weth; - address polygonZkEvmBridge; - } - // OP Stack Address Map struct OpStackAddresses { address L1CrossDomainMessenger; @@ -222,20 +202,20 @@ contract Constants is Script { } // Circle domain IDs mapping - function getCircleDomainId(uint256 chainId) public view returns (uint256) { - int256 cctpDomain = _getCctpDomain(chainId); + function getCircleDomainId(uint256 chainId) public view returns (uint32) { + int32 cctpDomain = _getCctpDomain(chainId); if (cctpDomain == -1) { revert("Circle domain ID not found"); } - return uint256(cctpDomain); + return uint32(cctpDomain); } function hasCctpDomain(uint256 chainId) public view returns (bool) { return _getCctpDomain(chainId) != -1; } - function _getCctpDomain(uint256 chainId) internal view returns (int256) { - return vm.parseJsonInt(file, string.concat(".PUBLIC_NETWORKS.", vm.toString(chainId), ".cctpDomain")); + function _getCctpDomain(uint256 chainId) internal view returns (int32) { + return int32(vm.parseJsonInt(file, string.concat(".PUBLIC_NETWORKS.", vm.toString(chainId), ".cctpDomain"))); } function getOftEid(uint256 chainId) public view returns (uint256) { From 4b2f04ea69b1dcdb1a1d1e73b073977568505f3f Mon Sep 17 00:00:00 2001 From: Ihor Farion <65650773+grasphoper@users.noreply.github.com> Date: Sun, 19 Oct 2025 20:35:50 -0700 Subject: [PATCH 07/47] feat: sponsored bridging -- OFT track (#1134) * commit before too late Signed-off-by: Ihor Farion * remove lz deps; reimplement minimal lz options functionality Signed-off-by: Ihor Farion * some polish and comments Signed-off-by: Ihor Farion * polish Signed-off-by: Ihor Farion * move things around Signed-off-by: Ihor Farion * add event for sponsored sends tracking Signed-off-by: Ihor Farion * polish Signed-off-by: Ihor Farion * add barebones DstOFTHandler Signed-off-by: Ihor Farion * improve lib quality Signed-off-by: Ihor Farion * added OFTComposeMsgCodec Signed-off-by: Ihor Farion * progress .. Flow implementaions left Signed-off-by: Ihor Farion * rough first draft of transfer-only flow Signed-off-by: Ihor Farion * add AccessControl Signed-off-by: Ihor Farion * pull account creation funds from donationbox Signed-off-by: Ihor Farion * remove HyperCoreLib Signed-off-by: Ihor Farion * first ROUGHEST draft of swap flow implementation Signed-off-by: Ihor Farion * a more complete implementation Signed-off-by: Ihor Farion * copy Forwarder + misc helper contracts into this branch Signed-off-by: Ihor Farion * adjust DstOFTHandler to use HyperCoreForwarder-implemented flows, instead of implementing flows from Handler direcly Signed-off-by: Ihor Farion * update error messageas Signed-off-by: Ihor Farion * updates Signed-off-by: Ihor Farion * added gas limits and max slippage bps Signed-off-by: Faisal Usmani * polish Signed-off-by: Ihor Farion * update HyperCoreForwarder import Signed-off-by: Ihor Farion * add maxUserSlippageBps to emitted event Signed-off-by: Ihor Farion * polish Signed-off-by: Ihor Farion * fix typechain oddness Signed-off-by: Ihor Farion * update HyperCoreFlowExecutor Signed-off-by: Ihor Farion * Deploy script Signed-off-by: Faisal Usmani * idk. fixing typechain :) Signed-off-by: Ihor Farion * deploy + test scripts + fix bugs Signed-off-by: Ihor Farion * update .gitignore and script Signed-off-by: Ihor Farion * fix Signed-off-by: Ihor Farion --------- Signed-off-by: Ihor Farion Signed-off-by: Faisal Usmani Co-authored-by: Faisal Usmani --- .../interfaces/ILayerZeroComposer.sol | 25 +++ contracts/interfaces/IOFT.sol | 12 ++ contracts/libraries/BytesLib.sol | 15 ++ contracts/libraries/MinimalLZOptions.sol | 124 +++++++++++++ contracts/libraries/OFTComposeMsgCodec.sol | 97 ++++++++++ .../sponsored-oft/ComposeMsgCodec.sol | 53 ++++++ .../mintburn/sponsored-oft/DstOFTHandler.sol | 170 ++++++++++++++++++ .../mintburn/sponsored-oft/QuoteSignLib.sol | 46 +++++ .../SponsoredOFTSrcPeriphery.sol | 155 ++++++++++++++++ .../mintburn/sponsored-oft/Structs.sol | 34 ++++ deploy/consts.ts | 1 + generated/constants.json | 1 + script/115DeploySrcOFTPeriphery.sol | 35 ++++ script/116DeployDstOFTHandler.sol | 52 ++++++ script/mintburn/CreateSponsoredDeposit.sol | 138 ++++++++++++++ script/mintburn/SetAuthorizedPeriphery.s.sol | 30 ++++ .../chain-adapters/Arbitrum_Adapter.ts | 4 +- .../hardhat/chain-adapters/Polygon_Adapter.ts | 4 +- .../Arbitrum_SpokePool.ts | 4 +- .../Polygon_SpokePool.ts | 4 +- 20 files changed, 996 insertions(+), 8 deletions(-) create mode 100644 contracts/external/interfaces/ILayerZeroComposer.sol create mode 100644 contracts/libraries/MinimalLZOptions.sol create mode 100644 contracts/libraries/OFTComposeMsgCodec.sol create mode 100644 contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol create mode 100644 contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol create mode 100644 contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol create mode 100644 contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol create mode 100644 contracts/periphery/mintburn/sponsored-oft/Structs.sol create mode 100644 script/115DeploySrcOFTPeriphery.sol create mode 100644 script/116DeployDstOFTHandler.sol create mode 100644 script/mintburn/CreateSponsoredDeposit.sol create mode 100644 script/mintburn/SetAuthorizedPeriphery.s.sol diff --git a/contracts/external/interfaces/ILayerZeroComposer.sol b/contracts/external/interfaces/ILayerZeroComposer.sol new file mode 100644 index 000000000..1fdd962d6 --- /dev/null +++ b/contracts/external/interfaces/ILayerZeroComposer.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0; + +/** + * @title ILayerZeroComposer + * @dev Copied over from https://github.com/LayerZero-Labs/LayerZero-v2/blob/2ff4988f85b5c94032eb71bbc4073e69c078179d/packages/layerzero-v2/evm/protocol/contracts/interfaces/ILayerZeroComposer.sol#L8 + */ +interface ILayerZeroComposer { + /** + * @notice Composes a LayerZero message from an OApp. + * @param _from The address initiating the composition, typically the OApp where the lzReceive was called. + * @param _guid The unique identifier for the corresponding LayerZero src/dst tx. + * @param _message The composed message payload in bytes. NOT necessarily the same payload passed via lzReceive. + * @param _executor The address of the executor for the composed message. + * @param _extraData Additional arbitrary data in bytes passed by the entity who executes the lzCompose. + */ + function lzCompose( + address _from, + bytes32 _guid, + bytes calldata _message, + address _executor, + bytes calldata _extraData + ) external payable; +} diff --git a/contracts/interfaces/IOFT.sol b/contracts/interfaces/IOFT.sol index 297229143..28ac0ba1d 100644 --- a/contracts/interfaces/IOFT.sol +++ b/contracts/interfaces/IOFT.sol @@ -87,3 +87,15 @@ interface IOFT { address _refundAddress ) external payable returns (MessagingReceipt memory, OFTReceipt memory); } + +interface IEndpoint { + function eid() external view returns (uint32); +} + +interface IOAppCore { + /** + * @notice Retrieves the LayerZero endpoint associated with the OApp. + * @return iEndpoint The LayerZero endpoint as an interface. + */ + function endpoint() external view returns (IEndpoint iEndpoint); +} diff --git a/contracts/libraries/BytesLib.sol b/contracts/libraries/BytesLib.sol index 451118641..fe83d77c7 100644 --- a/contracts/libraries/BytesLib.sol +++ b/contracts/libraries/BytesLib.sol @@ -13,6 +13,21 @@ library BytesLib { * FUNCTIONS * **************************************/ + /** + * @notice Reads a uint16 from a bytes array at a given start index + * @param _bytes The bytes array to convert + * @param _start The start index of the uint16 + * @return result The uint16 result + */ + function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16 result) { + require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); + + // solhint-disable-next-line no-inline-assembly + assembly { + result := mload(add(add(_bytes, 0x2), _start)) + } + } + /** * @notice Reads a uint32 from a bytes array at a given start index * @param _bytes The bytes array to convert diff --git a/contracts/libraries/MinimalLZOptions.sol b/contracts/libraries/MinimalLZOptions.sol new file mode 100644 index 000000000..6314a6b0d --- /dev/null +++ b/contracts/libraries/MinimalLZOptions.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { BytesLib } from "./BytesLib.sol"; +import "@openzeppelin/contracts/utils/math/SafeCast.sol"; + +/** + * @title MinimalExecutorOptions + * @notice This library is used to provide minimal required functionality of + * https://github.com/LayerZero-Labs/LayerZero-v2/blob/2ff4988f85b5c94032eb71bbc4073e69c078179d/packages/layerzero-v2/evm/messagelib/contracts/libs/ExecutorOptions.sol#L7 + */ +library MinimalExecutorOptions { + uint8 internal constant WORKER_ID = 1; + + uint8 internal constant OPTION_TYPE_LZRECEIVE = 1; + uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3; + + function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) { + return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value); + } + + function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) { + return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value); + } +} + +/** + * @title MinimalLZOptions + * @notice This library is used to provide minimal functionality of + * https://github.com/LayerZero-Labs/devtools/blob/52ad590ab249f660f803ae3aafcbf7115733359c/packages/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol + */ +library MinimalLZOptions { + // @dev Only used in `onlyType3` modifier + using BytesLib for bytes; + // @dev Only used in `addExecutorOption` + using SafeCast for uint256; + + // Constants for options types + uint16 internal constant TYPE_1 = 1; // legacy options type 1 + uint16 internal constant TYPE_2 = 2; // legacy options type 2 + uint16 internal constant TYPE_3 = 3; + + // Custom error message + error InvalidSize(uint256 max, uint256 actual); + error InvalidOptionType(uint16 optionType); + + // Modifier to ensure only options of type 3 are used + modifier onlyType3(bytes memory _options) { + if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0)); + _; + } + + /** + * @dev Creates a new options container with type 3. + * @return options The newly created options container. + */ + function newOptions() internal pure returns (bytes memory) { + return abi.encodePacked(TYPE_3); + } + + /** + * @dev Adds an executor LZ receive option to the existing options. + * @param _options The existing options container. + * @param _gas The gasLimit used on the lzReceive() function in the OApp. + * @param _value The msg.value passed to the lzReceive() function in the OApp. + * @return options The updated options container. + * + * @dev When multiples of this option are added, they are summed by the executor + * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint, + * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function. + */ + function addExecutorLzReceiveOption( + bytes memory _options, + uint128 _gas, + uint128 _value + ) internal pure onlyType3(_options) returns (bytes memory) { + bytes memory option = MinimalExecutorOptions.encodeLzReceiveOption(_gas, _value); + return addExecutorOption(_options, MinimalExecutorOptions.OPTION_TYPE_LZRECEIVE, option); + } + + /** + * @dev Adds an executor LZ compose option to the existing options. + * @param _options The existing options container. + * @param _index The index for the lzCompose() function call. + * @param _gas The gasLimit for the lzCompose() function call. + * @param _value The msg.value for the lzCompose() function call. + * @return options The updated options container. + * + * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain. + * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0. + * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2 + */ + function addExecutorLzComposeOption( + bytes memory _options, + uint16 _index, + uint128 _gas, + uint128 _value + ) internal pure onlyType3(_options) returns (bytes memory) { + bytes memory option = MinimalExecutorOptions.encodeLzComposeOption(_index, _gas, _value); + return addExecutorOption(_options, MinimalExecutorOptions.OPTION_TYPE_LZCOMPOSE, option); + } + + /** + * @dev Adds an executor option to the existing options. + * @param _options The existing options container. + * @param _optionType The type of the executor option. + * @param _option The encoded data for the executor option. + * @return options The updated options container. + */ + function addExecutorOption( + bytes memory _options, + uint8 _optionType, + bytes memory _option + ) internal pure onlyType3(_options) returns (bytes memory) { + return + abi.encodePacked( + _options, + MinimalExecutorOptions.WORKER_ID, + _option.length.toUint16() + 1, // +1 for optionType + _optionType, + _option + ); + } +} diff --git a/contracts/libraries/OFTComposeMsgCodec.sol b/contracts/libraries/OFTComposeMsgCodec.sol new file mode 100644 index 000000000..a2b4bae6c --- /dev/null +++ b/contracts/libraries/OFTComposeMsgCodec.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +/** + * @title OFTComposeMsgCodec + * @notice Copied from LZ implementation here: + * https://github.com/LayerZero-Labs/devtools/blob/608915a7e260d995ce28e41c4e4877db9b18613b/packages/oft-evm/contracts/libs/OFTComposeMsgCodec.sol#L5 + * + */ +library OFTComposeMsgCodec { + // Offset constants for decoding composed messages + uint8 private constant NONCE_OFFSET = 8; + uint8 private constant SRC_EID_OFFSET = 12; + uint8 private constant AMOUNT_LD_OFFSET = 44; + uint8 private constant COMPOSE_FROM_OFFSET = 76; + + /** + * @dev Encodes a OFT composed message. + * @param _nonce The nonce value. + * @param _srcEid The source endpoint ID. + * @param _amountLD The amount in local decimals. + * @param _composeMsg The composed message. + * @return _msg The encoded Composed message. + */ + function encode( + uint64 _nonce, + uint32 _srcEid, + uint256 _amountLD, + bytes memory _composeMsg // 0x[composeFrom][composeMsg] + ) internal pure returns (bytes memory _msg) { + _msg = abi.encodePacked(_nonce, _srcEid, _amountLD, _composeMsg); + } + + /** + * @dev Retrieves the nonce for the composed message. + * @param _msg The message. + * @return The nonce value. + */ + function nonce(bytes calldata _msg) internal pure returns (uint64) { + return uint64(bytes8(_msg[:NONCE_OFFSET])); + } + + /** + * @dev Retrieves the source endpoint ID for the composed message. + * @param _msg The message. + * @return The source endpoint ID. + */ + function srcEid(bytes calldata _msg) internal pure returns (uint32) { + return uint32(bytes4(_msg[NONCE_OFFSET:SRC_EID_OFFSET])); + } + + /** + * @dev Retrieves the amount in local decimals from the composed message. + * @param _msg The message. + * @return The amount in local decimals. + */ + function amountLD(bytes calldata _msg) internal pure returns (uint256) { + return uint256(bytes32(_msg[SRC_EID_OFFSET:AMOUNT_LD_OFFSET])); + } + + /** + * @dev Retrieves the composeFrom value from the composed message. + * @param _msg The message. + * @return The composeFrom value. + */ + function composeFrom(bytes calldata _msg) internal pure returns (bytes32) { + return bytes32(_msg[AMOUNT_LD_OFFSET:COMPOSE_FROM_OFFSET]); + } + + /** + * @dev Retrieves the composed message. + * @param _msg The message. + * @return The composed message. + */ + function composeMsg(bytes calldata _msg) internal pure returns (bytes memory) { + return _msg[COMPOSE_FROM_OFFSET:]; + } + + /** + * @dev Converts an address to bytes32. + * @param _addr The address to convert. + * @return The bytes32 representation of the address. + */ + function addressToBytes32(address _addr) internal pure returns (bytes32) { + return bytes32(uint256(uint160(_addr))); + } + + /** + * @dev Converts bytes32 to an address. + * @param _b The bytes32 value to convert. + * @return The address representation of bytes32. + */ + function bytes32ToAddress(bytes32 _b) internal pure returns (address) { + return address(uint160(uint256(_b))); + } +} diff --git a/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol b/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol new file mode 100644 index 000000000..d1d92b1ae --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; +import { BytesLib } from "../../../libraries/BytesLib.sol"; + +/// @notice Codec for params passed in OFT `composeMsg`. +library ComposeMsgCodec { + uint256 internal constant NONCE_OFFSET = 0; + uint256 internal constant DEADLINE_OFFSET = 32; + uint256 internal constant MAX_BPS_TO_SPONSOR_OFFSET = 64; + uint256 internal constant MAX_USER_SLIPPAGE_BPS_OFFSET = 96; + uint256 internal constant FINAL_RECIPIENT_OFFSET = 128; + uint256 internal constant FINAL_TOKEN_OFFSET = 160; + uint256 internal constant COMPOSE_MSG_BYTE_LENGTH = 192; + + function _encode( + bytes32 nonce, + uint256 deadline, + uint256 maxBpsToSponsor, + uint256 maxUserSlippageBps, + bytes32 finalRecipient, + bytes32 finalToken + ) internal pure returns (bytes memory) { + return abi.encode(nonce, deadline, maxBpsToSponsor, maxUserSlippageBps, finalRecipient, finalToken); + } + + function _getNonce(bytes memory data) internal pure returns (bytes32 v) { + return BytesLib.toBytes32(data, NONCE_OFFSET); + } + + function _getDeadline(bytes memory data) internal pure returns (uint256 v) { + return BytesLib.toUint256(data, DEADLINE_OFFSET); + } + + function _getMaxBpsToSponsor(bytes memory data) internal pure returns (uint256 v) { + return BytesLib.toUint256(data, MAX_BPS_TO_SPONSOR_OFFSET); + } + + function _getMaxUserSlippageBps(bytes memory data) internal pure returns (uint256 v) { + return BytesLib.toUint256(data, MAX_USER_SLIPPAGE_BPS_OFFSET); + } + + function _getFinalRecipient(bytes memory data) internal pure returns (bytes32 v) { + return BytesLib.toBytes32(data, FINAL_RECIPIENT_OFFSET); + } + + function _getFinalToken(bytes memory data) internal pure returns (bytes32 v) { + return BytesLib.toBytes32(data, FINAL_TOKEN_OFFSET); + } + + function _isValidComposeMsgBytelength(bytes memory data) internal pure returns (bool valid) { + valid = data.length == COMPOSE_MSG_BYTE_LENGTH; + } +} diff --git a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol new file mode 100644 index 000000000..8aa342133 --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +import { ILayerZeroComposer } from "../../../external/interfaces/ILayerZeroComposer.sol"; +import { OFTComposeMsgCodec } from "../../../libraries/OFTComposeMsgCodec.sol"; +import { DonationBox } from "../../../chain-adapters/DonationBox.sol"; +import { HyperCoreLib } from "../../../libraries/HyperCoreLib.sol"; +import { ComposeMsgCodec } from "./ComposeMsgCodec.sol"; +import { AddressToBytes32, Bytes32ToAddress } from "../../../libraries/AddressConverters.sol"; +import { IOFT, IOAppCore } from "../../../interfaces/IOFT.sol"; +import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; + +import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +/// @notice Handler that receives funds from LZ system, checks authorizations(both against LZ system and src chain +/// sender), and forwards authorized params to the `_executeFlow` function +contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor { + using ComposeMsgCodec for bytes; + using Bytes32ToAddress for bytes32; + using AddressToBytes32 for address; + + /// @notice We expect bridge amount that comes through to this Handler to be 1:1 with the src send amount, and we + /// require our src handler to ensure that it is. We don't sponsor extra bridge fees in this handler + uint256 public constant EXTRA_FEES_TO_SPONSOR = 0; + + address public immutable OFT_ENDPOINT_ADDRESS; + address public immutable IOFT_ADDRESS; + + /// @notice A mapping used to validate an incoming message against a list of authorized src periphery contracts. In + /// bytes32 to support non-EVM src chains + mapping(uint64 eid => bytes32 authorizedSrcPeriphery) public authorizedSrcPeripheryContracts; + + /// @notice A mapping used for nonce uniqueness checks. Our src periphery and LZ should have prevented this already, + /// but I guess better safe than sorry + mapping(bytes32 quoteNonce => bool used) usedNonces; + + /// @notice Emitted when a new authorized src periphery is configured + event SetAuthorizedPeriphery(uint32 srcEid, bytes32 srcPeriphery); + + /// @notice Thrown when trying to call lzCompose from a source periphery that's not been configured in `authorizedSrcPeripheryContracts` + error AuthorizedPeripheryNotSet(uint32 _srcEid); + /// @notice Thrown when source chain recipient is not authorized periphery contract + error UnauthorizedSrcPeriphery(uint32 _srcEid); + /// @notice Thrown when the supplied token does not match the supplied ioft messenger + error TokenIOFTMismatch(); + /// @notice Thrown when the supplied ioft address does not match the supplied endpoint address + error IOFTEndpointMismatch(); + /// @notice Thrown if Quote nonce was already used + error NonceAlreadyUsed(); + /// @notice Thrown if supplied OApp is not configured ioft + error InvalidOApp(); + /// @notice Thrown if called by an unauthorized endpoint + error UnauthorizedEndpoint(); + /// @notice Thrown when supplied _composeMsg format is unexpected + error InvalidComposeMsgFormat(); + + constructor( + address _oftEndpoint, + address _ioft, + address _donationBox, + address _baseToken, + uint32 _coreIndex, + bool _canBeUsedForAccountActivation, + uint64 _accountActivationFeeCore, + uint64 _bridgeSafetyBufferCore + ) + HyperCoreFlowExecutor( + _donationBox, + _baseToken, + _coreIndex, + _canBeUsedForAccountActivation, + _accountActivationFeeCore, + _bridgeSafetyBufferCore + ) + { + // baseToken is assigned on `HyperCoreFlowExecutor` creation + if (baseToken != IOFT(_ioft).token()) { + revert TokenIOFTMismatch(); + } + + OFT_ENDPOINT_ADDRESS = _oftEndpoint; + IOFT_ADDRESS = _ioft; + if (address(IOAppCore(IOFT_ADDRESS).endpoint()) != address(OFT_ENDPOINT_ADDRESS)) { + revert IOFTEndpointMismatch(); + } + } + + function setAuthorizedPeriphery(uint32 srcEid, bytes32 srcPeriphery) external onlyDefaultAdmin { + authorizedSrcPeripheryContracts[srcEid] = srcPeriphery; + emit SetAuthorizedPeriphery(srcEid, srcPeriphery); + } + + /** + * @notice Handles incoming composed messages from LayerZero. + * @dev Ensures the message comes from the correct OApp and is sent through the authorized endpoint. + * + * @param _oApp The address of the OApp that is sending the composed message. + */ + function lzCompose( + address _oApp, + bytes32 /* _guid */, + bytes calldata _message, + address /* _executor */, + bytes calldata /* _extraData */ + ) external payable override { + _requireAuthorizedMessage(_oApp, _message); + + // Decode the actual `composeMsg` payload to extract the recipient address + bytes memory composeMsg = OFTComposeMsgCodec.composeMsg(_message); + + // This check is a safety mechanism against blackholing funds. The funds were sent by the authorized periphery + // contract, but if the length is unexpected, we require funds be rescued, this is not a situation we aim to + // revover from in `lzCompose` call + if (composeMsg._isValidComposeMsgBytelength() == false) { + revert InvalidComposeMsgFormat(); + } + + bytes32 quoteNonce = composeMsg._getNonce(); + if (usedNonces[quoteNonce]) { + revert NonceAlreadyUsed(); + } + usedNonces[quoteNonce] = true; + + uint256 amountLD = OFTComposeMsgCodec.amountLD(_message); + uint256 maxBpsToSponsor = composeMsg._getMaxBpsToSponsor(); + uint256 maxUserSlippageBps = composeMsg._getMaxUserSlippageBps(); + address finalRecipient = composeMsg._getFinalRecipient().toAddress(); + address finalToken = composeMsg._getFinalToken().toAddress(); + + _executeFlow( + amountLD, + quoteNonce, + maxBpsToSponsor, + maxUserSlippageBps, + finalRecipient, + finalToken, + EXTRA_FEES_TO_SPONSOR + ); + } + + /// @notice Checks that message was authorized by LayerZero's identity system and that it came from authorized src periphery + function _requireAuthorizedMessage(address _oApp, bytes calldata _message) internal view { + if (_oApp != IOFT_ADDRESS) { + revert InvalidOApp(); + } + if (msg.sender != OFT_ENDPOINT_ADDRESS) { + revert UnauthorizedEndpoint(); + } + _requireAuthorizedPeriphery(_message); + } + + /// @dev Checks that _message came from the authorized src periphery contract stored in `authorizedSrcPeripheryContracts` + function _requireAuthorizedPeriphery(bytes calldata _message) internal view { + uint32 _srcEid = OFTComposeMsgCodec.srcEid(_message); + bytes32 authorizedPeriphery = authorizedSrcPeripheryContracts[_srcEid]; + if (authorizedPeriphery == bytes32(0)) { + revert AuthorizedPeripheryNotSet(_srcEid); + } + + // Decode original sender + bytes32 _composeFromBytes32 = OFTComposeMsgCodec.composeFrom(_message); + + // We don't allow arbitrary src chain callers. If such a caller does send a message to this handler, the funds + // will remain in this contract and will have to be rescued by an admin rescue function + if (authorizedPeriphery != _composeFromBytes32) { + revert UnauthorizedSrcPeriphery(_srcEid); + } + } +} diff --git a/contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol b/contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol new file mode 100644 index 000000000..26351d6ed --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import { SignedQuoteParams } from "./Structs.sol"; + +/// @notice Lib to check the signature for `SignedQuoteParams`. +/// The signature is checked against a keccak hash of abi-encoded fields of `SignedQuoteParams` +library QuoteSignLib { + using ECDSA for bytes32; + + /// @notice Compute the keccak of all `SignedQuoteParams` fields + function hash(SignedQuoteParams calldata p) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + p.srcEid, + p.dstEid, + p.destinationHandler, + p.amountLD, + p.nonce, + p.deadline, + p.maxBpsToSponsor, + p.finalRecipient, + p.finalToken, + p.lzReceiveGasLimit, + p.lzComposeGasLimit + ) + ); + } + + /// @notice Recover the signer for the given params and signature. + function recoverSigner(SignedQuoteParams calldata p, bytes calldata signature) internal pure returns (address) { + bytes32 digest = hash(p); + return digest.recover(signature); + } + + /// @notice Verify that `expectedSigner` signed `p` with `signature`. + function isSignatureValid( + address expectedSigner, + SignedQuoteParams calldata p, + bytes calldata signature + ) internal pure returns (bool) { + return recoverSigner(p, signature) == expectedSigner; + } +} diff --git a/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol new file mode 100644 index 000000000..226f88d89 --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +import { Quote } from "./Structs.sol"; +import { QuoteSignLib } from "./QuoteSignLib.sol"; +import { ComposeMsgCodec } from "./ComposeMsgCodec.sol"; + +import { IOFT, IOAppCore, IEndpoint, SendParam, MessagingFee } from "../../../interfaces/IOFT.sol"; +import { AddressToBytes32 } from "../../../libraries/AddressConverters.sol"; +import { MinimalLZOptions } from "../../../libraries/MinimalLZOptions.sol"; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +/// @notice Source chain periphery contract for users to interact with to start a sponsored or a non-sponsored flow +/// that allows custom Accross-supported flows on destination chain. Uses LayzerZero's OFT as an underlying bridge +contract SponsoredOFTSrcPeriphery is Ownable { + using AddressToBytes32 for address; + using MinimalLZOptions for bytes; + using SafeERC20 for IERC20; + + bytes public constant EMPTY_OFT_COMMAND = new bytes(0); + + /// @notice Token that's being sent by an OFT bridge + address public immutable TOKEN; + /// @notice OFT contract to interact with to initiate the bridge + address public immutable OFT_MESSENGER; + + /// @notice Source endpoint id + uint32 public immutable SRC_EID; + + /// @notice Signer public key to check the signed quote against + address public signer; + + /// @notice A mapping to enforce only a single usage per quote + mapping(bytes32 => bool) public quoteNonces; + + /// @notice Event with auxiliary information. To be used in concert with OftSent event to get relevant quote details + event SponsoredOFTSend( + bytes32 indexed quoteNonce, + address indexed originSender, + bytes32 indexed finalRecipient, + uint256 quoteDeadline, + uint256 maxBpsToSponsor, + uint256 maxUserSlippageBps, + bytes32 finalToken, + bytes sig + ); + + /// @notice Thrown when the source eid of the ioft messenger does not match the src eid supplied + error IncorrectSrcEid(); + /// @notice Thrown when the supplied token does not match the supplied ioft messenger + error TokenIOFTMismatch(); + /// @notice Thrown when the signer for quote does not match `signer` + error IncorrectSignature(); + /// @notice Thrown if Quote has expired + error QuoteExpired(); + /// @notice Thrown if Quote nonce was already used + error NonceAlreadyUsed(); + + constructor(address _token, address _oftMessenger, uint32 _srcEid, address _signer) { + TOKEN = _token; + OFT_MESSENGER = _oftMessenger; + SRC_EID = _srcEid; + if (IOAppCore(_oftMessenger).endpoint().eid() != _srcEid) { + revert IncorrectSrcEid(); + } + if (IOFT(_oftMessenger).token() != _token) { + revert TokenIOFTMismatch(); + } + signer = _signer; + } + + /// @notice Main entrypoint function to start the user flow + function deposit(Quote calldata quote, bytes calldata signature) external payable { + // Step 1: validate quote and mark quote nonce used + _validateQuote(quote, signature); + quoteNonces[quote.signedParams.nonce] = true; + + // Step 2: build oft send params from quote + (SendParam memory sendParam, MessagingFee memory fee, address refundAddress) = _buildOftTransfer(quote); + + // Step 3: pull tokens from user and apporove OFT messenger + IERC20(TOKEN).safeTransferFrom(msg.sender, address(this), quote.signedParams.amountLD); + IERC20(TOKEN).forceApprove(address(OFT_MESSENGER), quote.signedParams.amountLD); + + // Step 4: send oft transfer and emit event with auxiliary data + IOFT(OFT_MESSENGER).send{ value: msg.value }(sendParam, fee, refundAddress); + emit SponsoredOFTSend( + quote.signedParams.nonce, + msg.sender, + quote.signedParams.finalRecipient, + quote.signedParams.deadline, + quote.signedParams.maxBpsToSponsor, + quote.unsignedParams.maxUserSlippageBps, + quote.signedParams.finalToken, + signature + ); + } + + function _buildOftTransfer( + Quote calldata quote + ) internal view returns (SendParam memory, MessagingFee memory, address) { + bytes memory composeMsg = ComposeMsgCodec._encode( + quote.signedParams.nonce, + quote.signedParams.deadline, + quote.signedParams.maxBpsToSponsor, + quote.unsignedParams.maxUserSlippageBps, + quote.signedParams.finalRecipient, + quote.signedParams.finalToken + ); + + bytes memory extraOptions = MinimalLZOptions + .newOptions() + .addExecutorLzReceiveOption(uint128(quote.signedParams.lzReceiveGasLimit), uint128(0)) + .addExecutorLzComposeOption(uint16(0), uint128(quote.signedParams.lzComposeGasLimit), uint128(0)); + + SendParam memory sendParam = SendParam( + quote.signedParams.dstEid, + quote.signedParams.destinationHandler, + // Only support OFT sends that don't take fees in sent token. Set `minAmountLD = amountLD` to enforce this + quote.signedParams.amountLD, + quote.signedParams.amountLD, + extraOptions, + composeMsg, + // TODO? Is this an issue for ~classic tokens like USDT0? + // Only support empty OFT commands + EMPTY_OFT_COMMAND + ); + + MessagingFee memory fee = IOFT(OFT_MESSENGER).quoteSend(sendParam, false); + + return (sendParam, fee, quote.unsignedParams.refundRecipient); + } + + function _validateQuote(Quote calldata quote, bytes calldata signature) internal view { + if (!QuoteSignLib.isSignatureValid(signer, quote.signedParams, signature)) { + revert IncorrectSignature(); + } + if (quote.signedParams.deadline < block.timestamp) { + revert QuoteExpired(); + } + if (quote.signedParams.srcEid != SRC_EID) { + revert IncorrectSrcEid(); + } + if (quoteNonces[quote.signedParams.nonce]) { + revert NonceAlreadyUsed(); + } + } + + function setSigner(address _newSigner) external onlyOwner { + signer = _newSigner; + } +} diff --git a/contracts/periphery/mintburn/sponsored-oft/Structs.sol b/contracts/periphery/mintburn/sponsored-oft/Structs.sol new file mode 100644 index 000000000..cfc3eaf33 --- /dev/null +++ b/contracts/periphery/mintburn/sponsored-oft/Structs.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +import { SendParam, MessagingFee } from "../../../interfaces/IOFT.sol"; + +/// @notice A structure with all the relevant information about a particular sponsored bridging flow order +struct Quote { + SignedQuoteParams signedParams; + UnsignedQuoteParams unsignedParams; +} + +/// @notice Signed params of the sponsored bridging flow quote +struct SignedQuoteParams { + uint32 srcEid; // Source endpoint ID in OFT system. + // Params passed into OFT.send() + uint32 dstEid; // Destination endpoint ID in OFT system. + bytes32 destinationHandler; // `to`. Recipient address. Address of our Composer contract + uint256 amountLD; // Amount to send in local decimals. + // Signed params that go into `composeMsg` + bytes32 nonce; // quote nonce + uint256 deadline; // quote deadline + uint256 maxBpsToSponsor; // max bps (of sent amount) to sponsor for 1:1 + bytes32 finalRecipient; // user address on destination + bytes32 finalToken; // final token user will receive (might be different from OFT token we're sending) + // Signed gas limits for destination-side LZ execution + uint256 lzReceiveGasLimit; // gas limit for `lzReceive` call on destination side + uint256 lzComposeGasLimit; // gas limit for `lzCompose` call on destination side +} + +/// @notice Unsigned params of the sponsored bridging flow quote: user is free to choose these +struct UnsignedQuoteParams { + address refundRecipient; // recipient of extra msg.value passed into the OFT send on src chain + uint256 maxUserSlippageBps; // slippage tolerance for the swap on the destination +} diff --git a/deploy/consts.ts b/deploy/consts.ts index 15d00e33f..2ec5a9bb5 100644 --- a/deploy/consts.ts +++ b/deploy/consts.ts @@ -194,6 +194,7 @@ export const L2_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } }, [CHAIN_IDs.HYPEREVM]: { cctpV2TokenMessenger: "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + oftEndpoint: "0x3A73033C0b1407574C76BdBAc67f126f6b4a9AA9", cctpV2MessageTransmitter: "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3", }, diff --git a/generated/constants.json b/generated/constants.json index 50ae54d74..62f7b8d55 100644 --- a/generated/constants.json +++ b/generated/constants.json @@ -611,6 +611,7 @@ }, "999": { "cctpV2TokenMessenger": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", + "oftEndpoint": "0x3A73033C0b1407574C76BdBAc67f126f6b4a9AA9", "cctpV2MessageTransmitter": "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, diff --git a/script/115DeploySrcOFTPeriphery.sol b/script/115DeploySrcOFTPeriphery.sol new file mode 100644 index 000000000..7c9c4af01 --- /dev/null +++ b/script/115DeploySrcOFTPeriphery.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Test } from "forge-std/Test.sol"; +import { console } from "forge-std/console.sol"; + +import { DonationBox } from "../contracts/chain-adapters/DonationBox.sol"; +import { DeploymentUtils } from "./utils/DeploymentUtils.sol"; +import { SponsoredOFTSrcPeriphery } from "../contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol"; + +// Deploy: forge script script/115DeploySrcOFTPeriphery.sol:DepoySrcOFTPeriphery --rpc-url -vvvv +contract DepoySrcOFTPeriphery is Script, Test, DeploymentUtils { + function run() external { + console.log("Deploying SponsoredOFTSrcPeriphery..."); + console.log("Chain ID:", block.chainid); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + vm.startBroadcast(deployerPrivateKey); + + SponsoredOFTSrcPeriphery srcOftPeriphery = new SponsoredOFTSrcPeriphery( + 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9, + 0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92, + 30110, + deployer + ); + + console.log("SponsoredOFTSrcPeriphery deployed to:", address(srcOftPeriphery)); + + vm.stopBroadcast(); + } +} diff --git a/script/116DeployDstOFTHandler.sol b/script/116DeployDstOFTHandler.sol new file mode 100644 index 000000000..a9f7b04aa --- /dev/null +++ b/script/116DeployDstOFTHandler.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Test } from "forge-std/Test.sol"; +import { console } from "forge-std/console.sol"; + +import { DonationBox } from "../contracts/chain-adapters/DonationBox.sol"; +import { DeploymentUtils } from "./utils/DeploymentUtils.sol"; +import { DstOFTHandler } from "../contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol"; + +// Deploy: forge script script/116DeployDstOFTHandler.sol:DeployDstOFTHandler --rpc-url -vvvv +contract DeployDstOFTHandler is Script, Test, DeploymentUtils { + function run() external { + console.log("Deploying DstOFTHandler..."); + console.log("Chain ID:", block.chainid); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + + vm.startBroadcast(deployerPrivateKey); + + address oftEndpoint = getL2Address(block.chainid, "oftEndpoint"); + address ioft = 0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98; + + address baseToken = 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb; + uint32 coreIndex = 268; + bool canBeUsedForAccountActivation = true; + uint64 accountActivationFeeCore = 100000000; + uint64 bridgeSafetyBufferCore = 1_000_000_000_00000000; // 1billion USDC (8 decimals) + + DonationBox donationBox = new DonationBox(); + + DstOFTHandler dstOFTHandler = new DstOFTHandler( + oftEndpoint, + ioft, + address(donationBox), + baseToken, + coreIndex, + canBeUsedForAccountActivation, + accountActivationFeeCore, + bridgeSafetyBufferCore + ); + console.log("DstOFTHandler deployed to:", address(dstOFTHandler)); + + donationBox.transferOwnership(address(dstOFTHandler)); + + console.log("DonationBox ownership transferred to:", address(dstOFTHandler)); + + vm.stopBroadcast(); + } +} diff --git a/script/mintburn/CreateSponsoredDeposit.sol b/script/mintburn/CreateSponsoredDeposit.sol new file mode 100644 index 000000000..2fccf74b9 --- /dev/null +++ b/script/mintburn/CreateSponsoredDeposit.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { SponsoredOFTSrcPeriphery } from "../../contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol"; +import { Quote, SignedQuoteParams, UnsignedQuoteParams } from "../../contracts/periphery/mintburn/sponsored-oft/Structs.sol"; +import { AddressToBytes32 } from "../../contracts/libraries/AddressConverters.sol"; +import { ComposeMsgCodec } from "../../contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol"; +import { MinimalLZOptions } from "../../contracts/libraries/MinimalLZOptions.sol"; +import { IOFT, SendParam, MessagingFee } from "../../contracts/interfaces/IOFT.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +/// @notice Used in place of // import { QuoteSignLib } from "../contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol"; +/// just for the hashing function that works with a memory funciton argument +library DebugQuoteSignLib { + /// @notice Compute the keccak of all `SignedQuoteParams` fields. Accept memory arg + function hashMemory(SignedQuoteParams memory p) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + p.srcEid, + p.dstEid, + p.destinationHandler, + p.amountLD, + p.nonce, + p.deadline, + p.maxBpsToSponsor, + p.finalRecipient, + p.finalToken, + p.lzReceiveGasLimit, + p.lzComposeGasLimit + ) + ); + } +} + +// forge script script/mintburn/CreateSponsoredDeposit.sol:CreateSponsoredDeposit --rpc-url arbitrum -vvvv +contract CreateSponsoredDeposit is Script { + using AddressToBytes32 for address; + using SafeERC20 for IERC20; + using MinimalLZOptions for bytes; + + function run() external { + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); // dev wallet + + // --- START CONFIG --- + uint32 srcEid = 30110; // Arbitrum + address srcPeriphery = 0x2C4413C70Fd1BDB109d7DFEE7310f4B692Dec381; + address token = 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9; // Arbitrum USDT + uint256 amountLD = 2 * 10 ** 6 + 5456; // 1 USDT (6 decimals) + bytes32 nonce = bytes32(uint256(12351)); // Replace with unique nonce per deposit + uint256 deadline = block.timestamp + 1 hours; + uint256 maxBpsToSponsor = 100; // 1% + uint256 maxUserSlippageBps = 50; // 0.5% + uint32 dstEid = 30367; // HyperEVM + address destinationHandler = 0x40ad479382Ad2a5c3061487A5094a677B00f6Cb0; + address finalRecipient = 0xD1A68de1d242B3b98A7230ba003c19f7cF90e360; // alternative dev wallet + address finalToken = 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb; // USDT0 @ HyperEVM + uint256 lzReceiveGasLimit = 200_000; + uint256 lzComposeGasLimit = 300_000; + address refundRecipient = deployer; // dev wallet + // --- END CONFIG --- + + SponsoredOFTSrcPeriphery srcPeripheryContract = SponsoredOFTSrcPeriphery(srcPeriphery); + require(srcPeripheryContract.signer() == deployer, "quote signer mismatch"); + + SignedQuoteParams memory signedParams = SignedQuoteParams({ + srcEid: srcEid, + dstEid: dstEid, + destinationHandler: destinationHandler.toBytes32(), + amountLD: amountLD, + nonce: nonce, + deadline: deadline, + maxBpsToSponsor: maxBpsToSponsor, + finalRecipient: finalRecipient.toBytes32(), + finalToken: finalToken.toBytes32(), + lzReceiveGasLimit: lzReceiveGasLimit, + lzComposeGasLimit: lzComposeGasLimit + }); + + UnsignedQuoteParams memory unsignedParams = UnsignedQuoteParams({ + refundRecipient: refundRecipient, + maxUserSlippageBps: maxUserSlippageBps + }); + + Quote memory quote = Quote({ signedParams: signedParams, unsignedParams: unsignedParams }); + + bytes32 quoteDigest = DebugQuoteSignLib.hashMemory(signedParams); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(deployerPrivateKey, quoteDigest); + bytes memory signature = abi.encodePacked(r, s, v); + + MessagingFee memory fee = _quoteMessagingFee(srcPeripheryContract, quote); + + vm.startBroadcast(deployerPrivateKey); + + IERC20(token).forceApprove(srcPeriphery, amountLD); + + srcPeripheryContract.deposit{ value: fee.nativeFee }(quote, signature); + + vm.stopBroadcast(); + } + + function _quoteMessagingFee( + SponsoredOFTSrcPeriphery srcPeripheryContract, + Quote memory quote + ) internal view returns (MessagingFee memory) { + address oftMessenger = srcPeripheryContract.OFT_MESSENGER(); + + bytes memory composeMsg = ComposeMsgCodec._encode( + quote.signedParams.nonce, + quote.signedParams.deadline, + quote.signedParams.maxBpsToSponsor, + quote.unsignedParams.maxUserSlippageBps, + quote.signedParams.finalRecipient, + quote.signedParams.finalToken + ); + + bytes memory extraOptions = MinimalLZOptions + .newOptions() + .addExecutorLzReceiveOption(uint128(quote.signedParams.lzReceiveGasLimit), uint128(0)) + .addExecutorLzComposeOption(uint16(0), uint128(quote.signedParams.lzComposeGasLimit), uint128(0)); + + SendParam memory sendParam = SendParam({ + dstEid: quote.signedParams.dstEid, + to: quote.signedParams.destinationHandler, + amountLD: quote.signedParams.amountLD, + minAmountLD: quote.signedParams.amountLD, + extraOptions: extraOptions, + composeMsg: composeMsg, + oftCmd: srcPeripheryContract.EMPTY_OFT_COMMAND() + }); + + return IOFT(oftMessenger).quoteSend(sendParam, false); + } +} diff --git a/script/mintburn/SetAuthorizedPeriphery.s.sol b/script/mintburn/SetAuthorizedPeriphery.s.sol new file mode 100644 index 000000000..d94524388 --- /dev/null +++ b/script/mintburn/SetAuthorizedPeriphery.s.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { DstOFTHandler } from "../../contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol"; +import { AddressToBytes32 } from "../../contracts/libraries/AddressConverters.sol"; + +// forge script script/mintburn/SetAuthorizedPeriphery.s.sol:SetAuthorizedPeriphery --rpc-url hyper-evm -vvvv +contract SetAuthorizedPeriphery is Script { + using AddressToBytes32 for address; + + function run() external { + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + + // --- START CONFIG --- + uint32 srcEid = 30110; // Arbitrum + address srcPeriphery = 0x2C4413C70Fd1BDB109d7DFEE7310f4B692Dec381; + address dstHandlerAddress = 0x40ad479382Ad2a5c3061487A5094a677B00f6Cb0; + // --- END CONFIG --- + + DstOFTHandler dstHandler = DstOFTHandler(dstHandlerAddress); + + vm.startBroadcast(deployerPrivateKey); + + dstHandler.setAuthorizedPeriphery(srcEid, srcPeriphery.toBytes32()); + + vm.stopBroadcast(); + } +} diff --git a/test/evm/hardhat/chain-adapters/Arbitrum_Adapter.ts b/test/evm/hardhat/chain-adapters/Arbitrum_Adapter.ts index 2e58c5d2f..57c31b866 100644 --- a/test/evm/hardhat/chain-adapters/Arbitrum_Adapter.ts +++ b/test/evm/hardhat/chain-adapters/Arbitrum_Adapter.ts @@ -27,8 +27,8 @@ import { MessagingReceiptStructOutput, OFTReceiptStructOutput, SendParamStruct, -} from "../../../../typechain/contracts/interfaces/IOFT"; -import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT__factory"; +} from "../../../../typechain/contracts/interfaces/IOFT.sol/IOFT"; +import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT.sol/IOFT__factory"; import { hubPoolFixture, enableTokensForLP } from "../fixtures/HubPool.Fixture"; import { constructSingleChainTree } from "../MerkleLib.utils"; import { CIRCLE_DOMAIN_IDs } from "../../../../deploy/consts"; diff --git a/test/evm/hardhat/chain-adapters/Polygon_Adapter.ts b/test/evm/hardhat/chain-adapters/Polygon_Adapter.ts index b36181d4d..f8f3850a6 100644 --- a/test/evm/hardhat/chain-adapters/Polygon_Adapter.ts +++ b/test/evm/hardhat/chain-adapters/Polygon_Adapter.ts @@ -32,8 +32,8 @@ import { MessagingReceiptStructOutput, OFTReceiptStructOutput, SendParamStruct, -} from "../../../../typechain/contracts/interfaces/IOFT"; -import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT__factory"; +} from "../../../../typechain/contracts/interfaces/IOFT.sol/IOFT"; +import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT.sol/IOFT__factory"; import { CIRCLE_DOMAIN_IDs } from "../../../../deploy/consts"; import { AdapterStore, AdapterStore__factory } from "../../../../typechain"; import { CHAIN_IDs } from "@across-protocol/constants"; diff --git a/test/evm/hardhat/chain-specific-spokepools/Arbitrum_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Arbitrum_SpokePool.ts index 36a74031f..690d3a206 100644 --- a/test/evm/hardhat/chain-specific-spokepools/Arbitrum_SpokePool.ts +++ b/test/evm/hardhat/chain-specific-spokepools/Arbitrum_SpokePool.ts @@ -26,8 +26,8 @@ import { MessagingReceiptStructOutput, OFTReceiptStructOutput, SendParamStruct, -} from "../../../../typechain/contracts/interfaces/IOFT"; -import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT__factory"; +} from "../../../../typechain/contracts/interfaces/IOFT.sol/IOFT"; +import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT.sol/IOFT__factory"; import { CHAIN_IDs } from "@across-protocol/constants"; let hubPool: Contract, arbitrumSpokePool: Contract, dai: Contract, weth: Contract, l2UsdtContract: Contract; diff --git a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts index e74092837..b3989e5c3 100644 --- a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts +++ b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts @@ -25,13 +25,13 @@ import { } from "../../../../utils/utils"; import { getOftEid } from "../../../../utils/utils"; import { CHAIN_IDs } from "@across-protocol/constants"; -import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT__factory"; +import { IOFT__factory } from "../../../../typechain/factories/contracts/interfaces/IOFT.sol/IOFT__factory"; import { MessagingFeeStructOutput, MessagingReceiptStructOutput, OFTReceiptStructOutput, SendParamStruct, -} from "../../../../typechain/contracts/interfaces/IOFT"; +} from "../../../../typechain/contracts/interfaces/IOFT.sol/IOFT"; import { hre } from "../../../../utils/utils.hre"; import { hubPoolFixture } from "../fixtures/HubPool.Fixture"; import { buildRelayerRefundLeaves, buildRelayerRefundTree, constructSingleRelayerRefundTree } from "../MerkleLib.utils"; From cb310e2ef235a8776ddbe0e18c85ca27af8dccfd Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Mon, 20 Oct 2025 00:33:58 -0400 Subject: [PATCH 08/47] fix: add fixes for issues that codex found (#1142) Signed-off-by: Matt Rice --- contracts/periphery/mintburn/HyperCoreFlowExecutor.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 396827284..5add7cd47 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -625,7 +625,6 @@ contract HyperCoreFlowExecutor is AccessControl { require(address(finalTokenInfo.swapHandler) != address(0), "Final token not registered"); require(lastPullFundsBlock[finalToken] < block.number, "Can't finalize twice in the same block"); - lastPullFundsBlock[finalToken] = block.number; uint256 head = pendingQueueHead[finalToken]; bytes32[] storage queue = pendingQueue[finalToken]; @@ -668,6 +667,10 @@ contract HyperCoreFlowExecutor is AccessControl { } pendingQueueHead[finalToken] = head; + + if (processed > 0) { + lastPullFundsBlock[finalToken] = block.number; + } } /// @notice Cancells a pending limit order by `cloid` with an intention to submit a new limit order in its place. To @@ -718,7 +721,7 @@ contract HyperCoreFlowExecutor is AccessControl { address finalToken = pendingSwap.finalToken; FinalTokenInfo memory finalTokenInfo = finalTokenInfos[finalToken]; - CoreTokenInfo memory initialTokenInfo = coreTokenInfos[finalToken]; + CoreTokenInfo memory initialTokenInfo = coreTokenInfos[baseToken]; CoreTokenInfo memory finalTokenCoreInfo = coreTokenInfos[finalToken]; // Remaining budget of tokens attributable to the "old limit order" (now cancelled) From a608d74b394eab6e51d48b992ae8c7df2daf6e9d Mon Sep 17 00:00:00 2001 From: Ihor Farion Date: Sun, 19 Oct 2025 22:32:25 -0700 Subject: [PATCH 09/47] fix account activation flow Signed-off-by: Ihor Farion --- .../mintburn/HyperCoreFlowExecutor.sol | 182 ++++++++++++------ 1 file changed, 118 insertions(+), 64 deletions(-) diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 5add7cd47..5edfd1829 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -81,6 +81,10 @@ contract HyperCoreFlowExecutor is AccessControl { /// @notice Emitted when the donation box is insufficient funds and we can't proceed. error DonationBoxInsufficientFundsError(address token, uint256 amount); + /// @notice Emitted when we're inside the sponsored flow and a user doesn't have a HyperCore account activated. The + /// bot should activate user's account first by calling `activateUserAccount` + error AccountNotActivated(address user); + /// @notice Emitted when a simple transfer to core is executed. event SimpleTransferFlowCompleted( bytes32 indexed quoteNonce, @@ -144,6 +148,15 @@ contract HyperCoreFlowExecutor is AccessControl { /// @notice Emitted when the replacing Limit order has better price than the old one event BetterPricedLOSubmitted(bytes32 indexed quoteNonce, uint64 oldPriceX1e8, uint64 priceX1e8); + /// @notice Emitted from the simple trasnfer flow when we fall back to HyperEVM flow because a non-sponsored transfer's recipient has no HyperCore account + event SimpleTransferFallbackAccountActivation(bytes32 indexed quoteNonce); + + /// @notice Emitted from the simple trasnfer flow when we fall back to HyperEVM flow because bridging would be unsafe + event SimpleTransferFallbackUnsafeToBridge(bytes32 indexed quoteNonce); + + /// @notice Emitted from the swap flow when we fall back to HyperEVM flow because a non-sponsored transfer's recipient has no HyperCore account + event SwapFlowFallbackAccountActivation(bytes32 indexed quoteNonce); + /// @notice Emitted from the swap flow when falling back to the other flow becase the cost is to high compared to sponsored settings event SwapFlowFallbackTooExpensive( bytes32 indexed quoteNonce, @@ -168,15 +181,12 @@ contract HyperCoreFlowExecutor is AccessControl { bool finalTokenUnsafe ); - /// @notice Emitted from the swap flow when falling back to the other flow becase it's impossible to pay account activation fee in final token - event SwapFlowFallbackAccountActivation(bytes32 indexed quoteNonce, address finalToken); - - /// @notice Emitted from the simple transfer flow if either bridging is unsafe, or we couldn't pay for account activation in final token - event SimpleTransferFallback( + /// @notice Emitted whenever donationBox funds are used for activating a user account + event SponsoredAccountActivation( bytes32 indexed quoteNonce, - bool isBridgeSafe, - bool haveToPayForCoreAccountActivation, - bool tokenCanBeUsedForAccountActivation + address indexed finalRecipient, + address indexed fundingToken, + uint256 evmAmount ); /************************************** @@ -198,11 +208,30 @@ contract HyperCoreFlowExecutor is AccessControl { _; } - modifier onlyExistingToken(address evmTokenAddress) { - require(coreTokenInfos[evmTokenAddress].tokenInfo.evmContract != address(0), "Unknown token"); + modifier onlyExistingCoreToken(address evmTokenAddress) { + _getExistingCoreTokenInfo(evmTokenAddress); _; } + /// @notice Reverts if the token is not configured + function _getExistingCoreTokenInfo( + address evmTokenAddress + ) internal view returns (CoreTokenInfo memory coreTokenInfo) { + coreTokenInfo = coreTokenInfos[evmTokenAddress]; + require( + coreTokenInfo.tokenInfo.evmContract != address(0) && coreTokenInfo.tokenInfo.weiDecimals != 0, + "CoreTokenInfo not set" + ); + } + + /// @notice Reverts if the token is not configured + function _getExistingFinalTokenInfo( + address evmTokenAddress + ) internal view returns (FinalTokenInfo memory finalTokenInfo) { + finalTokenInfo = finalTokenInfos[evmTokenAddress]; + require(address(finalTokenInfo.swapHandler) != address(0), "FinalTokenInfo not set"); + } + /** * * @param _donationBox Sponsorship funds live here @@ -283,7 +312,7 @@ contract HyperCoreFlowExecutor is AccessControl { uint32 feePpm, uint32 suggestedDiscountBps, address accountActivationFeeToken - ) external onlyExistingToken(finalToken) onlyExistingToken(accountActivationFeeToken) onlyDefaultAdmin { + ) external onlyExistingCoreToken(finalToken) onlyExistingCoreToken(accountActivationFeeToken) onlyDefaultAdmin { SwapHandler swapHandler = finalTokenInfos[finalToken].swapHandler; if (address(swapHandler) == address(0)) { swapHandler = new SwapHandler(); @@ -359,21 +388,20 @@ contract HyperCoreFlowExecutor is AccessControl { address finalToken = baseToken; CoreTokenInfo storage coreTokenInfo = coreTokenInfos[finalToken]; - bool coreUserAccountExists = HyperCoreLib.coreUserExists(finalRecipient); - bool impossibleToForwardToCore = !coreUserAccountExists && !coreTokenInfo.canBeUsedForAccountActivation; - - // If the user has no HyperCore account and we can't sponsor its creation, - // fall back to sending user funds on HyperEVM - if (impossibleToForwardToCore) { - _fallbackHyperEVMFlow(amount, quoteNonce, maxBpsToSponsor, finalRecipient, extraFeesToSponsor); - return; + bool isSponsoredFlow = maxBpsToSponsor > 0; + bool userHasNoCoreAccount = !HyperCoreLib.coreUserExists(finalRecipient); + if (userHasNoCoreAccount) { + if (isSponsoredFlow) { + revert AccountNotActivated(finalRecipient); + } else { + _fallbackHyperEVMFlow(amount, quoteNonce, maxBpsToSponsor, finalRecipient, extraFeesToSponsor); + emit SimpleTransferFallbackAccountActivation(quoteNonce); + return; + } } - // Record `accountCreationFee` as zero if we can't use final token for account activation - uint256 accountCreationFee = coreUserAccountExists ? 0 : coreTokenInfo.accountActivationFeeEVM; uint256 maxEvmAmountToSponsor = (amount * maxBpsToSponsor) / BPS_SCALAR; - uint256 totalUserFeesEvm = extraFeesToSponsor + accountCreationFee; - uint256 amountToSponsor = totalUserFeesEvm; + uint256 amountToSponsor = extraFeesToSponsor; if (amountToSponsor > maxEvmAmountToSponsor) { amountToSponsor = maxEvmAmountToSponsor; } @@ -385,7 +413,6 @@ contract HyperCoreFlowExecutor is AccessControl { } uint256 finalAmount = amount + amountToSponsor; - (uint256 quotedEvmAmount, uint64 quotedCoreAmount) = HyperCoreLib.maximumEVMSendAmountToAmounts( finalAmount, coreTokenInfo.tokenInfo.evmExtraWeiDecimals @@ -402,6 +429,7 @@ contract HyperCoreFlowExecutor is AccessControl { // fall back to sending user funds on HyperEVM. if (!isSafe) { _fallbackHyperEVMFlow(amount, quoteNonce, maxBpsToSponsor, finalRecipient, extraFeesToSponsor); + emit SimpleTransferFallbackUnsafeToBridge(quoteNonce); return; } @@ -411,13 +439,9 @@ contract HyperCoreFlowExecutor is AccessControl { } cumulativeSponsoredAmount[finalToken] += amountToSponsor; - // Record the amount used to sponsor account creation - cumulativeSponsoredActivationFee[finalToken] += amountToSponsor < accountCreationFee - ? amountToSponsor - : accountCreationFee; - // There is a very slim chance that by the time we get here, the balance of the bridge changes - // and the funds are lost. + // There is a very slim change that someone is sending > buffer amount in the same EVM block and the balance of + // the bridge is not enough to cover our transfer, so the funds are lost. HyperCoreLib.transferERC20EVMToCore( finalToken, coreTokenInfo.coreIndex, @@ -443,7 +467,7 @@ contract HyperCoreFlowExecutor is AccessControl { // In initialToken uint256 amountInEVM, bytes32 quoteNonce, - address finalUser, + address finalRecipient, address finalToken, uint256 maxBpsToSponsor, // `maxUserSlippageBps` here means how much token user receives compared to a 1 to 1 @@ -451,42 +475,40 @@ contract HyperCoreFlowExecutor is AccessControl { // In initialToken uint256 extraBridgingFeesEVM ) internal { - FinalTokenInfo memory finalTokenInfo = finalTokenInfos[finalToken]; - require(address(finalTokenInfo.swapHandler) != address(0), "Final token not registered"); + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); address initialToken = baseToken; CoreTokenInfo memory initialCoreTokenInfo = coreTokenInfos[initialToken]; CoreTokenInfo memory finalCoreTokenInfo = coreTokenInfos[finalToken]; - bool isSponsoredFlow = maxBpsToSponsor > 0; // In initialToken (, uint64 amountInEquivalentCore) = HyperCoreLib.maximumEVMSendAmountToAmounts( amountInEVM, initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals ); - bool coreUserAccountExists = HyperCoreLib.coreUserExists(finalUser); - bool impossibleToForwardToCore = !coreUserAccountExists && !finalCoreTokenInfo.canBeUsedForAccountActivation; - - if (impossibleToForwardToCore) { - _fallbackHyperEVMFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalUser, extraBridgingFeesEVM); - emit SwapFlowFallbackAccountActivation(quoteNonce, finalToken); - return; + bool isSponsoredFlow = maxBpsToSponsor > 0; + bool userHasNoCoreAccount = !HyperCoreLib.coreUserExists(finalRecipient); + if (userHasNoCoreAccount) { + if (isSponsoredFlow) { + revert AccountNotActivated(finalRecipient); + } else { + _fallbackHyperEVMFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalRecipient, extraBridgingFeesEVM); + emit SwapFlowFallbackAccountActivation(quoteNonce); + return; + } } // In initialToken uint256 totalAmountBridgedEVM = amountInEVM + extraBridgingFeesEVM; // In finalToken - uint64 accountActivationFeeCore = coreUserAccountExists ? 0 : finalCoreTokenInfo.accountActivationFeeCore; - // In finalToken (uint64 minAllowableAmountToForwardCore, uint64 maxAmountToSponsorCore) = _calculateAllowableAmountsForFlow( totalAmountBridgedEVM, initialCoreTokenInfo, finalCoreTokenInfo, isSponsoredFlow, maxBpsToSponsor, - maxUserSlippageBps, - accountActivationFeeCore + maxUserSlippageBps ); uint64 limitPriceX1e8 = _getSuggestedPriceX1e8(finalTokenInfo); @@ -504,7 +526,7 @@ contract HyperCoreFlowExecutor is AccessControl { minAllowableAmountToForwardCore - guaranteedLOOut > maxAmountToSponsorCore ) { // We can't provide the required slippage in a swap flow, try simple transfer flow instead - _executeSimpleTransferFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalUser, extraBridgingFeesEVM); + _executeSimpleTransferFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalRecipient, extraBridgingFeesEVM); emit SwapFlowFallbackTooExpensive( quoteNonce, minAllowableAmountToForwardCore - guaranteedLOOut, @@ -529,7 +551,13 @@ contract HyperCoreFlowExecutor is AccessControl { if (totalEVMAmountToSponsor > 0) { if (!_availableInDonationBox(finalToken, totalEVMAmountToSponsor)) { // We can't provide the required `totalEVMAmountToSponsor` in a swap flow, try simple transfer flow instead - _executeSimpleTransferFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalUser, extraBridgingFeesEVM); + _executeSimpleTransferFlow( + amountInEVM, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + extraBridgingFeesEVM + ); emit SwapFlowFallbackDonationBox(quoteNonce, finalToken, totalEVMAmountToSponsor); return; } @@ -548,7 +576,7 @@ contract HyperCoreFlowExecutor is AccessControl { ); if (!isSafeToBridgeMainToken || !isSafeTobridgeSponsorshipFunds) { - _fallbackHyperEVMFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalUser, extraBridgingFeesEVM); + _fallbackHyperEVMFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalRecipient, extraBridgingFeesEVM); emit SwapFlowFallbackUnsafeToBridge( quoteNonce, isSafeToBridgeMainToken, @@ -587,9 +615,6 @@ contract HyperCoreFlowExecutor is AccessControl { totalEVMAmountToSponsor, finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals ); - cumulativeSponsoredActivationFee[finalToken] += accountActivationFeeCore > 0 - ? finalCoreTokenInfo.accountActivationFeeEVM - : 0; cumulativeSponsoredAmount[finalToken] += totalEVMAmountToSponsor; } @@ -597,7 +622,7 @@ contract HyperCoreFlowExecutor is AccessControl { swapHandler.submitLimitOrder(finalTokenInfo, limitPriceX1e8, sizeX1e8, cloid); pendingSwaps[quoteNonce] = PendingSwap({ - finalRecipient: finalUser, + finalRecipient: finalRecipient, finalToken: finalToken, minCoreAmountFromLO: guaranteedLOOut, sponsoredCoreAmountPreFunded: totalCoreAmountToSponsor, @@ -608,7 +633,7 @@ contract HyperCoreFlowExecutor is AccessControl { emit SwapFlowInitialized( quoteNonce, - finalUser, + finalRecipient, finalToken, amountInEVM, totalEVMAmountToSponsor, @@ -620,10 +645,9 @@ contract HyperCoreFlowExecutor is AccessControl { /// @notice Finalizes pending queue of swaps for `finalToken` if a corresponsing SwapHandler has enough balance function finalizePendingSwaps(address finalToken, uint256 maxToProcess) external { + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); CoreTokenInfo memory coreTokenInfo = coreTokenInfos[finalToken]; - FinalTokenInfo memory finalTokenInfo = finalTokenInfos[finalToken]; - require(address(finalTokenInfo.swapHandler) != address(0), "Final token not registered"); require(lastPullFundsBlock[finalToken] < block.number, "Can't finalize twice in the same block"); uint256 head = pendingQueueHead[finalToken]; @@ -673,11 +697,45 @@ contract HyperCoreFlowExecutor is AccessControl { } } + function activateUserAccount( + bytes32 quoteNonce, + address finalRecipient, + address fundingToken + ) external onlyPermissionedBot { + CoreTokenInfo memory coreTokenInfo = _getExistingCoreTokenInfo(fundingToken); + bool coreUserExists = HyperCoreLib.coreUserExists(finalRecipient); + require(coreUserExists == false, "Can't fund account activation for existing user"); + require(coreTokenInfo.canBeUsedForAccountActivation, "Token can't be used for this"); + bool safeToBridge = HyperCoreLib.isCoreAmountSafeToBridge( + coreTokenInfo.coreIndex, + coreTokenInfo.accountActivationFeeCore, + coreTokenInfo.bridgeSafetyBufferCore + ); + require(safeToBridge, "Not safe to bridge"); + uint256 activationFeeEvm = coreTokenInfo.accountActivationFeeEVM; + // donationBox @ evm -> Handler @ evm + _getFromDonationBox(fundingToken, activationFeeEvm); + // Handler @ evm -> Handler @ core -> finalRecipient @ core + HyperCoreLib.transferERC20EVMToCore( + fundingToken, + coreTokenInfo.coreIndex, + finalRecipient, + activationFeeEvm, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + + cumulativeSponsoredActivationFee[fundingToken] += activationFeeEvm; + + emit SponsoredAccountActivation(quoteNonce, finalRecipient, fundingToken, activationFeeEvm); + } + /// @notice Cancells a pending limit order by `cloid` with an intention to submit a new limit order in its place. To /// be used for stale limit orders to speed up executing user transactions function cancelLimitOrderByCloid(uint128 cloid) external onlyPermissionedBot returns (bytes32 quoteNonce) { quoteNonce = cloidToQuoteNonce[cloid]; PendingSwap storage pendingSwap = pendingSwaps[quoteNonce]; + // A pending swap was enqueued with this Final token, so it had to be set. Unsetting the final token config is not + // a valid configuration action so we can rely on finalToken being set here FinalTokenInfo memory finalTokenInfo = finalTokenInfos[pendingSwap.finalToken]; // Here, cloid == pendingSwap.limitOrderCloid @@ -879,7 +937,6 @@ contract HyperCoreFlowExecutor is AccessControl { function _getAccountActivationFeeEVM(address token, address recipient) internal view returns (uint256) { bool accountActivated = HyperCoreLib.coreUserExists(recipient); - return accountActivated ? 0 : coreTokenInfos[token].accountActivationFeeEVM; } @@ -889,8 +946,7 @@ contract HyperCoreFlowExecutor is AccessControl { CoreTokenInfo memory finalCoreTokenInfo, bool isSponsoredFlow, uint256 maxBpsToSponsor, - uint256 maxUserSlippageBps, - uint64 accountActivationFeeCore + uint256 maxUserSlippageBps ) internal pure returns (uint64 minAllowableAmountToForwardCore, uint64 maxAmountToSponsorCore) { (, uint64 feelessAmountCoreInitialToken) = HyperCoreLib.maximumEVMSendAmountToAmounts( totalAmountBridgedEVM, @@ -902,15 +958,13 @@ contract HyperCoreFlowExecutor is AccessControl { finalCoreTokenInfo.tokenInfo.weiDecimals ); if (isSponsoredFlow) { - // toCore(totalEvmBridgedAmount) + coreAccountActivationFee maxAmountToSponsorCore = uint64((feelessAmountCoreFinalToken * maxBpsToSponsor) / BPS_SCALAR); - minAllowableAmountToForwardCore = feelessAmountCoreFinalToken + accountActivationFeeCore; + minAllowableAmountToForwardCore = feelessAmountCoreFinalToken; } else { - // toCore(amountInEquivalentCore) - slippage + coreAccountActivationFee maxAmountToSponsorCore = 0; - minAllowableAmountToForwardCore = - uint64((feelessAmountCoreFinalToken * (BPS_SCALAR - maxUserSlippageBps)) / BPS_SCALAR) + - accountActivationFeeCore; + minAllowableAmountToForwardCore = uint64( + (feelessAmountCoreFinalToken * (BPS_SCALAR - maxUserSlippageBps)) / BPS_SCALAR + ); } } From 1361b2db25f2dd566f951c31d27d8b53d4cad892 Mon Sep 17 00:00:00 2001 From: Ihor Farion Date: Sun, 19 Oct 2025 23:13:20 -0700 Subject: [PATCH 10/47] use CREATE2 for deterministic SwapHandler addresses. Check for HyperCore account existence before allowing to set a final token or deploy a HyperCoreFlowExecutor contract Signed-off-by: Ihor Farion --- .../mintburn/HyperCoreFlowExecutor.sol | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 5edfd1829..34a56ca0e 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -249,8 +249,11 @@ contract HyperCoreFlowExecutor is AccessControl { uint64 _accountActivationFeeCore, uint64 _bridgeSafetyBufferCore ) { + // Handler core account must be prefunded to prevent loss of funds. Predict address -> fund -> deploy + require(HyperCoreLib.coreUserExists(address(this)), "Handler @ core doesn't exist"); + donationBox = DonationBox(_donationBox); - // @dev initialize this to 1 as to save 0 for special events when "no cloid is set" = no associated limit order + // Initialize this to 1 as to save 0 for special events when "no cloid is set" = no associated limit order nextCloid = 1; _setCoreTokenInfo( @@ -315,7 +318,8 @@ contract HyperCoreFlowExecutor is AccessControl { ) external onlyExistingCoreToken(finalToken) onlyExistingCoreToken(accountActivationFeeToken) onlyDefaultAdmin { SwapHandler swapHandler = finalTokenInfos[finalToken].swapHandler; if (address(swapHandler) == address(0)) { - swapHandler = new SwapHandler(); + bytes32 salt = _swapHandlerSalt(finalToken); + swapHandler = new SwapHandler{ salt: salt }(); } finalTokenInfos[finalToken] = FinalTokenInfo({ @@ -326,21 +330,21 @@ contract HyperCoreFlowExecutor is AccessControl { suggestedDiscountBps: suggestedDiscountBps }); - // TODO: this activation flow seems broken. Can a smart contract activate its core account by doing an EVM -> Core transfer? - uint256 accountActivationFee = _getAccountActivationFeeEVM(accountActivationFeeToken, address(swapHandler)); - if (accountActivationFee > 0) { - CoreTokenInfo memory accountActivationTokenInfo = coreTokenInfos[accountActivationFeeToken]; - require(accountActivationTokenInfo.canBeUsedForAccountActivation, "account activation fee token error"); - - _getFromDonationBox(accountActivationFeeToken, accountActivationFee); - IERC20(accountActivationFeeToken).safeTransfer(address(swapHandler), accountActivationFee); - swapHandler.activateCoreAccount( - accountActivationFeeToken, - accountActivationTokenInfo.coreIndex, - accountActivationFee, - accountActivationTokenInfo.tokenInfo.evmExtraWeiDecimals - ); - } + // We don't allow SwapHandler accounts to be uninitiated. That could lead to loss of funds. They instead should + // be pre-funded using `predictSwapHandler` to predict their address + require(HyperCoreLib.coreUserExists(address(swapHandler)), "SwapHandler @ core doesn't exist"); + } + + /// @notice Predicts the deterministic address of a SwapHandler for a given finalToken using CREATE2 + function predictSwapHandler(address finalToken) public view returns (address) { + bytes32 salt = _swapHandlerSalt(finalToken); + bytes32 initCodeHash = keccak256(type(SwapHandler).creationCode); + return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, initCodeHash))))); + } + + /// @notice Returns the salt to use when creating a SwapHandler via CREATE2 + function _swapHandlerSalt(address finalToken) internal view returns (bytes32) { + return keccak256(abi.encodePacked(address(this), finalToken)); } /************************************** From 1a063e55ad622c6854b875ed2e68c43459263360 Mon Sep 17 00:00:00 2001 From: Ihor Farion Date: Mon, 20 Oct 2025 00:58:43 -0700 Subject: [PATCH 11/47] improve events Signed-off-by: Ihor Farion --- .../mintburn/HyperCoreFlowExecutor.sol | 17 ++++++++++++++++- .../sponsored-oft/SponsoredOFTSrcPeriphery.sol | 3 ++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 34a56ca0e..134c2d0c1 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -189,6 +189,15 @@ contract HyperCoreFlowExecutor is AccessControl { uint256 evmAmount ); + /// @notice Emitted whenever a new CoreTokenInfo is configured + event SetCoreTokenInfo( + address indexed token, + uint32 coreIndex, + bool canBeUsedForAccountActivation, + uint64 accountActivationFeeCore, + uint64 bridgeSafetyBufferCore + ); + /************************************** * MODIFIERS * **************************************/ @@ -920,7 +929,13 @@ contract HyperCoreFlowExecutor is AccessControl { bridgeSafetyBufferCore: bridgeSafetyBufferCore }); - // TODO: emit event? maybe not if we are over limit + emit SetCoreTokenInfo( + token, + coreIndex, + canBeUsedForAccountActivation, + accountActivationFeeCore, + bridgeSafetyBufferCore + ); } /// @notice Gets `amount` of `token` from donationBox. Reverts if unsuccessful diff --git a/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol index 226f88d89..527d2ccc6 100644 --- a/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol @@ -41,6 +41,7 @@ contract SponsoredOFTSrcPeriphery is Ownable { bytes32 indexed quoteNonce, address indexed originSender, bytes32 indexed finalRecipient, + bytes32 destinationHandler, uint256 quoteDeadline, uint256 maxBpsToSponsor, uint256 maxUserSlippageBps, @@ -91,6 +92,7 @@ contract SponsoredOFTSrcPeriphery is Ownable { quote.signedParams.nonce, msg.sender, quote.signedParams.finalRecipient, + quote.signedParams.destinationHandler, quote.signedParams.deadline, quote.signedParams.maxBpsToSponsor, quote.unsignedParams.maxUserSlippageBps, @@ -124,7 +126,6 @@ contract SponsoredOFTSrcPeriphery is Ownable { quote.signedParams.amountLD, extraOptions, composeMsg, - // TODO? Is this an issue for ~classic tokens like USDT0? // Only support empty OFT commands EMPTY_OFT_COMMAND ); From fc513898baeefa1af92623b7f0d677e46b0e1141 Mon Sep 17 00:00:00 2001 From: Ihor Farion Date: Mon, 20 Oct 2025 01:32:30 -0700 Subject: [PATCH 12/47] misc Signed-off-by: Ihor Farion --- .../mintburn/HyperCoreFlowExecutor.sol | 24 ++++++++++--------- .../SponsoredCCTPSrcPeriphery.sol | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 134c2d0c1..7f541cb73 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -364,9 +364,11 @@ contract HyperCoreFlowExecutor is AccessControl { * @notice This function is to be called by an inheriting contract. It is to be called after the child contract * checked the API signature and made sure that the params passed here have been verified by either the underlying * bridge mechanics, or API signaure, or both. + * @param amountInEVM The real token amount to be used in this flow execution + * @param extraFeesToSponsor Any fees subtracted from the user up until this point (e.g. CCTP bridging fees) */ function _executeFlow( - uint256 amount, + uint256 amountInEVM, bytes32 quoteNonce, uint256 maxBpsToSponsor, uint256 maxUserSlippageBps, @@ -375,10 +377,10 @@ contract HyperCoreFlowExecutor is AccessControl { uint256 extraFeesToSponsor ) internal { if (finalToken == baseToken) { - _executeSimpleTransferFlow(amount, quoteNonce, maxBpsToSponsor, finalRecipient, extraFeesToSponsor); + _executeSimpleTransferFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalRecipient, extraFeesToSponsor); } else { _initiateSwapFlow( - amount, + amountInEVM, quoteNonce, finalRecipient, finalToken, @@ -392,11 +394,11 @@ contract HyperCoreFlowExecutor is AccessControl { /// @notice Execute a simple transfer flow in which we transfer `baseToken` to the user on HyperCore after receiving /// an amount of baseToken from the user on HyperEVM function _executeSimpleTransferFlow( - uint256 amount, + uint256 amountInEVM, bytes32 quoteNonce, uint256 maxBpsToSponsor, address finalRecipient, - uint256 extraFeesToSponsor + uint256 extraBridgingFeesEVM ) internal { address finalToken = baseToken; CoreTokenInfo storage coreTokenInfo = coreTokenInfos[finalToken]; @@ -407,14 +409,14 @@ contract HyperCoreFlowExecutor is AccessControl { if (isSponsoredFlow) { revert AccountNotActivated(finalRecipient); } else { - _fallbackHyperEVMFlow(amount, quoteNonce, maxBpsToSponsor, finalRecipient, extraFeesToSponsor); + _fallbackHyperEVMFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalRecipient, extraBridgingFeesEVM); emit SimpleTransferFallbackAccountActivation(quoteNonce); return; } } - uint256 maxEvmAmountToSponsor = (amount * maxBpsToSponsor) / BPS_SCALAR; - uint256 amountToSponsor = extraFeesToSponsor; + uint256 maxEvmAmountToSponsor = ((amountInEVM + extraBridgingFeesEVM) * maxBpsToSponsor) / BPS_SCALAR; + uint256 amountToSponsor = extraBridgingFeesEVM; if (amountToSponsor > maxEvmAmountToSponsor) { amountToSponsor = maxEvmAmountToSponsor; } @@ -425,7 +427,7 @@ contract HyperCoreFlowExecutor is AccessControl { } } - uint256 finalAmount = amount + amountToSponsor; + uint256 finalAmount = amountInEVM + amountToSponsor; (uint256 quotedEvmAmount, uint64 quotedCoreAmount) = HyperCoreLib.maximumEVMSendAmountToAmounts( finalAmount, coreTokenInfo.tokenInfo.evmExtraWeiDecimals @@ -441,7 +443,7 @@ contract HyperCoreFlowExecutor is AccessControl { // If the amount is not safe to bridge because the bridge doesn't have enough liquidity, // fall back to sending user funds on HyperEVM. if (!isSafe) { - _fallbackHyperEVMFlow(amount, quoteNonce, maxBpsToSponsor, finalRecipient, extraFeesToSponsor); + _fallbackHyperEVMFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalRecipient, extraBridgingFeesEVM); emit SimpleTransferFallbackUnsafeToBridge(quoteNonce); return; } @@ -467,7 +469,7 @@ contract HyperCoreFlowExecutor is AccessControl { quoteNonce, finalRecipient, finalToken, - amount, + amountInEVM, amountToSponsor, quotedEvmAmount, quotedCoreAmount diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol index 3df0494cd..422c7acd6 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol @@ -46,7 +46,7 @@ contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { ) = SponsoredCCTPQuoteLib.getDespoitForBurnData(quote); IERC20(burnToken).safeTransferFrom(msg.sender, address(this), amount); - IERC20(burnToken).safeApprove(address(cctpTokenMessenger), amount); + IERC20(burnToken).forceApprove(address(cctpTokenMessenger), amount); usedNonces[quote.nonce] = true; From cb1e75a3c2891f3c38622a5ece5691a7086c07de Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Mon, 20 Oct 2025 08:34:58 -0400 Subject: [PATCH 13/47] feat: add arbitrary actions execution to sponsored bridging (#1143) * feat: add arbitrary actions execution to sponsored bridging Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * fix the MIN_COMPOSE_MSG_BYTE_LENGTH Signed-off-by: Ihor Farion * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice --------- Signed-off-by: Matt Rice Signed-off-by: Ihor Farion Co-authored-by: Ihor Farion --- contracts/handlers/MulticallHandler.sol | 2 +- .../handlers/PermissionedMulticallHandler.sol | 73 ++++++ .../interfaces/SponsoredCCTPInterface.sol | 14 + contracts/libraries/SponsoredCCTPQuoteLib.sol | 45 +++- .../mintburn/ArbitraryActionFlowExecutor.sol | 239 ++++++++++++++++++ contracts/periphery/mintburn/Constants.sol | 8 + .../mintburn/HyperCoreFlowExecutor.sol | 80 ++++-- .../SponsoredCCTPDstPeriphery.sol | 96 ++++++- .../sponsored-oft/ComposeMsgCodec.sol | 39 ++- .../mintburn/sponsored-oft/DstOFTHandler.sol | 94 ++++++- .../mintburn/sponsored-oft/QuoteSignLib.sol | 4 +- .../SponsoredOFTSrcPeriphery.sol | 4 +- .../mintburn/sponsored-oft/Structs.sol | 13 + script/116DeployDstOFTHandler.sol | 4 +- script/mintburn/CreateSponsoredDeposit.sol | 8 +- script/mintburn/SetAuthorizedPeriphery.s.sol | 2 +- 16 files changed, 657 insertions(+), 68 deletions(-) create mode 100644 contracts/handlers/PermissionedMulticallHandler.sol create mode 100644 contracts/periphery/mintburn/ArbitraryActionFlowExecutor.sol create mode 100644 contracts/periphery/mintburn/Constants.sol diff --git a/contracts/handlers/MulticallHandler.sol b/contracts/handlers/MulticallHandler.sol index c4af8bcc4..72f08fab5 100644 --- a/contracts/handlers/MulticallHandler.sol +++ b/contracts/handlers/MulticallHandler.sol @@ -61,7 +61,7 @@ contract MulticallHandler is AcrossMessageHandler, ReentrancyGuard { * @param message abi encoded array of Call structs, containing a target, callData, and value for each call that * the contract should make. */ - function handleV3AcrossMessage(address token, uint256, address, bytes memory message) external nonReentrant { + function handleV3AcrossMessage(address token, uint256, address, bytes memory message) public virtual nonReentrant { Instructions memory instructions = abi.decode(message, (Instructions)); // If there is no fallback recipient, call and revert if the inner call fails. diff --git a/contracts/handlers/PermissionedMulticallHandler.sol b/contracts/handlers/PermissionedMulticallHandler.sol new file mode 100644 index 000000000..cbd08d5f4 --- /dev/null +++ b/contracts/handlers/PermissionedMulticallHandler.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.0; + +import { MulticallHandler } from "./MulticallHandler.sol"; +import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; + +/** + * @title PermissionedMulticallHandler + * @notice Extension of MulticallHandler that restricts access to whitelisted callers + * @dev Uses OpenZeppelin's AccessControl for caller permission management. + * Only addresses with the WHITELISTED_CALLER_ROLE can call handleV3AcrossMessage. + */ +contract PermissionedMulticallHandler is MulticallHandler, AccessControl { + /// @notice Role identifier for whitelisted callers + bytes32 public constant WHITELISTED_CALLER_ROLE = keccak256("WHITELISTED_CALLER_ROLE"); + + /// @notice Emitted when a caller is whitelisted + event CallerWhitelisted(address indexed caller); + + /// @notice Emitted when a caller is removed from whitelist + event CallerRemovedFromWhitelist(address indexed caller); + + /// @notice Error thrown when caller is not whitelisted + error CallerNotWhitelisted(address caller); + + /** + * @notice Constructor that sets up the initial admin + * @param admin Address that will have DEFAULT_ADMIN_ROLE + */ + constructor(address admin) { + _grantRole(DEFAULT_ADMIN_ROLE, admin); + } + + /** + * @notice Overrides handleV3AcrossMessage to add caller whitelist check + * @dev Only addresses with WHITELISTED_CALLER_ROLE can call this function + * @param token The token being transferred + * @param amount The amount of tokens + * @param relayer The relayer address + * @param message The encoded Instructions struct + */ + function handleV3AcrossMessage( + address token, + uint256 amount, + address relayer, + bytes memory message + ) public override { + if (!hasRole(WHITELISTED_CALLER_ROLE, msg.sender)) { + revert CallerNotWhitelisted(msg.sender); + } + + // Call parent implementation + super.handleV3AcrossMessage(token, amount, relayer, message); + } + + /** + * @notice Add a caller to the whitelist + * @param caller Address to whitelist + */ + function whitelistCaller(address caller) external onlyRole(DEFAULT_ADMIN_ROLE) { + grantRole(WHITELISTED_CALLER_ROLE, caller); + emit CallerWhitelisted(caller); + } + + /** + * @notice Remove a caller from the whitelist + * @param caller Address to remove from whitelist + */ + function removeCallerFromWhitelist(address caller) external onlyRole(DEFAULT_ADMIN_ROLE) { + revokeRole(WHITELISTED_CALLER_ROLE, caller); + emit CallerRemovedFromWhitelist(caller); + } +} diff --git a/contracts/interfaces/SponsoredCCTPInterface.sol b/contracts/interfaces/SponsoredCCTPInterface.sol index 06173cbfc..fe58ea078 100644 --- a/contracts/interfaces/SponsoredCCTPInterface.sol +++ b/contracts/interfaces/SponsoredCCTPInterface.sol @@ -47,6 +47,16 @@ interface SponsoredCCTPInterface { uint256 maxAmountToSponsor ); + // Execution modes for the sponsored CCTP flow + enum ExecutionMode { + // Send to core and perform swap (if needed) there. + DirectToCore, + // Execute arbitrary actions (like a swap) on HyperEVM, then transfer to HyperCore + ArbitraryActionsToCore, + // Execute arbitrary actions on HyperEVM only (no HyperCore transfer) + ArbitraryActionsToEVM + } + // Params that will be used to create a sponsored CCTP quote and deposit for burn. struct SponsoredCCTPQuote { // The domain ID of the source chain. @@ -80,5 +90,9 @@ interface SponsoredCCTPInterface { // The final token that final recipient will receive. This is needed as it can be different from the burnToken // in which case we perform a swap on the destination chain. bytes32 finalToken; + // Execution mode: DirectToCore, ArbitraryActionsToCore, or ArbitraryActionsToEVM + uint8 executionMode; + // Encoded action data for arbitrary execution. Empty for DirectToCore mode. + bytes actionData; } } diff --git a/contracts/libraries/SponsoredCCTPQuoteLib.sol b/contracts/libraries/SponsoredCCTPQuoteLib.sol index 8fe36e3d2..caa339758 100644 --- a/contracts/libraries/SponsoredCCTPQuoteLib.sol +++ b/contracts/libraries/SponsoredCCTPQuoteLib.sol @@ -31,8 +31,8 @@ library SponsoredCCTPQuoteLib { uint256 private constant FEE_EXECUTED_INDEX = 164; uint256 private constant HOOK_DATA_INDEX = 228; - // Total length of the message body (message body + hook data + 6 32-byte fields in hook data) - uint256 private constant MSG_BYTES_LENGTH = 568; + // Minimum length of the message body (can be longer due to variable actionData) + uint256 private constant MIN_MSG_BYTES_LENGTH = 568; function getDespoitForBurnData( SponsoredCCTPInterface.SponsoredCCTPQuote memory quote @@ -63,17 +63,34 @@ library SponsoredCCTPQuoteLib { quote.maxBpsToSponsor, quote.maxUserSlippageBps, quote.finalRecipient, - quote.finalToken + quote.finalToken, + quote.executionMode, + quote.actionData ); } function validateMessage(bytes memory message) internal view returns (bool) { - return - message.length == MSG_BYTES_LENGTH && - message.toBytes32(MESSAGE_BODY_INDEX + MINT_RECIPIENT_INDEX).toAddress() == address(this) && - // 4 & 5 here are the indices of the finalRecipient and finalToken in the hook data - message.toBytes32(MESSAGE_BODY_INDEX + HOOK_DATA_INDEX + 32 * 4).isValidAddress() && - message.toBytes32(MESSAGE_BODY_INDEX + HOOK_DATA_INDEX + 32 * 5).isValidAddress(); + // Message must be at least the minimum length (can be longer due to variable actionData) + if (message.length < MIN_MSG_BYTES_LENGTH) { + return false; + } + + // Mint recipient should be this contract + if (message.toBytes32(MESSAGE_BODY_INDEX + MINT_RECIPIENT_INDEX).toAddress() != address(this)) { + return false; + } + + // Validate that finalRecipient and finalToken addresses are valid + bytes memory messageBody = message.slice(MESSAGE_BODY_INDEX, message.length); + bytes memory hookData = messageBody.slice(HOOK_DATA_INDEX, messageBody.length); + + // Decode to check address validity + (, , , , bytes32 finalRecipient, bytes32 finalToken, , ) = abi.decode( + hookData, + (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes) + ); + + return finalRecipient.isValidAddress() && finalToken.isValidAddress(); } function getSponsoredCCTPQuoteData( @@ -98,8 +115,10 @@ library SponsoredCCTPQuoteLib { quote.maxBpsToSponsor, quote.maxUserSlippageBps, quote.finalRecipient, - quote.finalToken - ) = abi.decode(hookData, (bytes32, uint256, uint256, uint256, bytes32, bytes32)); + quote.finalToken, + quote.executionMode, + quote.actionData + ) = abi.decode(hookData, (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes)); } function validateSignature( @@ -128,7 +147,9 @@ library SponsoredCCTPQuoteLib { quote.maxBpsToSponsor, quote.maxUserSlippageBps, quote.finalRecipient, - quote.finalToken + quote.finalToken, + quote.executionMode, + keccak256(quote.actionData) // Hash the actionData to keep signature size reasonable ) ); diff --git a/contracts/periphery/mintburn/ArbitraryActionFlowExecutor.sol b/contracts/periphery/mintburn/ArbitraryActionFlowExecutor.sol new file mode 100644 index 000000000..7af2a49d9 --- /dev/null +++ b/contracts/periphery/mintburn/ArbitraryActionFlowExecutor.sol @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { DonationBox } from "../../chain-adapters/DonationBox.sol"; + +// Import MulticallHandler +import { MulticallHandler } from "../../handlers/MulticallHandler.sol"; + +// Import constants +import { BPS_SCALAR } from "./Constants.sol"; + +/** + * @title ArbitraryActionFlowExecutor + * @notice Base contract for executing arbitrary action sequences using MulticallHandler + * @dev This contract provides shared functionality for both OFT and CCTP handlers to execute + * arbitrary actions on HyperEVM via MulticallHandler, with optional transfer to HyperCore. + * @custom:security-contact bugs@across.to + */ +abstract contract ArbitraryActionFlowExecutor { + using SafeERC20 for IERC20; + + /// @notice Compressed call struct (no value field to save gas) + struct CompressedCall { + address target; + bytes callData; + } + + /// @notice MulticallHandler contract instance + address public immutable multicallHandler; + + /// @notice Emitted when arbitrary actions are executed successfully + event ArbitraryActionsExecuted(bytes32 indexed quoteNonce, uint256 callCount, uint256 finalAmount); + + /// @notice Error thrown when final balance is insufficient + error InsufficientFinalBalance(address token, uint256 expected, uint256 actual); + + uint256 constant BPS_TOTAL_PRECISION = 18; + uint256 constant BPS_DECIMALS = 4; + uint256 constant BPS_PRECISION_SCALAR = 10 ** BPS_TOTAL_PRECISION; + + constructor(address _multicallHandler) { + multicallHandler = _multicallHandler; + } + + /** + * @notice Executes arbitrary actions by transferring tokens to MulticallHandler + * @dev Decompresses CompressedCall[] to MulticallHandler.Call[] format (adds value: 0) + * @param amount Amount of tokens to transfer to MulticallHandler + * @param quoteNonce Unique nonce for this quote + * @param maxBpsToSponsor Maximum basis points to sponsor + * @param initialToken Token to transfer to MulticallHandler + * @param finalRecipient Final recipient address + * @param finalToken Expected final token after actions + * @param actionData Encoded actions: abi.encode(CompressedCall[] calls) + * @param transferToCore Whether to transfer result to HyperCore + * @param extraFeesToSponsor Extra fees to sponsor + */ + function _executeArbitraryActionFlow( + uint256 amount, + bytes32 quoteNonce, + uint256 maxBpsToSponsor, + address initialToken, + address finalRecipient, + address finalToken, + bytes memory actionData, + bool transferToCore, + uint256 extraFeesToSponsor + ) internal { + // Decode the compressed action data + CompressedCall[] memory compressedCalls = abi.decode(actionData, (CompressedCall[])); + + // Total amount to sponsor is the extra fees to sponsor, ceiling division. + uint256 totalAmount = amount + extraFeesToSponsor; + uint256 bpsToSponsor = ((extraFeesToSponsor * BPS_PRECISION_SCALAR) + totalAmount - 1) / totalAmount; + uint256 maxBpsToSponsorAdjusted = maxBpsToSponsor * (10 ** (BPS_TOTAL_PRECISION - BPS_DECIMALS)); + if (bpsToSponsor > maxBpsToSponsorAdjusted) { + bpsToSponsor = maxBpsToSponsorAdjusted; + } + + // Snapshot balances + uint256 initialAmountSnapshot = IERC20(initialToken).balanceOf(address(this)); + uint256 finalAmountSnapshot = IERC20(finalToken).balanceOf(address(this)); + + // Transfer tokens to MulticallHandler + IERC20(initialToken).safeTransfer(multicallHandler, amount); + + // Decompress calls: add value: 0 to each call and wrap in Instructions + // We encode Instructions with calls and a drainLeftoverTokens call at the end + uint256 callCount = compressedCalls.length; + + // Build instructions for MulticallHandler + bytes memory instructions = _buildMulticallInstructions( + compressedCalls, + finalToken, + address(this) // Send leftover tokens back to this contract + ); + + // Execute via MulticallHandler + MulticallHandler(payable(multicallHandler)).handleV3AcrossMessage( + initialToken, + amount, + address(this), + instructions + ); + + uint256 finalAmount; + + // This means the swap (if one was intended) didn't happen (action failed), so we use the initial token as the final token. + if (initialAmountSnapshot == IERC20(initialToken).balanceOf(address(this))) { + finalToken = initialToken; + finalAmount = amount; + } else { + uint256 finalBalance = IERC20(finalToken).balanceOf(address(this)); + if (finalBalance >= finalAmountSnapshot) { + // This means the swap did happen, so we check the balance of the output token and send it. + finalAmount = finalBalance - finalAmountSnapshot; + } else { + // If we somehow lost final tokens, just set the finalAmount to 0. + finalAmount = 0; + } + } + + // Apply the bps to sponsor to the final amount to get the amount to sponsor, ceiling division. + uint256 bpsToSponsorAdjusted = BPS_PRECISION_SCALAR - bpsToSponsor; + uint256 amountToSponsor = (((finalAmount * BPS_PRECISION_SCALAR) + bpsToSponsorAdjusted - 1) / + bpsToSponsorAdjusted) - finalAmount; + if (amountToSponsor > 0) { + DonationBox donationBox = _getDonationBox(); + if (IERC20(finalToken).balanceOf(address(donationBox)) < amountToSponsor) { + amountToSponsor = 0; + } else { + donationBox.withdraw(IERC20(finalToken), amountToSponsor); + } + } + + emit ArbitraryActionsExecuted(quoteNonce, callCount, finalAmount); + + // Route to appropriate destination based on transferToCore flag + if (transferToCore) { + _executeSimpleTransferFlow( + finalAmount, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + amountToSponsor, + finalToken + ); + } else { + _fallbackHyperEVMFlow( + finalAmount, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + amountToSponsor, + finalToken + ); + } + } + + /** + * @notice Builds MulticallHandler Instructions from compressed calls + * @dev Decompresses calls by adding value: 0, and adds drainLeftoverTokens call at the end + */ + function _buildMulticallInstructions( + CompressedCall[] memory compressedCalls, + address finalToken, + address fallbackRecipient + ) internal view returns (bytes memory) { + uint256 callCount = compressedCalls.length; + + // Create Call[] array with value: 0 for each call, plus one for drainLeftoverTokens + MulticallHandler.Call[] memory calls = new MulticallHandler.Call[](callCount + 1); + + // Decompress: add value: 0 to each call + for (uint256 i = 0; i < callCount; ++i) { + calls[i] = MulticallHandler.Call({ + target: compressedCalls[i].target, + callData: compressedCalls[i].callData, + value: 0 + }); + } + + // Add final call to drain leftover tokens back to this contract + calls[callCount] = MulticallHandler.Call({ + target: multicallHandler, + callData: abi.encodeWithSelector( + MulticallHandler.drainLeftoverTokens.selector, + finalToken, + fallbackRecipient + ), + value: 0 + }); + + // Build Instructions struct + MulticallHandler.Instructions memory instructions = MulticallHandler.Instructions({ + calls: calls, + fallbackRecipient: fallbackRecipient + }); + + return abi.encode(instructions); + } + + /** + * @notice Execute simple transfer flow to HyperCore with the final token + * @dev Must be implemented by contracts that inherit from this contract + */ + function _executeSimpleTransferFlow( + uint256 finalAmount, + bytes32 quoteNonce, + uint256 maxBpsToSponsor, + address finalRecipient, + uint256 extraFeesToSponsor, + address finalToken + ) internal virtual; + + /** + * @notice Execute fallback HyperEVM flow (stay on HyperEVM) + * @dev Must be implemented by contracts that inherit from this contract + */ + function _fallbackHyperEVMFlow( + uint256 finalAmount, + bytes32 quoteNonce, + uint256 maxBpsToSponsor, + address finalRecipient, + uint256 extraFeesToSponsor, + address finalToken + ) internal virtual; + + /** + * @notice Get the donation box instance + * @dev Must be implemented by contracts that inherit from this contract + */ + function _getDonationBox() internal view virtual returns (DonationBox); + + /// @notice Allow contract to receive native tokens for arbitrary action execution + receive() external payable virtual {} +} diff --git a/contracts/periphery/mintburn/Constants.sol b/contracts/periphery/mintburn/Constants.sol new file mode 100644 index 000000000..2fd79521e --- /dev/null +++ b/contracts/periphery/mintburn/Constants.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +// Basis points decimals (4 decimals = 10000 for 100%) +uint256 constant BPS_DECIMALS = 4; + +// Basis points scalar (10000 = 100%) +uint256 constant BPS_SCALAR = 10 ** BPS_DECIMALS; diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 7f541cb73..cfbfe5a52 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -9,6 +9,7 @@ import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol"; import { CoreTokenInfo } from "./Structs.sol"; import { FinalTokenInfo } from "./Structs.sol"; import { SwapHandler } from "./SwapHandler.sol"; +import { BPS_DECIMALS, BPS_SCALAR } from "./Constants.sol"; /** * @title HyperCoreFlowExecutor @@ -20,9 +21,7 @@ contract HyperCoreFlowExecutor is AccessControl { using SafeERC20 for IERC20; // Common decimals scalars - uint256 public constant BPS_DECIMALS = 4; uint256 public constant PPM_DECIMALS = 6; - uint256 public constant BPS_SCALAR = 10 ** BPS_DECIMALS; uint256 public constant PPM_SCALAR = 10 ** PPM_DECIMALS; // Decimals to use for Price calculations in limit order-related calculation functions uint8 public constant PX_D = 8; @@ -377,7 +376,14 @@ contract HyperCoreFlowExecutor is AccessControl { uint256 extraFeesToSponsor ) internal { if (finalToken == baseToken) { - _executeSimpleTransferFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalRecipient, extraFeesToSponsor); + _executeSimpleTransferFlow( + amountInEVM, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + extraFeesToSponsor, + finalToken + ); } else { _initiateSwapFlow( amountInEVM, @@ -391,16 +397,16 @@ contract HyperCoreFlowExecutor is AccessControl { } } - /// @notice Execute a simple transfer flow in which we transfer `baseToken` to the user on HyperCore after receiving - /// an amount of baseToken from the user on HyperEVM + /// @notice Execute a simple transfer flow in which we transfer `finalToken` to the user on HyperCore after receiving + /// an amount of finalToken from the user on HyperEVM function _executeSimpleTransferFlow( uint256 amountInEVM, bytes32 quoteNonce, uint256 maxBpsToSponsor, address finalRecipient, - uint256 extraBridgingFeesEVM - ) internal { - address finalToken = baseToken; + uint256 extraBridgingFeesEVM, + address finalToken + ) internal virtual { CoreTokenInfo storage coreTokenInfo = coreTokenInfos[finalToken]; bool isSponsoredFlow = maxBpsToSponsor > 0; @@ -409,7 +415,14 @@ contract HyperCoreFlowExecutor is AccessControl { if (isSponsoredFlow) { revert AccountNotActivated(finalRecipient); } else { - _fallbackHyperEVMFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalRecipient, extraBridgingFeesEVM); + _fallbackHyperEVMFlow( + amountInEVM, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + extraBridgingFeesEVM, + finalToken + ); emit SimpleTransferFallbackAccountActivation(quoteNonce); return; } @@ -443,7 +456,14 @@ contract HyperCoreFlowExecutor is AccessControl { // If the amount is not safe to bridge because the bridge doesn't have enough liquidity, // fall back to sending user funds on HyperEVM. if (!isSafe) { - _fallbackHyperEVMFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalRecipient, extraBridgingFeesEVM); + _fallbackHyperEVMFlow( + amountInEVM, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + extraBridgingFeesEVM, + finalToken + ); emit SimpleTransferFallbackUnsafeToBridge(quoteNonce); return; } @@ -508,7 +528,14 @@ contract HyperCoreFlowExecutor is AccessControl { if (isSponsoredFlow) { revert AccountNotActivated(finalRecipient); } else { - _fallbackHyperEVMFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalRecipient, extraBridgingFeesEVM); + _fallbackHyperEVMFlow( + amountInEVM, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + extraBridgingFeesEVM, + initialToken + ); emit SwapFlowFallbackAccountActivation(quoteNonce); return; } @@ -541,7 +568,14 @@ contract HyperCoreFlowExecutor is AccessControl { minAllowableAmountToForwardCore - guaranteedLOOut > maxAmountToSponsorCore ) { // We can't provide the required slippage in a swap flow, try simple transfer flow instead - _executeSimpleTransferFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalRecipient, extraBridgingFeesEVM); + _executeSimpleTransferFlow( + amountInEVM, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + extraBridgingFeesEVM, + initialToken + ); emit SwapFlowFallbackTooExpensive( quoteNonce, minAllowableAmountToForwardCore - guaranteedLOOut, @@ -571,7 +605,8 @@ contract HyperCoreFlowExecutor is AccessControl { quoteNonce, maxBpsToSponsor, finalRecipient, - extraBridgingFeesEVM + extraBridgingFeesEVM, + initialToken ); emit SwapFlowFallbackDonationBox(quoteNonce, finalToken, totalEVMAmountToSponsor); return; @@ -591,7 +626,14 @@ contract HyperCoreFlowExecutor is AccessControl { ); if (!isSafeToBridgeMainToken || !isSafeTobridgeSponsorshipFunds) { - _fallbackHyperEVMFlow(amountInEVM, quoteNonce, maxBpsToSponsor, finalRecipient, extraBridgingFeesEVM); + _fallbackHyperEVMFlow( + amountInEVM, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + extraBridgingFeesEVM, + initialToken + ); emit SwapFlowFallbackUnsafeToBridge( quoteNonce, isSafeToBridgeMainToken, @@ -880,19 +922,19 @@ contract HyperCoreFlowExecutor is AccessControl { bytes32 quoteNonce, uint256 maxBpsToSponsor, address finalRecipient, - uint256 extraFeesToSponsor - ) internal { - address finalToken = baseToken; + uint256 extraFeesToSponsor, + address finalToken + ) internal virtual { uint256 maxEvmAmountToSponsor = ((amount + extraFeesToSponsor) * maxBpsToSponsor) / BPS_SCALAR; uint256 sponsorshipFundsToForward = extraFeesToSponsor > maxEvmAmountToSponsor ? maxEvmAmountToSponsor : extraFeesToSponsor; - if (!_availableInDonationBox(baseToken, sponsorshipFundsToForward)) { + if (!_availableInDonationBox(finalToken, sponsorshipFundsToForward)) { sponsorshipFundsToForward = 0; } if (sponsorshipFundsToForward > 0) { - _getFromDonationBox(baseToken, sponsorshipFundsToForward); + _getFromDonationBox(finalToken, sponsorshipFundsToForward); } uint256 totalAmountToForward = amount + sponsorshipFundsToForward; IERC20(finalToken).safeTransfer(finalRecipient, totalAmountToForward); diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol index 6b91bf295..8eacf0583 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol @@ -7,9 +7,11 @@ import { IMessageTransmitterV2 } from "../../../external/interfaces/CCTPInterfac import { SponsoredCCTPQuoteLib } from "../../../libraries/SponsoredCCTPQuoteLib.sol"; import { SponsoredCCTPInterface } from "../../../interfaces/SponsoredCCTPInterface.sol"; import { Bytes32ToAddress } from "../../../libraries/AddressConverters.sol"; +import { DonationBox } from "../../../chain-adapters/DonationBox.sol"; import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; +import { ArbitraryActionFlowExecutor } from "../ArbitraryActionFlowExecutor.sol"; -contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecutor { +contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecutor, ArbitraryActionFlowExecutor { using SafeERC20 for IERC20Metadata; using Bytes32ToAddress for bytes32; @@ -33,7 +35,8 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu uint32 _coreIndex, bool _canBeUsedForAccountActivation, uint64 _accountActivationFeeCore, - uint64 _bridgeSafetyBufferCore + uint64 _bridgeSafetyBufferCore, + address _multicallHandler ) HyperCoreFlowExecutor( _donationBox, @@ -43,6 +46,7 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu _accountActivationFeeCore, _bridgeSafetyBufferCore ) + ArbitraryActionFlowExecutor(_multicallHandler) { cctpMessageTransmitter = IMessageTransmitterV2(_cctpMessageTransmitter); signer = _signer; @@ -73,18 +77,40 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu usedNonces[quote.nonce] = true; } - _executeFlow( - // We subtract the feeExecuted from the `amount` as the `amount` is the what the user pays on the source chain. - quote.amount - feeExecuted, - quote.nonce, - // If the quote is invalid we don't sponsor the flow or the extra fees - isQuoteValid ? quote.maxBpsToSponsor : 0, - quote.maxUserSlippageBps, - quote.finalRecipient.toAddress(), - // If the quote is invalid we don't want to swap, so we use the base token as the final token - isQuoteValid ? quote.finalToken.toAddress() : baseToken, - isQuoteValid ? feeExecuted : 0 - ); + uint256 amountAfterFees = quote.amount - feeExecuted; + + // Route to appropriate execution based on executionMode + if ( + isQuoteValid && + (quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) || + quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToEVM)) + ) { + // Execute arbitrary actions flow + _executeArbitraryActionFlow( + amountAfterFees, + quote.nonce, + quote.maxBpsToSponsor, + baseToken, // initialToken + quote.finalRecipient.toAddress(), + quote.finalToken.toAddress(), + quote.actionData, + quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToCore), + feeExecuted + ); + } else { + // Execute standard HyperCore flow (default) + _executeFlow( + amountAfterFees, + quote.nonce, + // If the quote is invalid we don't sponsor the flow or the extra fees + isQuoteValid ? quote.maxBpsToSponsor : 0, + quote.maxUserSlippageBps, + quote.finalRecipient.toAddress(), + // If the quote is invalid we don't want to swap, so we use the base token as the final token + isQuoteValid ? quote.finalToken.toAddress() : baseToken, + isQuoteValid ? feeExecuted : 0 + ); + } emit SponsoredMintAndWithdraw( quote.nonce, @@ -106,4 +132,46 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu !usedNonces[quote.nonce] && quote.deadline + quoteDeadlineBuffer >= block.timestamp; } + + /// @notice Override to resolve diamond inheritance - use HyperCoreFlowExecutor implementation + function _executeSimpleTransferFlow( + uint256 finalAmount, + bytes32 quoteNonce, + uint256 maxBpsToSponsor, + address finalRecipient, + uint256 extraFeesToSponsor, + address finalToken + ) internal override(ArbitraryActionFlowExecutor, HyperCoreFlowExecutor) { + HyperCoreFlowExecutor._executeSimpleTransferFlow( + finalAmount, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + extraFeesToSponsor, + finalToken + ); + } + + /// @notice Override to resolve diamond inheritance - use HyperCoreFlowExecutor implementation + function _fallbackHyperEVMFlow( + uint256 finalAmount, + bytes32 quoteNonce, + uint256 maxBpsToSponsor, + address finalRecipient, + uint256 extraFeesToSponsor, + address finalToken + ) internal override(ArbitraryActionFlowExecutor, HyperCoreFlowExecutor) { + HyperCoreFlowExecutor._fallbackHyperEVMFlow( + finalAmount, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + extraFeesToSponsor, + finalToken + ); + } + + function _getDonationBox() internal view override(ArbitraryActionFlowExecutor) returns (DonationBox) { + return donationBox; + } } diff --git a/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol b/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol index d1d92b1ae..df11de58d 100644 --- a/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol +++ b/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol @@ -10,7 +10,9 @@ library ComposeMsgCodec { uint256 internal constant MAX_USER_SLIPPAGE_BPS_OFFSET = 96; uint256 internal constant FINAL_RECIPIENT_OFFSET = 128; uint256 internal constant FINAL_TOKEN_OFFSET = 160; - uint256 internal constant COMPOSE_MSG_BYTE_LENGTH = 192; + uint256 internal constant EXECUTION_MODE_OFFSET = 192; + // Minimum length with empty actionData: 7 regular params (32 bytes each) and 1 dynamic byte array (minumum 64 bytes) + uint256 internal constant MIN_COMPOSE_MSG_BYTE_LENGTH = 288; function _encode( bytes32 nonce, @@ -18,9 +20,21 @@ library ComposeMsgCodec { uint256 maxBpsToSponsor, uint256 maxUserSlippageBps, bytes32 finalRecipient, - bytes32 finalToken + bytes32 finalToken, + uint8 executionMode, + bytes memory actionData ) internal pure returns (bytes memory) { - return abi.encode(nonce, deadline, maxBpsToSponsor, maxUserSlippageBps, finalRecipient, finalToken); + return + abi.encode( + nonce, + deadline, + maxBpsToSponsor, + maxUserSlippageBps, + finalRecipient, + finalToken, + executionMode, + actionData + ); } function _getNonce(bytes memory data) internal pure returns (bytes32 v) { @@ -47,7 +61,24 @@ library ComposeMsgCodec { return BytesLib.toBytes32(data, FINAL_TOKEN_OFFSET); } + function _getExecutionMode(bytes memory data) internal pure returns (uint8 v) { + (, , , , , , uint8 executionMode, ) = abi.decode( + data, + (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes) + ); + return executionMode; + } + + function _getActionData(bytes memory data) internal pure returns (bytes memory v) { + (, , , , , , , bytes memory actionData) = abi.decode( + data, + (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes) + ); + return actionData; + } + function _isValidComposeMsgBytelength(bytes memory data) internal pure returns (bool valid) { - valid = data.length == COMPOSE_MSG_BYTE_LENGTH; + // Message must be at least the minimum length (can be longer due to variable actionData) + valid = data.length >= MIN_COMPOSE_MSG_BYTE_LENGTH; } } diff --git a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol index 8aa342133..cf7046046 100644 --- a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol +++ b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol @@ -6,19 +6,23 @@ import { OFTComposeMsgCodec } from "../../../libraries/OFTComposeMsgCodec.sol"; import { DonationBox } from "../../../chain-adapters/DonationBox.sol"; import { HyperCoreLib } from "../../../libraries/HyperCoreLib.sol"; import { ComposeMsgCodec } from "./ComposeMsgCodec.sol"; +import { ExecutionMode } from "./Structs.sol"; import { AddressToBytes32, Bytes32ToAddress } from "../../../libraries/AddressConverters.sol"; import { IOFT, IOAppCore } from "../../../interfaces/IOFT.sol"; import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; +import { ArbitraryActionFlowExecutor } from "../ArbitraryActionFlowExecutor.sol"; import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /// @notice Handler that receives funds from LZ system, checks authorizations(both against LZ system and src chain /// sender), and forwards authorized params to the `_executeFlow` function -contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor { +contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryActionFlowExecutor { using ComposeMsgCodec for bytes; using Bytes32ToAddress for bytes32; using AddressToBytes32 for address; + using SafeERC20 for IERC20; /// @notice We expect bridge amount that comes through to this Handler to be 1:1 with the src send amount, and we /// require our src handler to ensure that it is. We don't sponsor extra bridge fees in this handler @@ -63,7 +67,8 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor { uint32 _coreIndex, bool _canBeUsedForAccountActivation, uint64 _accountActivationFeeCore, - uint64 _bridgeSafetyBufferCore + uint64 _bridgeSafetyBufferCore, + address _multicallHandler ) HyperCoreFlowExecutor( _donationBox, @@ -73,6 +78,7 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor { _accountActivationFeeCore, _bridgeSafetyBufferCore ) + ArbitraryActionFlowExecutor(_multicallHandler) { // baseToken is assigned on `HyperCoreFlowExecutor` creation if (baseToken != IOFT(_ioft).token()) { @@ -127,16 +133,38 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor { uint256 maxUserSlippageBps = composeMsg._getMaxUserSlippageBps(); address finalRecipient = composeMsg._getFinalRecipient().toAddress(); address finalToken = composeMsg._getFinalToken().toAddress(); - - _executeFlow( - amountLD, - quoteNonce, - maxBpsToSponsor, - maxUserSlippageBps, - finalRecipient, - finalToken, - EXTRA_FEES_TO_SPONSOR - ); + uint8 executionMode = composeMsg._getExecutionMode(); + bytes memory actionData = composeMsg._getActionData(); + + // Route to appropriate execution based on executionMode + if ( + executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) || + executionMode == uint8(ExecutionMode.ArbitraryActionsToEVM) + ) { + // Execute arbitrary actions flow + _executeArbitraryActionFlow( + amountLD, + quoteNonce, + maxBpsToSponsor, + baseToken, // initialToken + finalRecipient, + finalToken, + actionData, + executionMode == uint8(ExecutionMode.ArbitraryActionsToCore), + EXTRA_FEES_TO_SPONSOR + ); + } else { + // Execute standard HyperCore flow (default) + _executeFlow( + amountLD, + quoteNonce, + maxBpsToSponsor, + maxUserSlippageBps, + finalRecipient, + finalToken, + EXTRA_FEES_TO_SPONSOR + ); + } } /// @notice Checks that message was authorized by LayerZero's identity system and that it came from authorized src periphery @@ -167,4 +195,46 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor { revert UnauthorizedSrcPeriphery(_srcEid); } } + + /// @notice Override to resolve diamond inheritance - use HyperCoreFlowExecutor implementation + function _executeSimpleTransferFlow( + uint256 finalAmount, + bytes32 quoteNonce, + uint256 maxBpsToSponsor, + address finalRecipient, + uint256 extraFeesToSponsor, + address finalToken + ) internal override(ArbitraryActionFlowExecutor, HyperCoreFlowExecutor) { + HyperCoreFlowExecutor._executeSimpleTransferFlow( + finalAmount, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + extraFeesToSponsor, + finalToken + ); + } + + /// @notice Override to resolve diamond inheritance - use HyperCoreFlowExecutor implementation + function _fallbackHyperEVMFlow( + uint256 finalAmount, + bytes32 quoteNonce, + uint256 maxBpsToSponsor, + address finalRecipient, + uint256 extraFeesToSponsor, + address finalToken + ) internal override(ArbitraryActionFlowExecutor, HyperCoreFlowExecutor) { + HyperCoreFlowExecutor._fallbackHyperEVMFlow( + finalAmount, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + extraFeesToSponsor, + finalToken + ); + } + + function _getDonationBox() internal view override returns (DonationBox) { + return donationBox; + } } diff --git a/contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol b/contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol index 26351d6ed..5a9e64847 100644 --- a/contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol +++ b/contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol @@ -24,7 +24,9 @@ library QuoteSignLib { p.finalRecipient, p.finalToken, p.lzReceiveGasLimit, - p.lzComposeGasLimit + p.lzComposeGasLimit, + p.executionMode, + keccak256(p.actionData) // Hash the actionData to keep signature size reasonable ) ); } diff --git a/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol index 527d2ccc6..860c49b8e 100644 --- a/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol @@ -110,7 +110,9 @@ contract SponsoredOFTSrcPeriphery is Ownable { quote.signedParams.maxBpsToSponsor, quote.unsignedParams.maxUserSlippageBps, quote.signedParams.finalRecipient, - quote.signedParams.finalToken + quote.signedParams.finalToken, + quote.signedParams.executionMode, + quote.signedParams.actionData ); bytes memory extraOptions = MinimalLZOptions diff --git a/contracts/periphery/mintburn/sponsored-oft/Structs.sol b/contracts/periphery/mintburn/sponsored-oft/Structs.sol index cfc3eaf33..b947e6555 100644 --- a/contracts/periphery/mintburn/sponsored-oft/Structs.sol +++ b/contracts/periphery/mintburn/sponsored-oft/Structs.sol @@ -3,6 +3,16 @@ pragma solidity ^0.8.23; import { SendParam, MessagingFee } from "../../../interfaces/IOFT.sol"; +/// @notice Execution modes for the sponsored OFT flow +enum ExecutionMode { + // Send to core and perform swap (if needed) there. + DirectToCore, + // Execute arbitrary actions (like a swap) on HyperEVM, then transfer to HyperCore + ArbitraryActionsToCore, + // Execute arbitrary actions on HyperEVM only (no HyperCore transfer) + ArbitraryActionsToEVM +} + /// @notice A structure with all the relevant information about a particular sponsored bridging flow order struct Quote { SignedQuoteParams signedParams; @@ -25,6 +35,9 @@ struct SignedQuoteParams { // Signed gas limits for destination-side LZ execution uint256 lzReceiveGasLimit; // gas limit for `lzReceive` call on destination side uint256 lzComposeGasLimit; // gas limit for `lzCompose` call on destination side + // Execution mode and action data + uint8 executionMode; // ExecutionMode: DirectToCore, ArbitraryActionsToCore, or ArbitraryActionsToEVM + bytes actionData; // Encoded action data for arbitrary execution. Empty for DirectToCore mode. } /// @notice Unsigned params of the sponsored bridging flow quote: user is free to choose these diff --git a/script/116DeployDstOFTHandler.sol b/script/116DeployDstOFTHandler.sol index a9f7b04aa..62484b541 100644 --- a/script/116DeployDstOFTHandler.sol +++ b/script/116DeployDstOFTHandler.sol @@ -28,6 +28,7 @@ contract DeployDstOFTHandler is Script, Test, DeploymentUtils { bool canBeUsedForAccountActivation = true; uint64 accountActivationFeeCore = 100000000; uint64 bridgeSafetyBufferCore = 1_000_000_000_00000000; // 1billion USDC (8 decimals) + address multicallHandler = getL2Address(block.chainid, "multicallHandler"); DonationBox donationBox = new DonationBox(); @@ -39,7 +40,8 @@ contract DeployDstOFTHandler is Script, Test, DeploymentUtils { coreIndex, canBeUsedForAccountActivation, accountActivationFeeCore, - bridgeSafetyBufferCore + bridgeSafetyBufferCore, + multicallHandler ); console.log("DstOFTHandler deployed to:", address(dstOFTHandler)); diff --git a/script/mintburn/CreateSponsoredDeposit.sol b/script/mintburn/CreateSponsoredDeposit.sol index 2fccf74b9..1f18fd9c1 100644 --- a/script/mintburn/CreateSponsoredDeposit.sol +++ b/script/mintburn/CreateSponsoredDeposit.sol @@ -78,7 +78,9 @@ contract CreateSponsoredDeposit is Script { finalRecipient: finalRecipient.toBytes32(), finalToken: finalToken.toBytes32(), lzReceiveGasLimit: lzReceiveGasLimit, - lzComposeGasLimit: lzComposeGasLimit + lzComposeGasLimit: lzComposeGasLimit, + executionMode: 0, // DirectToCore mode + actionData: "" // Empty for DirectToCore mode }); UnsignedQuoteParams memory unsignedParams = UnsignedQuoteParams({ @@ -115,7 +117,9 @@ contract CreateSponsoredDeposit is Script { quote.signedParams.maxBpsToSponsor, quote.unsignedParams.maxUserSlippageBps, quote.signedParams.finalRecipient, - quote.signedParams.finalToken + quote.signedParams.finalToken, + quote.signedParams.executionMode, + quote.signedParams.actionData ); bytes memory extraOptions = MinimalLZOptions diff --git a/script/mintburn/SetAuthorizedPeriphery.s.sol b/script/mintburn/SetAuthorizedPeriphery.s.sol index d94524388..7832befa3 100644 --- a/script/mintburn/SetAuthorizedPeriphery.s.sol +++ b/script/mintburn/SetAuthorizedPeriphery.s.sol @@ -19,7 +19,7 @@ contract SetAuthorizedPeriphery is Script { address dstHandlerAddress = 0x40ad479382Ad2a5c3061487A5094a677B00f6Cb0; // --- END CONFIG --- - DstOFTHandler dstHandler = DstOFTHandler(dstHandlerAddress); + DstOFTHandler dstHandler = DstOFTHandler(payable(dstHandlerAddress)); vm.startBroadcast(deployerPrivateKey); From 9aa5bbced4947a4e15fafaabc965fd61bdedc093 Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Mon, 20 Oct 2025 15:16:32 -0400 Subject: [PATCH 14/47] Update contracts/periphery/mintburn/HyperCoreFlowExecutor.sol Co-authored-by: nicholaspai <9457025+nicholaspai@users.noreply.github.com> --- contracts/periphery/mintburn/HyperCoreFlowExecutor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index cfbfe5a52..58c3ded19 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -13,7 +13,7 @@ import { BPS_DECIMALS, BPS_SCALAR } from "./Constants.sol"; /** * @title HyperCoreFlowExecutor - * @notice Contract handling HyperCore interactions for trasnfer-to-core or swap-with-core actions after stablecoin bridge transactions + * @notice Contract handling HyperCore interactions for transfer-to-core or swap-with-core actions after stablecoin bridge transactions * @dev This contract is designed to work with stablecoins. baseToken and every finalToken should all be stablecoins. * @custom:security-contact bugs@across.to */ From 7ff09825d9a4ee800dc9fb181d6f427a085747b2 Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Mon, 20 Oct 2025 15:16:41 -0400 Subject: [PATCH 15/47] Update contracts/periphery/mintburn/HyperCoreFlowExecutor.sol Co-authored-by: nicholaspai <9457025+nicholaspai@users.noreply.github.com> --- contracts/periphery/mintburn/HyperCoreFlowExecutor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 58c3ded19..48a4c4641 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -700,7 +700,7 @@ contract HyperCoreFlowExecutor is AccessControl { ); } - /// @notice Finalizes pending queue of swaps for `finalToken` if a corresponsing SwapHandler has enough balance + /// @notice Finalizes pending queue of swaps for `finalToken` if a corresponding SwapHandler has enough balance function finalizePendingSwaps(address finalToken, uint256 maxToProcess) external { FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); CoreTokenInfo memory coreTokenInfo = coreTokenInfos[finalToken]; From 335b7bd040c91b91d64df23ac242c8e1377f83fc Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Mon, 20 Oct 2025 15:17:55 -0400 Subject: [PATCH 16/47] fix typo Signed-off-by: Matt Rice --- contracts/libraries/SponsoredCCTPQuoteLib.sol | 2 +- .../mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/libraries/SponsoredCCTPQuoteLib.sol b/contracts/libraries/SponsoredCCTPQuoteLib.sol index caa339758..c10a063ff 100644 --- a/contracts/libraries/SponsoredCCTPQuoteLib.sol +++ b/contracts/libraries/SponsoredCCTPQuoteLib.sol @@ -34,7 +34,7 @@ library SponsoredCCTPQuoteLib { // Minimum length of the message body (can be longer due to variable actionData) uint256 private constant MIN_MSG_BYTES_LENGTH = 568; - function getDespoitForBurnData( + function getDepositForBurnData( SponsoredCCTPInterface.SponsoredCCTPQuote memory quote ) internal diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol index 422c7acd6..1281771b9 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol @@ -43,7 +43,7 @@ contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { uint256 maxFee, uint32 minFinalityThreshold, bytes memory hookData - ) = SponsoredCCTPQuoteLib.getDespoitForBurnData(quote); + ) = SponsoredCCTPQuoteLib.getDepositForBurnData(quote); IERC20(burnToken).safeTransferFrom(msg.sender, address(this), amount); IERC20(burnToken).forceApprove(address(cctpTokenMessenger), amount); From 5de7a4bc0dcd153ed47e68b76293f96f032ccad3 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Tue, 21 Oct 2025 09:47:51 -0400 Subject: [PATCH 17/47] moved nonce setting before external calls Signed-off-by: Faisal Usmani --- .../mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol index 1281771b9..af18f3da7 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol @@ -45,11 +45,11 @@ contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { bytes memory hookData ) = SponsoredCCTPQuoteLib.getDepositForBurnData(quote); + usedNonces[quote.nonce] = true; + IERC20(burnToken).safeTransferFrom(msg.sender, address(this), amount); IERC20(burnToken).forceApprove(address(cctpTokenMessenger), amount); - usedNonces[quote.nonce] = true; - cctpTokenMessenger.depositForBurnWithHook( amount, destinationDomain, From 356c8522ea6fb1ef35f8de6246459c276d3fb3e7 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Tue, 21 Oct 2025 18:53:58 -0400 Subject: [PATCH 18/47] Fix: Remvoed token info setting on deploy Signed-off-by: Faisal Usmani --- .../mintburn/HyperCoreFlowExecutor.sol | 12 +--- foundry.toml | 4 +- script/113DeploySponsoredCCTPSrcPeriphery.sol | 41 +++++++++++ script/114DeploySponsoredCCTPDstPeriphery.sol | 69 +++++++++++++++++++ script/116DeployDstOFTHandler.sol | 10 +++ 5 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 script/113DeploySponsoredCCTPSrcPeriphery.sol create mode 100644 script/114DeploySponsoredCCTPDstPeriphery.sol diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 48a4c4641..3356b7a23 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -257,20 +257,10 @@ contract HyperCoreFlowExecutor is AccessControl { uint64 _accountActivationFeeCore, uint64 _bridgeSafetyBufferCore ) { - // Handler core account must be prefunded to prevent loss of funds. Predict address -> fund -> deploy - require(HyperCoreLib.coreUserExists(address(this)), "Handler @ core doesn't exist"); - donationBox = DonationBox(_donationBox); // Initialize this to 1 as to save 0 for special events when "no cloid is set" = no associated limit order nextCloid = 1; - _setCoreTokenInfo( - _baseToken, - _coreIndex, - _canBeUsedForAccountActivation, - _accountActivationFeeCore, - _bridgeSafetyBufferCore - ); baseToken = _baseToken; // AccessControl setup @@ -957,7 +947,7 @@ contract HyperCoreFlowExecutor is AccessControl { uint64 bridgeSafetyBufferCore ) internal { HyperCoreLib.TokenInfo memory tokenInfo = HyperCoreLib.tokenInfo(coreIndex); - require(tokenInfo.evmContract == token, "Token mismatch"); + // require(tokenInfo.evmContract == token, "Token mismatch"); (uint256 accountActivationFeeEVM, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( accountActivationFeeCore, diff --git a/foundry.toml b/foundry.toml index 5bc8bcd9a..5bccbae62 100644 --- a/foundry.toml +++ b/foundry.toml @@ -66,8 +66,8 @@ zksync = "${NODE_URL_324}" hyperevm = "${NODE_URL_999}" # testnet -arbitrum_testnet = "${NODE_URL_421614}" -hyperevm_testnet = "${NODE_URL_998}" +arbitrum-testnet = "${NODE_URL_421614}" +hyperevm-testnet = "${NODE_URL_998}" [etherscan] diff --git a/script/113DeploySponsoredCCTPSrcPeriphery.sol b/script/113DeploySponsoredCCTPSrcPeriphery.sol new file mode 100644 index 000000000..d192aa910 --- /dev/null +++ b/script/113DeploySponsoredCCTPSrcPeriphery.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Test } from "forge-std/Test.sol"; +import { console } from "forge-std/console.sol"; +import { DeploymentUtils } from "./utils/DeploymentUtils.sol"; + +import { SponsoredCCTPSrcPeriphery } from "../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol"; + +// Deploy: forge script script/114DeploySponsoredCCTPSrcPeriphery.sol:DeploySponsoredCCTPSrcPeriphery --rpc-url -vvvv +contract DeploySponsoredCCTPSrcPeriphery is Script, Test, DeploymentUtils { + function run() external { + console.log("Deploying SponsoredCCTPSrcPeriphery..."); + console.log("Chain ID:", block.chainid); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + address cctpTokenMessenger = getL2Address(block.chainid, "cctpV2TokenMessenger"); + // Testnet + // address cctpTokenMessenger = 0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA; + + uint32 sourceDomain = getCircleDomainId(block.chainid); + + console.log("cctpTokenMessenger:", cctpTokenMessenger); + console.log("sourceDomain:", sourceDomain); + console.log("deployer:", deployer); + + vm.startBroadcast(deployerPrivateKey); + + SponsoredCCTPSrcPeriphery sponsoredCCTPSrcPeriphery = new SponsoredCCTPSrcPeriphery( + cctpTokenMessenger, + sourceDomain, + deployer + ); + + console.log("SponsoredCCTPSrcPeriphery deployed to:", address(sponsoredCCTPSrcPeriphery)); + } +} diff --git a/script/114DeploySponsoredCCTPDstPeriphery.sol b/script/114DeploySponsoredCCTPDstPeriphery.sol new file mode 100644 index 000000000..d694337a8 --- /dev/null +++ b/script/114DeploySponsoredCCTPDstPeriphery.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Test } from "forge-std/Test.sol"; +import { console } from "forge-std/console.sol"; + +import { DeploymentUtils } from "./utils/DeploymentUtils.sol"; +import { DonationBox } from "../contracts/chain-adapters/DonationBox.sol"; +import { SponsoredCCTPDstPeriphery } from "../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol"; + +// Deploy: forge script script/114DeploySponsoredCCTPDstPeriphery.sol:DeploySponsoredCCTPDstPeriphery --rpc-url -vvvv +contract DeploySponsoredCCTPDstPeriphery is Script, Test, DeploymentUtils { + function run() external { + console.log("Deploying SponsoredCCTPDstPeriphery..."); + console.log("Chain ID:", block.chainid); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + address cctpMessageTransmitter = getL2Address(block.chainid, "cctpV2MessageTransmitter"); + // address cctpMessageTransmitter = 0x81D40F21F12A8F0E3252Bccb954D722d4c464B64; + + vm.startBroadcast(deployerPrivateKey); + + DonationBox donationBox = new DonationBox(); + console.log("DonationBox deployed to:", address(donationBox)); + + // USDC on HyperEVM Testnet + // address baseToken = 0x2B3370eE501B4a559b57D449569354196457D8Ab; + // USDC on HyperEVM Mainnet + address baseToken = 0xb88339CB7199b77E23DB6E890353E22632Ba630f; + uint32 coreIndex = 0; + bool canBeUsedForAccountActivation = true; + uint64 accountActivationFeeCore = 100000000; // 1 USDC + uint64 bridgeSafetyBufferCore = 1_000_000_00000000; // 1mil USDC (8 decimals) + + SponsoredCCTPDstPeriphery sponsoredCCTPDstPeriphery = new SponsoredCCTPDstPeriphery( + cctpMessageTransmitter, + deployer, + address(donationBox), + baseToken, + coreIndex, + canBeUsedForAccountActivation, + accountActivationFeeCore, + bridgeSafetyBufferCore, + address(0) + ); + + console.log("SponsoredCCTPDstPeriphery deployed to:", address(sponsoredCCTPDstPeriphery)); + + donationBox.transferOwnership(address(sponsoredCCTPDstPeriphery)); + + console.log("DonationBox ownership transferred to:", address(sponsoredCCTPDstPeriphery)); + + sponsoredCCTPDstPeriphery.setCoreTokenInfo( + baseToken, + coreIndex, + canBeUsedForAccountActivation, + accountActivationFeeCore, + bridgeSafetyBufferCore + ); + + console.log("CoreTokenInfo set"); + + vm.stopBroadcast(); + } +} diff --git a/script/116DeployDstOFTHandler.sol b/script/116DeployDstOFTHandler.sol index 62484b541..adb569002 100644 --- a/script/116DeployDstOFTHandler.sol +++ b/script/116DeployDstOFTHandler.sol @@ -49,6 +49,16 @@ contract DeployDstOFTHandler is Script, Test, DeploymentUtils { console.log("DonationBox ownership transferred to:", address(dstOFTHandler)); + dstOFTHandler.setCoreTokenInfo( + baseToken, + coreIndex, + canBeUsedForAccountActivation, + accountActivationFeeCore, + bridgeSafetyBufferCore + ); + + console.log("CoreTokenInfo set"); + vm.stopBroadcast(); } } From 08503a145602a939d7e4fdd2735533d0168f34cf Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Tue, 21 Oct 2025 18:59:54 -0400 Subject: [PATCH 19/47] Undo commented out require Signed-off-by: Faisal Usmani --- contracts/periphery/mintburn/HyperCoreFlowExecutor.sol | 2 +- package.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 3356b7a23..df4beae22 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -947,7 +947,7 @@ contract HyperCoreFlowExecutor is AccessControl { uint64 bridgeSafetyBufferCore ) internal { HyperCoreLib.TokenInfo memory tokenInfo = HyperCoreLib.tokenInfo(coreIndex); - // require(tokenInfo.evmContract == token, "Token mismatch"); + require(tokenInfo.evmContract == token, "Token mismatch"); (uint256 accountActivationFeeEVM, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( accountActivationFeeCore, diff --git a/package.json b/package.json index 63b681ea7..f896f8b68 100644 --- a/package.json +++ b/package.json @@ -152,5 +152,6 @@ "**/eth-crypto/secp256k1": "5.0.1", "**/create-hash/sha.js": "2.4.12", "sha.js": "2.4.12" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } From c402e1f5a70e9c9ec206519516c96672bd2ad892 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Tue, 21 Oct 2025 20:50:34 -0400 Subject: [PATCH 20/47] Remove unused constructor args Signed-off-by: Faisal Usmani --- .../periphery/mintburn/HyperCoreFlowExecutor.sol | 13 +------------ .../sponsored-cctp/SponsoredCCTPDstPeriphery.sol | 16 +--------------- .../mintburn/sponsored-oft/DstOFTHandler.sol | 16 +--------------- package.json | 3 +-- script/114DeploySponsoredCCTPDstPeriphery.sol | 14 -------------- script/116DeployDstOFTHandler.sol | 14 -------------- 6 files changed, 4 insertions(+), 72 deletions(-) diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index df4beae22..cdfbd00db 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -244,19 +244,8 @@ contract HyperCoreFlowExecutor is AccessControl { * * @param _donationBox Sponsorship funds live here * @param _baseToken Main token used with this Forwarder - * @param _coreIndex HCore index of baseToken - * @param _canBeUsedForAccountActivation Whether or not baseToken can be used for account activation fee on HCore - * @param _accountActivationFeeCore Fee amount to pay for account activation - * @param _bridgeSafetyBufferCore Buffer to use the availability of Bridge funds on core side when bridging this token */ - constructor( - address _donationBox, - address _baseToken, - uint32 _coreIndex, - bool _canBeUsedForAccountActivation, - uint64 _accountActivationFeeCore, - uint64 _bridgeSafetyBufferCore - ) { + constructor(address _donationBox, address _baseToken) { donationBox = DonationBox(_donationBox); // Initialize this to 1 as to save 0 for special events when "no cloid is set" = no associated limit order nextCloid = 1; diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol index 8eacf0583..c56c14ea6 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol @@ -32,22 +32,8 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu address _signer, address _donationBox, address _baseToken, - uint32 _coreIndex, - bool _canBeUsedForAccountActivation, - uint64 _accountActivationFeeCore, - uint64 _bridgeSafetyBufferCore, address _multicallHandler - ) - HyperCoreFlowExecutor( - _donationBox, - _baseToken, - _coreIndex, - _canBeUsedForAccountActivation, - _accountActivationFeeCore, - _bridgeSafetyBufferCore - ) - ArbitraryActionFlowExecutor(_multicallHandler) - { + ) HyperCoreFlowExecutor(_donationBox, _baseToken) ArbitraryActionFlowExecutor(_multicallHandler) { cctpMessageTransmitter = IMessageTransmitterV2(_cctpMessageTransmitter); signer = _signer; } diff --git a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol index cf7046046..77790cbd9 100644 --- a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol +++ b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol @@ -64,22 +64,8 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryAc address _ioft, address _donationBox, address _baseToken, - uint32 _coreIndex, - bool _canBeUsedForAccountActivation, - uint64 _accountActivationFeeCore, - uint64 _bridgeSafetyBufferCore, address _multicallHandler - ) - HyperCoreFlowExecutor( - _donationBox, - _baseToken, - _coreIndex, - _canBeUsedForAccountActivation, - _accountActivationFeeCore, - _bridgeSafetyBufferCore - ) - ArbitraryActionFlowExecutor(_multicallHandler) - { + ) HyperCoreFlowExecutor(_donationBox, _baseToken) ArbitraryActionFlowExecutor(_multicallHandler) { // baseToken is assigned on `HyperCoreFlowExecutor` creation if (baseToken != IOFT(_ioft).token()) { revert TokenIOFTMismatch(); diff --git a/package.json b/package.json index f896f8b68..63b681ea7 100644 --- a/package.json +++ b/package.json @@ -152,6 +152,5 @@ "**/eth-crypto/secp256k1": "5.0.1", "**/create-hash/sha.js": "2.4.12", "sha.js": "2.4.12" - }, - "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" + } } diff --git a/script/114DeploySponsoredCCTPDstPeriphery.sol b/script/114DeploySponsoredCCTPDstPeriphery.sol index d694337a8..25a46b695 100644 --- a/script/114DeploySponsoredCCTPDstPeriphery.sol +++ b/script/114DeploySponsoredCCTPDstPeriphery.sol @@ -41,10 +41,6 @@ contract DeploySponsoredCCTPDstPeriphery is Script, Test, DeploymentUtils { deployer, address(donationBox), baseToken, - coreIndex, - canBeUsedForAccountActivation, - accountActivationFeeCore, - bridgeSafetyBufferCore, address(0) ); @@ -54,16 +50,6 @@ contract DeploySponsoredCCTPDstPeriphery is Script, Test, DeploymentUtils { console.log("DonationBox ownership transferred to:", address(sponsoredCCTPDstPeriphery)); - sponsoredCCTPDstPeriphery.setCoreTokenInfo( - baseToken, - coreIndex, - canBeUsedForAccountActivation, - accountActivationFeeCore, - bridgeSafetyBufferCore - ); - - console.log("CoreTokenInfo set"); - vm.stopBroadcast(); } } diff --git a/script/116DeployDstOFTHandler.sol b/script/116DeployDstOFTHandler.sol index adb569002..7c87ce7cf 100644 --- a/script/116DeployDstOFTHandler.sol +++ b/script/116DeployDstOFTHandler.sol @@ -37,10 +37,6 @@ contract DeployDstOFTHandler is Script, Test, DeploymentUtils { ioft, address(donationBox), baseToken, - coreIndex, - canBeUsedForAccountActivation, - accountActivationFeeCore, - bridgeSafetyBufferCore, multicallHandler ); console.log("DstOFTHandler deployed to:", address(dstOFTHandler)); @@ -49,16 +45,6 @@ contract DeployDstOFTHandler is Script, Test, DeploymentUtils { console.log("DonationBox ownership transferred to:", address(dstOFTHandler)); - dstOFTHandler.setCoreTokenInfo( - baseToken, - coreIndex, - canBeUsedForAccountActivation, - accountActivationFeeCore, - bridgeSafetyBufferCore - ); - - console.log("CoreTokenInfo set"); - vm.stopBroadcast(); } } From 7b05bb1cc6e300b46b01d0f71cc96a5d5654ae91 Mon Sep 17 00:00:00 2001 From: Ihor Farion <65650773+grasphoper@users.noreply.github.com> Date: Tue, 21 Oct 2025 23:34:03 -0700 Subject: [PATCH 21/47] move LayerZero libs to `external/` (#1151) Signed-off-by: Ihor Farion --- contracts/{ => external}/libraries/MinimalLZOptions.sol | 6 ++++-- contracts/{ => external}/libraries/OFTComposeMsgCodec.sol | 2 +- .../periphery/mintburn/sponsored-oft/DstOFTHandler.sol | 4 +--- .../mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol | 4 ++-- script/mintburn/CreateSponsoredDeposit.sol | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) rename contracts/{ => external}/libraries/MinimalLZOptions.sol (96%) rename contracts/{ => external}/libraries/OFTComposeMsgCodec.sol (98%) diff --git a/contracts/libraries/MinimalLZOptions.sol b/contracts/external/libraries/MinimalLZOptions.sol similarity index 96% rename from contracts/libraries/MinimalLZOptions.sol rename to contracts/external/libraries/MinimalLZOptions.sol index 6314a6b0d..ecf495ea9 100644 --- a/contracts/libraries/MinimalLZOptions.sol +++ b/contracts/external/libraries/MinimalLZOptions.sol @@ -1,13 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; -import { BytesLib } from "./BytesLib.sol"; -import "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import { BytesLib } from "../../libraries/BytesLib.sol"; +import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; /** * @title MinimalExecutorOptions * @notice This library is used to provide minimal required functionality of * https://github.com/LayerZero-Labs/LayerZero-v2/blob/2ff4988f85b5c94032eb71bbc4073e69c078179d/packages/layerzero-v2/evm/messagelib/contracts/libs/ExecutorOptions.sol#L7 + * Code was copied, was not modified */ library MinimalExecutorOptions { uint8 internal constant WORKER_ID = 1; @@ -28,6 +29,7 @@ library MinimalExecutorOptions { * @title MinimalLZOptions * @notice This library is used to provide minimal functionality of * https://github.com/LayerZero-Labs/devtools/blob/52ad590ab249f660f803ae3aafcbf7115733359c/packages/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol + * Code was copied, was not modified */ library MinimalLZOptions { // @dev Only used in `onlyType3` modifier diff --git a/contracts/libraries/OFTComposeMsgCodec.sol b/contracts/external/libraries/OFTComposeMsgCodec.sol similarity index 98% rename from contracts/libraries/OFTComposeMsgCodec.sol rename to contracts/external/libraries/OFTComposeMsgCodec.sol index a2b4bae6c..1ed1b2a46 100644 --- a/contracts/libraries/OFTComposeMsgCodec.sol +++ b/contracts/external/libraries/OFTComposeMsgCodec.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.20; * @title OFTComposeMsgCodec * @notice Copied from LZ implementation here: * https://github.com/LayerZero-Labs/devtools/blob/608915a7e260d995ce28e41c4e4877db9b18613b/packages/oft-evm/contracts/libs/OFTComposeMsgCodec.sol#L5 - * + * Code was copied, was not modified */ library OFTComposeMsgCodec { // Offset constants for decoding composed messages diff --git a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol index 77790cbd9..6076fd33a 100644 --- a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol +++ b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol @@ -2,9 +2,8 @@ pragma solidity ^0.8.23; import { ILayerZeroComposer } from "../../../external/interfaces/ILayerZeroComposer.sol"; -import { OFTComposeMsgCodec } from "../../../libraries/OFTComposeMsgCodec.sol"; +import { OFTComposeMsgCodec } from "../../../external/libraries/OFTComposeMsgCodec.sol"; import { DonationBox } from "../../../chain-adapters/DonationBox.sol"; -import { HyperCoreLib } from "../../../libraries/HyperCoreLib.sol"; import { ComposeMsgCodec } from "./ComposeMsgCodec.sol"; import { ExecutionMode } from "./Structs.sol"; import { AddressToBytes32, Bytes32ToAddress } from "../../../libraries/AddressConverters.sol"; @@ -12,7 +11,6 @@ import { IOFT, IOAppCore } from "../../../interfaces/IOFT.sol"; import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; import { ArbitraryActionFlowExecutor } from "../ArbitraryActionFlowExecutor.sol"; -import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; diff --git a/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol index 860c49b8e..27fa2729f 100644 --- a/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol @@ -5,9 +5,9 @@ import { Quote } from "./Structs.sol"; import { QuoteSignLib } from "./QuoteSignLib.sol"; import { ComposeMsgCodec } from "./ComposeMsgCodec.sol"; -import { IOFT, IOAppCore, IEndpoint, SendParam, MessagingFee } from "../../../interfaces/IOFT.sol"; +import { IOFT, IOAppCore, SendParam, MessagingFee } from "../../../interfaces/IOFT.sol"; import { AddressToBytes32 } from "../../../libraries/AddressConverters.sol"; -import { MinimalLZOptions } from "../../../libraries/MinimalLZOptions.sol"; +import { MinimalLZOptions } from "../../../external/libraries/MinimalLZOptions.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/script/mintburn/CreateSponsoredDeposit.sol b/script/mintburn/CreateSponsoredDeposit.sol index 1f18fd9c1..44783ea1b 100644 --- a/script/mintburn/CreateSponsoredDeposit.sol +++ b/script/mintburn/CreateSponsoredDeposit.sol @@ -6,7 +6,7 @@ import { SponsoredOFTSrcPeriphery } from "../../contracts/periphery/mintburn/spo import { Quote, SignedQuoteParams, UnsignedQuoteParams } from "../../contracts/periphery/mintburn/sponsored-oft/Structs.sol"; import { AddressToBytes32 } from "../../contracts/libraries/AddressConverters.sol"; import { ComposeMsgCodec } from "../../contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol"; -import { MinimalLZOptions } from "../../contracts/libraries/MinimalLZOptions.sol"; +import { MinimalLZOptions } from "../../contracts/external/libraries/MinimalLZOptions.sol"; import { IOFT, SendParam, MessagingFee } from "../../contracts/interfaces/IOFT.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; From 4e8e1a20fd1553a3f0936f65c19fdf6a64ad2dee Mon Sep 17 00:00:00 2001 From: Taylor Webb <84364476+tbwebb22@users.noreply.github.com> Date: Wed, 22 Oct 2025 00:41:06 -0600 Subject: [PATCH 22/47] feat: add swapsProcessed return values to finalizePendingsSwaps (#1145) * add swapsProcessed return values to finalizePendingsSwaps Signed-off-by: Taylor Webb * return finalized swap amount & number of swaps remaining Signed-off-by: Taylor Webb * update var name process to finalize Signed-off-by: Taylor Webb --------- Signed-off-by: Taylor Webb --- .../mintburn/HyperCoreFlowExecutor.sol | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index cdfbd00db..23c543e09 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -680,7 +680,10 @@ contract HyperCoreFlowExecutor is AccessControl { } /// @notice Finalizes pending queue of swaps for `finalToken` if a corresponding SwapHandler has enough balance - function finalizePendingSwaps(address finalToken, uint256 maxToProcess) external { + function finalizePendingSwaps( + address finalToken, + uint256 maxSwapCountToFinalize + ) external returns (uint256 finalizedSwapsCount, uint256 finalizedSwapsAmount, uint256 totalPendingSwapsRemaining) { FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); CoreTokenInfo memory coreTokenInfo = coreTokenInfos[finalToken]; @@ -688,13 +691,13 @@ contract HyperCoreFlowExecutor is AccessControl { uint256 head = pendingQueueHead[finalToken]; bytes32[] storage queue = pendingQueue[finalToken]; - if (head >= queue.length || maxToProcess == 0) return; + if (head >= queue.length) return (0, 0, 0); + if (maxSwapCountToFinalize == 0) return (0, 0, queue.length - head); // Note: `availableCore` is the SwapHandler's Core balance for `finalToken`, which monotonically increases uint64 availableCore = HyperCoreLib.spotBalance(address(finalTokenInfo.swapHandler), coreTokenInfo.coreIndex); - uint256 processed = 0; - while (head < queue.length && processed < maxToProcess) { + while (head < queue.length && finalizedSwapsCount < maxSwapCountToFinalize) { bytes32 nonce = queue[head]; PendingSwap storage pendingSwap = pendingSwaps[nonce]; @@ -723,14 +726,17 @@ contract HyperCoreFlowExecutor is AccessControl { // We don't delete `pendingSwaps` state, because we might require it for accounting purposes if we need to // update the associated limit order head += 1; - processed += 1; + finalizedSwapsCount += 1; + finalizedSwapsAmount += totalAmountToForwardToUser; } pendingQueueHead[finalToken] = head; - if (processed > 0) { + if (finalizedSwapsCount > 0) { lastPullFundsBlock[finalToken] = block.number; } + + return (finalizedSwapsCount, finalizedSwapsAmount, queue.length - head); } function activateUserAccount( From e5da74908cce13ecef3013e37040f60b4ec9acf2 Mon Sep 17 00:00:00 2001 From: Ihor Farion <65650773+grasphoper@users.noreply.github.com> Date: Tue, 21 Oct 2025 23:42:58 -0700 Subject: [PATCH 23/47] fix: arbitrary actions flow (#1149) * fix Signed-off-by: Ihor Farion * a few renames Signed-off-by: Ihor Farion * remove dedundant maxBpsToSponsor enforcement Signed-off-by: Ihor Farion * save 1 stack depth. Signed-off-by: Ihor Farion * add _calcFinalExtraFees Signed-off-by: Ihor Farion * comment Signed-off-by: Ihor Farion --------- Signed-off-by: Ihor Farion --- ...cutor.sol => ArbitraryEVMFlowExecutor.sol} | 129 +++++------------- .../SponsoredCCTPDstPeriphery.sol | 63 ++++----- .../mintburn/sponsored-oft/DstOFTHandler.sol | 96 ++++++------- 3 files changed, 102 insertions(+), 186 deletions(-) rename contracts/periphery/mintburn/{ArbitraryActionFlowExecutor.sol => ArbitraryEVMFlowExecutor.sol} (60%) diff --git a/contracts/periphery/mintburn/ArbitraryActionFlowExecutor.sol b/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol similarity index 60% rename from contracts/periphery/mintburn/ArbitraryActionFlowExecutor.sol rename to contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol index 7af2a49d9..a4fd69dad 100644 --- a/contracts/periphery/mintburn/ArbitraryActionFlowExecutor.sol +++ b/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol @@ -8,17 +8,14 @@ import { DonationBox } from "../../chain-adapters/DonationBox.sol"; // Import MulticallHandler import { MulticallHandler } from "../../handlers/MulticallHandler.sol"; -// Import constants -import { BPS_SCALAR } from "./Constants.sol"; - /** - * @title ArbitraryActionFlowExecutor + * @title ArbitraryEVMFlowExecutor * @notice Base contract for executing arbitrary action sequences using MulticallHandler * @dev This contract provides shared functionality for both OFT and CCTP handlers to execute - * arbitrary actions on HyperEVM via MulticallHandler, with optional transfer to HyperCore. + * arbitrary actions on HyperEVM via MulticallHandler, returning information about the resulting token amount * @custom:security-contact bugs@across.to */ -abstract contract ArbitraryActionFlowExecutor { +abstract contract ArbitraryEVMFlowExecutor { using SafeERC20 for IERC20; /// @notice Compressed call struct (no value field to save gas) @@ -36,9 +33,9 @@ abstract contract ArbitraryActionFlowExecutor { /// @notice Error thrown when final balance is insufficient error InsufficientFinalBalance(address token, uint256 expected, uint256 actual); - uint256 constant BPS_TOTAL_PRECISION = 18; - uint256 constant BPS_DECIMALS = 4; - uint256 constant BPS_PRECISION_SCALAR = 10 ** BPS_TOTAL_PRECISION; + uint256 private constant BPS_TOTAL_PRECISION = 18; + uint256 private constant BPS_DECIMALS = 4; + uint256 private constant BPS_PRECISION_SCALAR = 10 ** BPS_TOTAL_PRECISION; constructor(address _multicallHandler) { multicallHandler = _multicallHandler; @@ -49,36 +46,22 @@ abstract contract ArbitraryActionFlowExecutor { * @dev Decompresses CompressedCall[] to MulticallHandler.Call[] format (adds value: 0) * @param amount Amount of tokens to transfer to MulticallHandler * @param quoteNonce Unique nonce for this quote - * @param maxBpsToSponsor Maximum basis points to sponsor * @param initialToken Token to transfer to MulticallHandler - * @param finalRecipient Final recipient address * @param finalToken Expected final token after actions * @param actionData Encoded actions: abi.encode(CompressedCall[] calls) - * @param transferToCore Whether to transfer result to HyperCore - * @param extraFeesToSponsor Extra fees to sponsor + * @param extraFeesToSponsorTokenIn Extra fees to sponsor in initialToken */ - function _executeArbitraryActionFlow( + function _executeFlow( uint256 amount, bytes32 quoteNonce, - uint256 maxBpsToSponsor, address initialToken, - address finalRecipient, address finalToken, bytes memory actionData, - bool transferToCore, - uint256 extraFeesToSponsor - ) internal { + uint256 extraFeesToSponsorTokenIn + ) internal returns (address /* finalToken */, uint256 finalAmount, uint256 extraFeesToSponsorFinalToken) { // Decode the compressed action data CompressedCall[] memory compressedCalls = abi.decode(actionData, (CompressedCall[])); - // Total amount to sponsor is the extra fees to sponsor, ceiling division. - uint256 totalAmount = amount + extraFeesToSponsor; - uint256 bpsToSponsor = ((extraFeesToSponsor * BPS_PRECISION_SCALAR) + totalAmount - 1) / totalAmount; - uint256 maxBpsToSponsorAdjusted = maxBpsToSponsor * (10 ** (BPS_TOTAL_PRECISION - BPS_DECIMALS)); - if (bpsToSponsor > maxBpsToSponsorAdjusted) { - bpsToSponsor = maxBpsToSponsorAdjusted; - } - // Snapshot balances uint256 initialAmountSnapshot = IERC20(initialToken).balanceOf(address(this)); uint256 finalAmountSnapshot = IERC20(finalToken).balanceOf(address(this)); @@ -86,10 +69,6 @@ abstract contract ArbitraryActionFlowExecutor { // Transfer tokens to MulticallHandler IERC20(initialToken).safeTransfer(multicallHandler, amount); - // Decompress calls: add value: 0 to each call and wrap in Instructions - // We encode Instructions with calls and a drainLeftoverTokens call at the end - uint256 callCount = compressedCalls.length; - // Build instructions for MulticallHandler bytes memory instructions = _buildMulticallInstructions( compressedCalls, @@ -105,8 +84,6 @@ abstract contract ArbitraryActionFlowExecutor { instructions ); - uint256 finalAmount; - // This means the swap (if one was intended) didn't happen (action failed), so we use the initial token as the final token. if (initialAmountSnapshot == IERC20(initialToken).balanceOf(address(this))) { finalToken = initialToken; @@ -122,41 +99,11 @@ abstract contract ArbitraryActionFlowExecutor { } } - // Apply the bps to sponsor to the final amount to get the amount to sponsor, ceiling division. - uint256 bpsToSponsorAdjusted = BPS_PRECISION_SCALAR - bpsToSponsor; - uint256 amountToSponsor = (((finalAmount * BPS_PRECISION_SCALAR) + bpsToSponsorAdjusted - 1) / - bpsToSponsorAdjusted) - finalAmount; - if (amountToSponsor > 0) { - DonationBox donationBox = _getDonationBox(); - if (IERC20(finalToken).balanceOf(address(donationBox)) < amountToSponsor) { - amountToSponsor = 0; - } else { - donationBox.withdraw(IERC20(finalToken), amountToSponsor); - } - } + extraFeesToSponsorFinalToken = _calcExtraFeesFinal(amount, extraFeesToSponsorTokenIn, finalAmount); - emit ArbitraryActionsExecuted(quoteNonce, callCount, finalAmount); - - // Route to appropriate destination based on transferToCore flag - if (transferToCore) { - _executeSimpleTransferFlow( - finalAmount, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - amountToSponsor, - finalToken - ); - } else { - _fallbackHyperEVMFlow( - finalAmount, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - amountToSponsor, - finalToken - ); - } + emit ArbitraryActionsExecuted(quoteNonce, compressedCalls.length, finalAmount); + + return (finalToken, finalAmount, extraFeesToSponsorFinalToken); } /** @@ -202,37 +149,25 @@ abstract contract ArbitraryActionFlowExecutor { return abi.encode(instructions); } - /** - * @notice Execute simple transfer flow to HyperCore with the final token - * @dev Must be implemented by contracts that inherit from this contract - */ - function _executeSimpleTransferFlow( - uint256 finalAmount, - bytes32 quoteNonce, - uint256 maxBpsToSponsor, - address finalRecipient, - uint256 extraFeesToSponsor, - address finalToken - ) internal virtual; - - /** - * @notice Execute fallback HyperEVM flow (stay on HyperEVM) - * @dev Must be implemented by contracts that inherit from this contract - */ - function _fallbackHyperEVMFlow( - uint256 finalAmount, - bytes32 quoteNonce, - uint256 maxBpsToSponsor, - address finalRecipient, - uint256 extraFeesToSponsor, - address finalToken - ) internal virtual; + /// @notice Calcualtes proportional fees to sponsor in finalToken, given the fees to sponsor in initial token and initial amount + function _calcExtraFeesFinal( + uint256 amount, + uint256 extraFeesToSponsorTokenIn, + uint256 finalAmount + ) internal pure returns (uint256 extraFeesToSponsorFinalToken) { + // Total amount to sponsor is the extra fees to sponsor, ceiling division. + uint256 bpsToSponsor; + { + uint256 totalAmount = amount + extraFeesToSponsorTokenIn; + bpsToSponsor = ((extraFeesToSponsorTokenIn * BPS_PRECISION_SCALAR) + totalAmount - 1) / totalAmount; + } - /** - * @notice Get the donation box instance - * @dev Must be implemented by contracts that inherit from this contract - */ - function _getDonationBox() internal view virtual returns (DonationBox); + // Apply the bps to sponsor to the final amount to get the amount to sponsor, ceiling division. + uint256 bpsToSponsorAdjusted = BPS_PRECISION_SCALAR - bpsToSponsor; + extraFeesToSponsorFinalToken = + (((finalAmount * BPS_PRECISION_SCALAR) + bpsToSponsorAdjusted - 1) / bpsToSponsorAdjusted) - + finalAmount; + } /// @notice Allow contract to receive native tokens for arbitrary action execution receive() external payable virtual {} diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol index c56c14ea6..add6d243f 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol @@ -9,9 +9,9 @@ import { SponsoredCCTPInterface } from "../../../interfaces/SponsoredCCTPInterfa import { Bytes32ToAddress } from "../../../libraries/AddressConverters.sol"; import { DonationBox } from "../../../chain-adapters/DonationBox.sol"; import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; -import { ArbitraryActionFlowExecutor } from "../ArbitraryActionFlowExecutor.sol"; +import { ArbitraryEVMFlowExecutor } from "../ArbitraryEVMFlowExecutor.sol"; -contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecutor, ArbitraryActionFlowExecutor { +contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecutor, ArbitraryEVMFlowExecutor { using SafeERC20 for IERC20Metadata; using Bytes32ToAddress for bytes32; @@ -33,7 +33,7 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu address _donationBox, address _baseToken, address _multicallHandler - ) HyperCoreFlowExecutor(_donationBox, _baseToken) ArbitraryActionFlowExecutor(_multicallHandler) { + ) HyperCoreFlowExecutor(_donationBox, _baseToken) ArbitraryEVMFlowExecutor(_multicallHandler) { cctpMessageTransmitter = IMessageTransmitterV2(_cctpMessageTransmitter); signer = _signer; } @@ -71,21 +71,21 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu (quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) || quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToEVM)) ) { - // Execute arbitrary actions flow - _executeArbitraryActionFlow( + // Execute flow with arbitrary evm actions + _executeWithEVMFlow( amountAfterFees, quote.nonce, quote.maxBpsToSponsor, baseToken, // initialToken - quote.finalRecipient.toAddress(), quote.finalToken.toAddress(), + quote.finalRecipient.toAddress(), quote.actionData, - quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToCore), - feeExecuted + feeExecuted, + quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) ); } else { // Execute standard HyperCore flow (default) - _executeFlow( + HyperCoreFlowExecutor._executeFlow( amountAfterFees, quote.nonce, // If the quote is invalid we don't sponsor the flow or the extra fees @@ -119,45 +119,36 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu quote.deadline + quoteDeadlineBuffer >= block.timestamp; } - /// @notice Override to resolve diamond inheritance - use HyperCoreFlowExecutor implementation - function _executeSimpleTransferFlow( - uint256 finalAmount, + function _executeWithEVMFlow( + uint256 amount, bytes32 quoteNonce, uint256 maxBpsToSponsor, + address initialToken, + address finalToken, address finalRecipient, + bytes memory actionData, uint256 extraFeesToSponsor, - address finalToken - ) internal override(ArbitraryActionFlowExecutor, HyperCoreFlowExecutor) { - HyperCoreFlowExecutor._executeSimpleTransferFlow( - finalAmount, + bool transferToCore + ) internal { + uint256 finalAmount; + uint256 extraFeesToSponsorFinalToken; + (finalToken, finalAmount, extraFeesToSponsorFinalToken) = ArbitraryEVMFlowExecutor._executeFlow( + amount, quoteNonce, - maxBpsToSponsor, - finalRecipient, - extraFeesToSponsor, - finalToken + initialToken, + finalToken, + actionData, + extraFeesToSponsor ); - } - /// @notice Override to resolve diamond inheritance - use HyperCoreFlowExecutor implementation - function _fallbackHyperEVMFlow( - uint256 finalAmount, - bytes32 quoteNonce, - uint256 maxBpsToSponsor, - address finalRecipient, - uint256 extraFeesToSponsor, - address finalToken - ) internal override(ArbitraryActionFlowExecutor, HyperCoreFlowExecutor) { - HyperCoreFlowExecutor._fallbackHyperEVMFlow( + // Route to appropriate destination based on transferToCore flag + (transferToCore ? _executeSimpleTransferFlow : _fallbackHyperEVMFlow)( finalAmount, quoteNonce, maxBpsToSponsor, finalRecipient, - extraFeesToSponsor, + extraFeesToSponsorFinalToken, finalToken ); } - - function _getDonationBox() internal view override(ArbitraryActionFlowExecutor) returns (DonationBox) { - return donationBox; - } } diff --git a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol index 6076fd33a..1d5d9ca4c 100644 --- a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol +++ b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol @@ -3,20 +3,19 @@ pragma solidity ^0.8.23; import { ILayerZeroComposer } from "../../../external/interfaces/ILayerZeroComposer.sol"; import { OFTComposeMsgCodec } from "../../../external/libraries/OFTComposeMsgCodec.sol"; -import { DonationBox } from "../../../chain-adapters/DonationBox.sol"; import { ComposeMsgCodec } from "./ComposeMsgCodec.sol"; import { ExecutionMode } from "./Structs.sol"; import { AddressToBytes32, Bytes32ToAddress } from "../../../libraries/AddressConverters.sol"; import { IOFT, IOAppCore } from "../../../interfaces/IOFT.sol"; import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; -import { ArbitraryActionFlowExecutor } from "../ArbitraryActionFlowExecutor.sol"; +import { ArbitraryEVMFlowExecutor } from "../ArbitraryEVMFlowExecutor.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /// @notice Handler that receives funds from LZ system, checks authorizations(both against LZ system and src chain /// sender), and forwards authorized params to the `_executeFlow` function -contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryActionFlowExecutor { +contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEVMFlowExecutor { using ComposeMsgCodec for bytes; using Bytes32ToAddress for bytes32; using AddressToBytes32 for address; @@ -35,7 +34,7 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryAc /// @notice A mapping used for nonce uniqueness checks. Our src periphery and LZ should have prevented this already, /// but I guess better safe than sorry - mapping(bytes32 quoteNonce => bool used) usedNonces; + mapping(bytes32 quoteNonce => bool used) public usedNonces; /// @notice Emitted when a new authorized src periphery is configured event SetAuthorizedPeriphery(uint32 srcEid, bytes32 srcPeriphery); @@ -63,7 +62,7 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryAc address _donationBox, address _baseToken, address _multicallHandler - ) HyperCoreFlowExecutor(_donationBox, _baseToken) ArbitraryActionFlowExecutor(_multicallHandler) { + ) HyperCoreFlowExecutor(_donationBox, _baseToken) ArbitraryEVMFlowExecutor(_multicallHandler) { // baseToken is assigned on `HyperCoreFlowExecutor` creation if (baseToken != IOFT(_ioft).token()) { revert TokenIOFTMismatch(); @@ -125,21 +124,21 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryAc executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) || executionMode == uint8(ExecutionMode.ArbitraryActionsToEVM) ) { - // Execute arbitrary actions flow - _executeArbitraryActionFlow( + // Execute flow with arbitrary evm actions + _executeWithEVMFlow( amountLD, quoteNonce, maxBpsToSponsor, baseToken, // initialToken - finalRecipient, finalToken, + finalRecipient, actionData, - executionMode == uint8(ExecutionMode.ArbitraryActionsToCore), - EXTRA_FEES_TO_SPONSOR + EXTRA_FEES_TO_SPONSOR, + executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) ); } else { // Execute standard HyperCore flow (default) - _executeFlow( + HyperCoreFlowExecutor._executeFlow( amountLD, quoteNonce, maxBpsToSponsor, @@ -151,6 +150,39 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryAc } } + function _executeWithEVMFlow( + uint256 amount, + bytes32 quoteNonce, + uint256 maxBpsToSponsor, + address initialToken, + address finalToken, + address finalRecipient, + bytes memory actionData, + uint256 extraFeesToSponsor, + bool transferToCore + ) internal { + uint256 finalAmount; + uint256 extraFeesToSponsorFinalToken; + (finalToken, finalAmount, extraFeesToSponsorFinalToken) = ArbitraryEVMFlowExecutor._executeFlow( + amount, + quoteNonce, + initialToken, + finalToken, + actionData, + extraFeesToSponsor + ); + + // Route to appropriate destination based on transferToCore flag + (transferToCore ? _executeSimpleTransferFlow : _fallbackHyperEVMFlow)( + finalAmount, + quoteNonce, + maxBpsToSponsor, + finalRecipient, + extraFeesToSponsorFinalToken, + finalToken + ); + } + /// @notice Checks that message was authorized by LayerZero's identity system and that it came from authorized src periphery function _requireAuthorizedMessage(address _oApp, bytes calldata _message) internal view { if (_oApp != IOFT_ADDRESS) { @@ -179,46 +211,4 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryAc revert UnauthorizedSrcPeriphery(_srcEid); } } - - /// @notice Override to resolve diamond inheritance - use HyperCoreFlowExecutor implementation - function _executeSimpleTransferFlow( - uint256 finalAmount, - bytes32 quoteNonce, - uint256 maxBpsToSponsor, - address finalRecipient, - uint256 extraFeesToSponsor, - address finalToken - ) internal override(ArbitraryActionFlowExecutor, HyperCoreFlowExecutor) { - HyperCoreFlowExecutor._executeSimpleTransferFlow( - finalAmount, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - extraFeesToSponsor, - finalToken - ); - } - - /// @notice Override to resolve diamond inheritance - use HyperCoreFlowExecutor implementation - function _fallbackHyperEVMFlow( - uint256 finalAmount, - bytes32 quoteNonce, - uint256 maxBpsToSponsor, - address finalRecipient, - uint256 extraFeesToSponsor, - address finalToken - ) internal override(ArbitraryActionFlowExecutor, HyperCoreFlowExecutor) { - HyperCoreFlowExecutor._fallbackHyperEVMFlow( - finalAmount, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - extraFeesToSponsor, - finalToken - ); - } - - function _getDonationBox() internal view override returns (DonationBox) { - return donationBox; - } } From 00eda37fd8f50e0e1084371ea5026f08d393a2b1 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Wed, 22 Oct 2025 02:44:41 -0400 Subject: [PATCH 24/47] chore: Added NatSpec to CCTP contracts (#1144) Signed-off-by: Faisal Usmani Co-authored-by: Ihor Farion <65650773+grasphoper@users.noreply.github.com> --- contracts/libraries/SponsoredCCTPQuoteLib.sol | 42 ++++++++++++++++++- .../mintburn/HyperCoreFlowExecutor.sol | 36 ++++++++++++++++ .../SponsoredCCTPDstPeriphery.sol | 38 ++++++++++++++++- .../SponsoredCCTPSrcPeriphery.sol | 26 +++++++++++- 4 files changed, 136 insertions(+), 6 deletions(-) diff --git a/contracts/libraries/SponsoredCCTPQuoteLib.sol b/contracts/libraries/SponsoredCCTPQuoteLib.sol index c10a063ff..251060312 100644 --- a/contracts/libraries/SponsoredCCTPQuoteLib.sol +++ b/contracts/libraries/SponsoredCCTPQuoteLib.sol @@ -7,11 +7,16 @@ import { SponsoredCCTPInterface } from "../interfaces/SponsoredCCTPInterface.sol import { BytesLib } from "./BytesLib.sol"; import { Bytes32ToAddress } from "./AddressConverters.sol"; +/** + * @title SponsoredCCTPQuoteLib + * @notice Library that contains the functions to get the data from the quotes and validate the signatures. + */ library SponsoredCCTPQuoteLib { using BytesLib for bytes; using Bytes32ToAddress for bytes32; - // Indices of each field in message + /// @dev Indices of each field in message that we get from CCTP + /// Source: https://github.com/circlefin/evm-cctp-contracts/blob/4061786a5726bc05f99fcdb53b0985599f0dbaf7/src/messages/v2/MessageV2.sol#L52-L61 uint256 private constant VERSION_INDEX = 0; uint256 private constant SOURCE_DOMAIN_INDEX = 4; uint256 private constant DESTINATION_DOMAIN_INDEX = 8; @@ -23,7 +28,8 @@ library SponsoredCCTPQuoteLib { uint256 private constant FINALITY_THRESHOLD_EXECUTED_INDEX = 144; uint256 private constant MESSAGE_BODY_INDEX = 148; - // Field indices in message body + /// @dev Indices of each field in message body that is extracted from message + /// Source: https://github.com/circlefin/evm-cctp-contracts/blob/4061786a5726bc05f99fcdb53b0985599f0dbaf7/src/messages/v2/BurnMessageV2.sol#L48-L52 uint256 private constant BURN_TOKEN_INDEX = 4; uint256 private constant MINT_RECIPIENT_INDEX = 36; uint256 private constant AMOUNT_INDEX = 68; @@ -34,6 +40,18 @@ library SponsoredCCTPQuoteLib { // Minimum length of the message body (can be longer due to variable actionData) uint256 private constant MIN_MSG_BYTES_LENGTH = 568; + /** + * @notice Gets the data for the deposit for burn. + * @param quote The quote that contains the data for the deposit. + * @return amount The amount of tokens to deposit for burn. + * @return destinationDomain The destination domain ID for the chain that the tokens are being deposited to. + * @return mintRecipient The recipent of the minted tokens. This would be the destination periphery contract. + * @return burnToken The address of the token to burn. + * @return destinationCaller The address that will call the CCTP receiveMessage function. This would be the destination periphery contract. + * @return maxFee The maximum fee that can be paid for the deposit. + * @return minFinalityThreshold The minimum finality threshold for the deposit. + * @return hookData The hook data for the deposit. Contrains additional data to be used by the destination periphery contract. + */ function getDepositForBurnData( SponsoredCCTPInterface.SponsoredCCTPQuote memory quote ) @@ -69,6 +87,12 @@ library SponsoredCCTPQuoteLib { ); } + /** + * @notice Validates the message that is received from CCTP. If this checks fails, then the quote on source chain was invalid + * and we are unable to retrieve user's address to send the funds to. In that case the funds will stay in this contract. + * @param message The message that is received from CCTP. + * @return isValid True if the message is valid, false otherwise. + */ function validateMessage(bytes memory message) internal view returns (bool) { // Message must be at least the minimum length (can be longer due to variable actionData) if (message.length < MIN_MSG_BYTES_LENGTH) { @@ -93,6 +117,12 @@ library SponsoredCCTPQuoteLib { return finalRecipient.isValidAddress() && finalToken.isValidAddress(); } + /** + * @notice Returns the quote and the fee that was executed from the CCTP message. + * @param message The message that is received from CCTP. + * @return quote The quote that contains the data of the deposit. + * @return feeExecuted The fee that was executed for the deposit. This is the fee that was paid to the CCTP message transmitter. + */ function getSponsoredCCTPQuoteData( bytes memory message ) internal pure returns (SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, uint256 feeExecuted) { @@ -101,6 +131,7 @@ library SponsoredCCTPQuoteLib { quote.destinationCaller = message.toBytes32(DESTINATION_CALLER_INDEX); quote.minFinalityThreshold = message.toUint32(MIN_FINALITY_THRESHOLD_INDEX); + // first need to extract the message body from the message bytes memory messageBody = message.slice(MESSAGE_BODY_INDEX, message.length); quote.mintRecipient = messageBody.toBytes32(MINT_RECIPIENT_INDEX); quote.amount = messageBody.toUint256(AMOUNT_INDEX); @@ -121,6 +152,13 @@ library SponsoredCCTPQuoteLib { ) = abi.decode(hookData, (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes)); } + /** + * @notice Validates the signature against the quote. + * @param signer The signer address that was used to sign the quote. + * @param quote The quote that contains the data of the deposit. + * @param signature The signature of the quote. + * @return isValid True if the signature is valid, false otherwise. + */ function validateSignature( address signer, SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 23c543e09..9eecf302c 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -739,6 +739,12 @@ contract HyperCoreFlowExecutor is AccessControl { return (finalizedSwapsCount, finalizedSwapsAmount, queue.length - head); } + /** + * @notice Activates a user account on Core by funding the account activation fee. + * @param quoteNonce The nonce of the quote that is used to identify the user. + * @param finalRecipient The address of the recipient of the funds. + * @param fundingToken The address of the token that is used to fund the account activation fee. + */ function activateUserAccount( bytes32 quoteNonce, address finalRecipient, @@ -1139,6 +1145,21 @@ contract HyperCoreFlowExecutor is AccessControl { } } + /** + * @notice Given the quote budget and the price, this function calculates the size of the buy limit order to set + * as well as the minimum amount of out token to expect. This calculation is based on the HIP-1 spot trading formula. + * Source: https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-1-native-token-standard#spot-trading + * @param quoteBudget The budget of the quote in base token. + * @param pxX1e8 The price of the quote token in base token. + * @param quoteD The decimals of the quote token. + * @param quoteSz The size decimals of the quote token. + * @param baseD The decimals of the base token. + * @param baseSz The size decimals of the base token. + * @param feePpm The fee in ppm that is applied to the quote. + * @return szX1e8 The size of the limit order to set. + * @return tokensToSendCore The number of tokens to send for this trade to suceed. + * @return minAmountOutCore The minimum amount of out token to expect. + */ function _calcLOAmountsBuy( uint64 quoteBudget, uint64 pxX1e8, @@ -1158,6 +1179,21 @@ contract HyperCoreFlowExecutor is AccessControl { minAmountOutCore = outBaseNet; } + /** + * @notice Given the quote budget and the price, this function calculates the size of the sell limit order to set + * as well as the minimum amount of out token to expect. This calculation is based on the HIP-1 spot trading formula. + * Source: https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-1-native-token-standard#spot-trading + * @param baseBudget The budget of the quote in base token. + * @param pxX1e8 The price of the quote token in base token. + * @param quoteD The decimals of the quote token. + * @param quoteSz The size decimals of the quote token. + * @param baseD The decimals of the base token. + * @param baseSz The size decimals of the base token. + * @param feePpm The fee in ppm that is applied to the quote. + * @return szX1e8 The size of the limit order to set. + * @return tokensToSendCore The number of tokens to send for this trade to suceed. + * @return minAmountOutCore The minimum amount of out token to expect. + */ function _calcLOAmountsSell( uint64 baseBudget, uint64 pxX1e8, diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol index add6d243f..ec05d1a8c 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol @@ -11,6 +11,11 @@ import { DonationBox } from "../../../chain-adapters/DonationBox.sol"; import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; import { ArbitraryEVMFlowExecutor } from "../ArbitraryEVMFlowExecutor.sol"; +/** + * @title SponsoredCCTPDstPeriphery + * @notice Destination chain periphery contract that supports sponsored/non-sponsored CCTP deposits. + * @dev This contract is used to receive tokens via CCTP and execute the flow accordingly. + */ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecutor, ArbitraryEVMFlowExecutor { using SafeERC20 for IERC20Metadata; using Bytes32ToAddress for bytes32; @@ -27,6 +32,18 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu /// @notice A mapping of used nonces to prevent replay attacks. mapping(bytes32 => bool) public usedNonces; + /** + * @notice Constructor for the SponsoredCCTPDstPeriphery contract. + * @param _cctpMessageTransmitter The address of the CCTP message transmitter contract. + * @param _signer The address of the signer that was used to sign the quotes. + * @param _donationBox The address of the donation box contract. This is used to store funds that are used for sponsored flows. + * @param _baseToken The address of the base token which would be the USDC on HyperEVM. + * @param _coreIndex The index of the base token on HyperCore. + * @param _canBeUsedForAccountActivation Whether the token can be used for account activation. + * @param _accountActivationFeeCore If the token can be used for account activation, this is the fee that is needed for account activation. + * @param _bridgeSafetyBufferCore This buffers is used to check if the bridging to Core is safe. + * @param _multicallHandler The address of the multicall handler contract. + */ constructor( address _cctpMessageTransmitter, address _signer, @@ -38,26 +55,43 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu signer = _signer; } + /** + * @notice Sets the signer address that is used to validate the signatures of the quotes. + * @param _signer The new signer address. + */ function setSigner(address _signer) external onlyDefaultAdmin { signer = _signer; } + /** + * @notice Sets the quote deadline buffer. This is used to prevent the quote from being used after it has expired. + * @param _quoteDeadlineBuffer The new quote deadline buffer. + */ function setQuoteDeadlineBuffer(uint256 _quoteDeadlineBuffer) external onlyDefaultAdmin { quoteDeadlineBuffer = _quoteDeadlineBuffer; } + /** + * @notice Receives a message from CCTP and executes the flow accordingly. This function first calls the + * CCTP message transmitter to receive the funds before validating the quote and executing the flow. + * @param message The message that is received from CCTP. + * @param attestation The attestation that is received from CCTP. + * @param signature The signature of the quote. + */ function receiveMessage(bytes memory message, bytes memory attestation, bytes memory signature) external { cctpMessageTransmitter.receiveMessage(message, attestation); - // If the hook data is invalid or the mint recipient is not this contract we cannot process the message and therefore we return. - // In this case the funds will be kept in this contract + // If the hook data is invalid or the mint recipient is not this contract we cannot process the message + // and therefore we exit. In this case the funds will be kept in this contract. if (!SponsoredCCTPQuoteLib.validateMessage(message)) { return; } + // Extract the quote and the fee that was executed from the message. (SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, uint256 feeExecuted) = SponsoredCCTPQuoteLib .getSponsoredCCTPQuoteData(message); + // Validate the quote and the signature. bool isQuoteValid = _isQuoteValid(quote, signature); if (isQuoteValid) { usedNonces[quote.nonce] = true; diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol index af18f3da7..d7a70ac76 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol @@ -9,25 +9,43 @@ import { ITokenMessengerV2 } from "../../../external/interfaces/CCTPInterfaces.s import { SponsoredCCTPQuoteLib } from "../../../libraries/SponsoredCCTPQuoteLib.sol"; import { SponsoredCCTPInterface } from "../../../interfaces/SponsoredCCTPInterface.sol"; +/** + * @title SponsoredCCTPSrcPeriphery + * @notice Source chain periphery contract that supports sponsored/non-sponsored CCTP deposits. + * @dev This contract is used to deposit tokens for burn via CCTP. + */ contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { using SafeERC20 for IERC20; + /// @notice The CCTP token messenger contract. ITokenMessengerV2 public immutable cctpTokenMessenger; + /// @notice The source domain ID for the chain that this contract is deployed on. uint32 public immutable sourceDomain; + /// @notice The signer address that is used to validate the signatures of the quotes. address public signer; + /// @notice A mapping of used nonces to prevent replay attacks. mapping(bytes32 => bool) public usedNonces; + /** + * @notice Constructor for the SponsoredCCTPSrcPeriphery contract. + * @param _cctpTokenMessenger The address of the CCTP token messenger contract. + * @param _sourceDomain The source domain ID for the chain that this contract is deployed on. + * @param _signer The signer address that is used to validate the signatures of the quotes. + */ constructor(address _cctpTokenMessenger, uint32 _sourceDomain, address _signer) { cctpTokenMessenger = ITokenMessengerV2(_cctpTokenMessenger); sourceDomain = _sourceDomain; signer = _signer; } - using SponsoredCCTPQuoteLib for bytes; - + /** + * @notice Deposits tokens for burn via CCTP. + * @param quote The quote that contains the data for the deposit. + * @param signature The signature of the quote. + */ function depositForBurn(SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, bytes memory signature) external { if (!SponsoredCCTPQuoteLib.validateSignature(signer, quote, signature)) revert InvalidSignature(); if (usedNonces[quote.nonce]) revert InvalidNonce(); @@ -73,6 +91,10 @@ contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { ); } + /** + * @notice Sets the signer address that is used to validate the signatures of the quotes. + * @param _signer The new signer address. + */ function setSigner(address _signer) external onlyOwner { signer = _signer; } From da9028288b09a5bffc46680e2216e065b08786fb Mon Sep 17 00:00:00 2001 From: Ihor Farion <65650773+grasphoper@users.noreply.github.com> Date: Tue, 21 Oct 2025 23:56:05 -0700 Subject: [PATCH 25/47] add reentrancy guards and follow the CEI pattern where possible (#1148) Signed-off-by: Ihor Farion --- .../mintburn/ArbitraryEVMFlowExecutor.sol | 1 - .../mintburn/HyperCoreFlowExecutor.sol | 104 ++++++++++-------- .../SponsoredCCTPDstPeriphery.sol | 15 ++- .../mintburn/sponsored-oft/DstOFTHandler.sol | 4 +- 4 files changed, 68 insertions(+), 56 deletions(-) diff --git a/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol b/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol index a4fd69dad..a4413ff8d 100644 --- a/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol +++ b/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import { DonationBox } from "../../chain-adapters/DonationBox.sol"; // Import MulticallHandler import { MulticallHandler } from "../../handlers/MulticallHandler.sol"; diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 9eecf302c..ba5ee8946 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -2,14 +2,15 @@ pragma solidity ^0.8.0; import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; -import { IERC20Metadata, IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { DonationBox } from "../../chain-adapters/DonationBox.sol"; import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol"; import { CoreTokenInfo } from "./Structs.sol"; import { FinalTokenInfo } from "./Structs.sol"; import { SwapHandler } from "./SwapHandler.sol"; -import { BPS_DECIMALS, BPS_SCALAR } from "./Constants.sol"; +import { BPS_SCALAR } from "./Constants.sol"; +import { Lockable } from "../../Lockable.sol"; /** * @title HyperCoreFlowExecutor @@ -17,7 +18,7 @@ import { BPS_DECIMALS, BPS_SCALAR } from "./Constants.sol"; * @dev This contract is designed to work with stablecoins. baseToken and every finalToken should all be stablecoins. * @custom:security-contact bugs@across.to */ -contract HyperCoreFlowExecutor is AccessControl { +contract HyperCoreFlowExecutor is AccessControl, Lockable { using SafeERC20 for IERC20; // Common decimals scalars @@ -40,11 +41,11 @@ contract HyperCoreFlowExecutor is AccessControl { mapping(address => FinalTokenInfo) public finalTokenInfos; /// @notice All operations performed in this contract are relative to this baseToken - address immutable baseToken; + address public immutable baseToken; /// @notice The block number of the last funds pull action per final token: either as a part of finalizing pending swaps, /// or an admin funds pull - mapping(address finalToken => uint256 lastPullFundsBlock) lastPullFundsBlock; + mapping(address finalToken => uint256 lastPullFundsBlock) public lastPullFundsBlock; /// @notice A struct used for storing state of a swap flow that has been initialized, but not yet finished struct PendingSwap { @@ -274,7 +275,7 @@ contract HyperCoreFlowExecutor is AccessControl { bool canBeUsedForAccountActivation, uint64 accountActivationFeeCore, uint64 bridgeSafetyBufferCore - ) external onlyDefaultAdmin { + ) external nonReentrant onlyDefaultAdmin { _setCoreTokenInfo( token, coreIndex, @@ -302,7 +303,13 @@ contract HyperCoreFlowExecutor is AccessControl { uint32 feePpm, uint32 suggestedDiscountBps, address accountActivationFeeToken - ) external onlyExistingCoreToken(finalToken) onlyExistingCoreToken(accountActivationFeeToken) onlyDefaultAdmin { + ) + external + nonReentrant + onlyExistingCoreToken(finalToken) + onlyExistingCoreToken(accountActivationFeeToken) + onlyDefaultAdmin + { SwapHandler swapHandler = finalTokenInfos[finalToken].swapHandler; if (address(swapHandler) == address(0)) { bytes32 salt = _swapHandlerSalt(finalToken); @@ -622,15 +629,39 @@ contract HyperCoreFlowExecutor is AccessControl { return; } - // Transfer funds to SwapHandler @ core - SwapHandler swapHandler = finalTokenInfo.swapHandler; + // State changes + uint128 cloid = ++nextCloid; + pendingSwaps[quoteNonce] = PendingSwap({ + finalRecipient: finalRecipient, + finalToken: finalToken, + minCoreAmountFromLO: guaranteedLOOut, + sponsoredCoreAmountPreFunded: totalCoreAmountToSponsor, + limitOrderCloid: cloid + }); + pendingQueue[finalToken].push(quoteNonce); + cloidToQuoteNonce[cloid] = quoteNonce; + cumulativeSponsoredAmount[finalToken] += totalEVMAmountToSponsor; + + emit SwapFlowInitialized( + quoteNonce, + finalRecipient, + finalToken, + amountInEVM, + totalEVMAmountToSponsor, + finalCoreSendAmount, + finalTokenInfo.assetIndex, + cloid + ); - // 1. Fund SwapHandler @ core with `initialToken`: use it for the trade (uint256 evmToSendForTrade, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( tokensToSendCore, initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals ); - // Here, we're sending amount that is <= amountInEVM (came from user's bridge transaction) + + SwapHandler swapHandler = finalTokenInfo.swapHandler; + // Interactions with external contracts + // 1. Fund SwapHandler @ core with `initialToken`: use it for the trade + // Always: evmToSendForTrade <= amountInEVM because of how it's calculated IERC20(initialToken).safeTransfer(address(swapHandler), evmToSendForTrade); swapHandler.transferFundsToSelfOnCore( initialToken, @@ -651,39 +682,20 @@ contract HyperCoreFlowExecutor is AccessControl { totalEVMAmountToSponsor, finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals ); - cumulativeSponsoredAmount[finalToken] += totalEVMAmountToSponsor; } - uint128 cloid = ++nextCloid; swapHandler.submitLimitOrder(finalTokenInfo, limitPriceX1e8, sizeX1e8, cloid); - - pendingSwaps[quoteNonce] = PendingSwap({ - finalRecipient: finalRecipient, - finalToken: finalToken, - minCoreAmountFromLO: guaranteedLOOut, - sponsoredCoreAmountPreFunded: totalCoreAmountToSponsor, - limitOrderCloid: cloid - }); - pendingQueue[finalToken].push(quoteNonce); - cloidToQuoteNonce[cloid] = quoteNonce; - - emit SwapFlowInitialized( - quoteNonce, - finalRecipient, - finalToken, - amountInEVM, - totalEVMAmountToSponsor, - finalCoreSendAmount, - finalTokenInfo.assetIndex, - cloid - ); } /// @notice Finalizes pending queue of swaps for `finalToken` if a corresponding SwapHandler has enough balance function finalizePendingSwaps( address finalToken, uint256 maxSwapCountToFinalize - ) external returns (uint256 finalizedSwapsCount, uint256 finalizedSwapsAmount, uint256 totalPendingSwapsRemaining) { + ) + external + nonReentrant + returns (uint256 finalizedSwapsCount, uint256 finalizedSwapsAmount, uint256 totalPendingSwapsRemaining) + { FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); CoreTokenInfo memory coreTokenInfo = coreTokenInfos[finalToken]; @@ -749,7 +761,7 @@ contract HyperCoreFlowExecutor is AccessControl { bytes32 quoteNonce, address finalRecipient, address fundingToken - ) external onlyPermissionedBot { + ) external nonReentrant onlyPermissionedBot { CoreTokenInfo memory coreTokenInfo = _getExistingCoreTokenInfo(fundingToken); bool coreUserExists = HyperCoreLib.coreUserExists(finalRecipient); require(coreUserExists == false, "Can't fund account activation for existing user"); @@ -761,6 +773,8 @@ contract HyperCoreFlowExecutor is AccessControl { ); require(safeToBridge, "Not safe to bridge"); uint256 activationFeeEvm = coreTokenInfo.accountActivationFeeEVM; + cumulativeSponsoredActivationFee[fundingToken] += activationFeeEvm; + // donationBox @ evm -> Handler @ evm _getFromDonationBox(fundingToken, activationFeeEvm); // Handler @ evm -> Handler @ core -> finalRecipient @ core @@ -772,14 +786,14 @@ contract HyperCoreFlowExecutor is AccessControl { coreTokenInfo.tokenInfo.evmExtraWeiDecimals ); - cumulativeSponsoredActivationFee[fundingToken] += activationFeeEvm; - emit SponsoredAccountActivation(quoteNonce, finalRecipient, fundingToken, activationFeeEvm); } /// @notice Cancells a pending limit order by `cloid` with an intention to submit a new limit order in its place. To /// be used for stale limit orders to speed up executing user transactions - function cancelLimitOrderByCloid(uint128 cloid) external onlyPermissionedBot returns (bytes32 quoteNonce) { + function cancelLimitOrderByCloid( + uint128 cloid + ) external nonReentrant onlyPermissionedBot returns (bytes32 quoteNonce) { quoteNonce = cloidToQuoteNonce[cloid]; PendingSwap storage pendingSwap = pendingSwaps[quoteNonce]; // A pending swap was enqueued with this Final token, so it had to be set. Unsetting the final token config is not @@ -821,7 +835,7 @@ contract HyperCoreFlowExecutor is AccessControl { uint64 priceX1e8, uint64 oldPriceX1e8, uint64 oldSizeX1e8Left - ) external onlyPermissionedBot { + ) external nonReentrant onlyPermissionedBot { PendingSwap storage pendingSwap = pendingSwaps[quoteNonce]; require(pendingSwap.limitOrderCloid == 0, "Cannot resubmit LO for non-empty cloid"); @@ -1026,27 +1040,27 @@ contract HyperCoreFlowExecutor is AccessControl { * SWEEP FUNCTIONS * **************************************/ - function sweepErc20(address token, uint256 amount) external onlyFundsSweeper { + function sweepErc20(address token, uint256 amount) external nonReentrant onlyFundsSweeper { IERC20(token).safeTransfer(msg.sender, amount); } - function sweepErc20FromDonationBox(address token, uint256 amount) external onlyFundsSweeper { + function sweepErc20FromDonationBox(address token, uint256 amount) external nonReentrant onlyFundsSweeper { _getFromDonationBox(token, amount); IERC20(token).safeTransfer(msg.sender, amount); } - function sweepERC20FromSwapHandler(address token, uint256 amount) external onlyFundsSweeper { + function sweepERC20FromSwapHandler(address token, uint256 amount) external nonReentrant onlyFundsSweeper { SwapHandler swapHandler = finalTokenInfos[token].swapHandler; swapHandler.sweepErc20(token, amount); IERC20(token).safeTransfer(msg.sender, amount); } - function sweepOnCore(address token, uint64 amount) external onlyFundsSweeper { + function sweepOnCore(address token, uint64 amount) external nonReentrant onlyFundsSweeper { HyperCoreLib.transferERC20CoreToCore(coreTokenInfos[token].coreIndex, msg.sender, amount); } // TODO? Alternative flow: make this permissionless, send money SwapHandler @ core -> DonationBox @ core => DonationBox pulls money from Core to Self (needs DonationBox code change) - function sweepOnCoreFromSwapHandler(address token, uint64 amount) external onlyPermissionedBot { + function sweepOnCoreFromSwapHandler(address token, uint64 amount) external nonReentrant onlyPermissionedBot { // We first want to make sure there are not pending limit orders for this token uint256 head = pendingQueueHead[token]; if (head < pendingQueue[token].length) { diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol index ec05d1a8c..9c99508a5 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol @@ -7,7 +7,6 @@ import { IMessageTransmitterV2 } from "../../../external/interfaces/CCTPInterfac import { SponsoredCCTPQuoteLib } from "../../../libraries/SponsoredCCTPQuoteLib.sol"; import { SponsoredCCTPInterface } from "../../../interfaces/SponsoredCCTPInterface.sol"; import { Bytes32ToAddress } from "../../../libraries/AddressConverters.sol"; -import { DonationBox } from "../../../chain-adapters/DonationBox.sol"; import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; import { ArbitraryEVMFlowExecutor } from "../ArbitraryEVMFlowExecutor.sol"; @@ -38,10 +37,6 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu * @param _signer The address of the signer that was used to sign the quotes. * @param _donationBox The address of the donation box contract. This is used to store funds that are used for sponsored flows. * @param _baseToken The address of the base token which would be the USDC on HyperEVM. - * @param _coreIndex The index of the base token on HyperCore. - * @param _canBeUsedForAccountActivation Whether the token can be used for account activation. - * @param _accountActivationFeeCore If the token can be used for account activation, this is the fee that is needed for account activation. - * @param _bridgeSafetyBufferCore This buffers is used to check if the bridging to Core is safe. * @param _multicallHandler The address of the multicall handler contract. */ constructor( @@ -59,7 +54,7 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu * @notice Sets the signer address that is used to validate the signatures of the quotes. * @param _signer The new signer address. */ - function setSigner(address _signer) external onlyDefaultAdmin { + function setSigner(address _signer) external nonReentrant onlyDefaultAdmin { signer = _signer; } @@ -67,7 +62,7 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu * @notice Sets the quote deadline buffer. This is used to prevent the quote from being used after it has expired. * @param _quoteDeadlineBuffer The new quote deadline buffer. */ - function setQuoteDeadlineBuffer(uint256 _quoteDeadlineBuffer) external onlyDefaultAdmin { + function setQuoteDeadlineBuffer(uint256 _quoteDeadlineBuffer) external nonReentrant onlyDefaultAdmin { quoteDeadlineBuffer = _quoteDeadlineBuffer; } @@ -78,7 +73,11 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu * @param attestation The attestation that is received from CCTP. * @param signature The signature of the quote. */ - function receiveMessage(bytes memory message, bytes memory attestation, bytes memory signature) external { + function receiveMessage( + bytes memory message, + bytes memory attestation, + bytes memory signature + ) external nonReentrant { cctpMessageTransmitter.receiveMessage(message, attestation); // If the hook data is invalid or the mint recipient is not this contract we cannot process the message diff --git a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol index 1d5d9ca4c..7b0682b58 100644 --- a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol +++ b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol @@ -75,7 +75,7 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV } } - function setAuthorizedPeriphery(uint32 srcEid, bytes32 srcPeriphery) external onlyDefaultAdmin { + function setAuthorizedPeriphery(uint32 srcEid, bytes32 srcPeriphery) external nonReentrant onlyDefaultAdmin { authorizedSrcPeripheryContracts[srcEid] = srcPeriphery; emit SetAuthorizedPeriphery(srcEid, srcPeriphery); } @@ -92,7 +92,7 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV bytes calldata _message, address /* _executor */, bytes calldata /* _extraData */ - ) external payable override { + ) external payable override nonReentrant { _requireAuthorizedMessage(_oApp, _message); // Decode the actual `composeMsg` payload to extract the recipient address From bd3c77a695711e8a4e14b8622f0fd684b45607e2 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Wed, 22 Oct 2025 20:35:55 -0400 Subject: [PATCH 26/47] improve: Move BytesLib to external folder (#1153) * improve: Move BytesLib to external folder Signed-off-by: Faisal Usmani * Undo var scoping Signed-off-by: Faisal Usmani --------- Signed-off-by: Faisal Usmani --- contracts/{ => external}/libraries/BytesLib.sol | 12 +++++++++--- contracts/external/libraries/MinimalLZOptions.sol | 2 +- contracts/libraries/SponsoredCCTPQuoteLib.sol | 2 +- .../mintburn/sponsored-oft/ComposeMsgCodec.sol | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) rename contracts/{ => external}/libraries/BytesLib.sol (88%) diff --git a/contracts/libraries/BytesLib.sol b/contracts/external/libraries/BytesLib.sol similarity index 88% rename from contracts/libraries/BytesLib.sol rename to contracts/external/libraries/BytesLib.sol index fe83d77c7..31777de49 100644 --- a/contracts/libraries/BytesLib.sol +++ b/contracts/external/libraries/BytesLib.sol @@ -6,13 +6,15 @@ library BytesLib { * ERRORS * **************************************/ error OutOfBounds(); - error InvalidBytes(); - error InvalidStart(); /************************************** * FUNCTIONS * **************************************/ + // The following 4 functions are copied from solidity-bytes-utils library + // https://github.com/GNSPS/solidity-bytes-utils/blob/fc502455bb2a7e26a743378df042612dd50d1eb9/contracts/BytesLib.sol#L323C5-L398C6 + // Code was copied, and slightly modified to use revert instead of require + /** * @notice Reads a uint16 from a bytes array at a given start index * @param _bytes The bytes array to convert @@ -20,7 +22,9 @@ library BytesLib { * @return result The uint16 result */ function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16 result) { - require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); + if (_bytes.length < _start + 2) { + revert OutOfBounds(); + } // solhint-disable-next-line no-inline-assembly assembly { @@ -81,6 +85,8 @@ library BytesLib { /** * @notice Reads a bytes array from a bytes array at a given start index and length + * Source: https://github.com/Vectorized/solady/blob/21202175063a0010826bf42697b5aa2ff0c27f9f/src/utils/LibBytes.sol#L369C5-L396C6 + * Code was copied, was not modified * @param _bytes The bytes array to convert * @param _start The start index of the bytes array * @param _end The end index of the bytes array diff --git a/contracts/external/libraries/MinimalLZOptions.sol b/contracts/external/libraries/MinimalLZOptions.sol index ecf495ea9..350da7591 100644 --- a/contracts/external/libraries/MinimalLZOptions.sol +++ b/contracts/external/libraries/MinimalLZOptions.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; -import { BytesLib } from "../../libraries/BytesLib.sol"; +import { BytesLib } from "../../external/libraries/BytesLib.sol"; import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; /** diff --git a/contracts/libraries/SponsoredCCTPQuoteLib.sol b/contracts/libraries/SponsoredCCTPQuoteLib.sol index 251060312..db19790a8 100644 --- a/contracts/libraries/SponsoredCCTPQuoteLib.sol +++ b/contracts/libraries/SponsoredCCTPQuoteLib.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; import { SponsoredCCTPInterface } from "../interfaces/SponsoredCCTPInterface.sol"; -import { BytesLib } from "./BytesLib.sol"; +import { BytesLib } from "../external/libraries/BytesLib.sol"; import { Bytes32ToAddress } from "./AddressConverters.sol"; /** diff --git a/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol b/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol index df11de58d..bb24ef646 100644 --- a/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol +++ b/contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.23; -import { BytesLib } from "../../../libraries/BytesLib.sol"; +import { BytesLib } from "../../../external/libraries/BytesLib.sol"; /// @notice Codec for params passed in OFT `composeMsg`. library ComposeMsgCodec { From 081eb36200a17ac25214eaa3884c5928d3d40f9c Mon Sep 17 00:00:00 2001 From: Taylor Webb <84364476+tbwebb22@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:51:39 -0600 Subject: [PATCH 27/47] fix: HyperCoreFlowExecutor stack too deep (#1154) * fix stack too deep - need to verify equivalance Signed-off-by: Taylor Webb * update comment Signed-off-by: Taylor Webb * remove confusing comment Signed-off-by: Taylor Webb * use CommonFlowParams struct Signed-off-by: Taylor Webb * use CommonFlowParams struct in _executeFlow Signed-off-by: Taylor Webb * remove confusing comment Signed-off-by: Taylor Webb * move things around Signed-off-by: Ihor Farion * remove remappings.txt Signed-off-by: Ihor Farion * chore: Update solidity and OZ versions (#1156) * chore: Update solidity and OZ versions Signed-off-by: Faisal Usmani * Upgrade hardhat as well Signed-off-by: Faisal Usmani * downgrade to 0.8.24 Signed-off-by: Faisal Usmani --------- Signed-off-by: Faisal Usmani --------- Signed-off-by: Taylor Webb Signed-off-by: Ihor Farion Signed-off-by: Faisal Usmani Co-authored-by: Ihor Farion Co-authored-by: Faisal Usmani --- .../mintburn/ArbitraryEVMFlowExecutor.sol | 52 +-- .../mintburn/HyperCoreFlowExecutor.sol | 436 ++++++++---------- contracts/periphery/mintburn/Structs.sol | 18 + .../SponsoredCCTPDstPeriphery.sol | 73 +-- .../mintburn/sponsored-oft/DstOFTHandler.sol | 67 +-- foundry.toml | 4 +- hardhat.config.ts | 2 +- package.json | 1 + yarn.lock | 5 + 9 files changed, 281 insertions(+), 377 deletions(-) diff --git a/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol b/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol index a4413ff8d..1072aac06 100644 --- a/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol +++ b/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol @@ -6,6 +6,7 @@ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.s // Import MulticallHandler import { MulticallHandler } from "../../handlers/MulticallHandler.sol"; +import { EVMFlowParams, CommonFlowParams } from "./Structs.sol"; /** * @title ArbitraryEVMFlowExecutor @@ -43,52 +44,42 @@ abstract contract ArbitraryEVMFlowExecutor { /** * @notice Executes arbitrary actions by transferring tokens to MulticallHandler * @dev Decompresses CompressedCall[] to MulticallHandler.Call[] format (adds value: 0) - * @param amount Amount of tokens to transfer to MulticallHandler - * @param quoteNonce Unique nonce for this quote - * @param initialToken Token to transfer to MulticallHandler - * @param finalToken Expected final token after actions - * @param actionData Encoded actions: abi.encode(CompressedCall[] calls) - * @param extraFeesToSponsorTokenIn Extra fees to sponsor in initialToken + * @param params Parameters of HyperEVM execution + * @return commonParams Parameters to continue sponsored execution to transfer funds to final recipient at correct destination */ - function _executeFlow( - uint256 amount, - bytes32 quoteNonce, - address initialToken, - address finalToken, - bytes memory actionData, - uint256 extraFeesToSponsorTokenIn - ) internal returns (address /* finalToken */, uint256 finalAmount, uint256 extraFeesToSponsorFinalToken) { + function _executeFlow(EVMFlowParams memory params) internal returns (CommonFlowParams memory commonParams) { // Decode the compressed action data - CompressedCall[] memory compressedCalls = abi.decode(actionData, (CompressedCall[])); + CompressedCall[] memory compressedCalls = abi.decode(params.actionData, (CompressedCall[])); // Snapshot balances - uint256 initialAmountSnapshot = IERC20(initialToken).balanceOf(address(this)); - uint256 finalAmountSnapshot = IERC20(finalToken).balanceOf(address(this)); + uint256 initialAmountSnapshot = IERC20(params.initialToken).balanceOf(address(this)); + uint256 finalAmountSnapshot = IERC20(params.commonParams.finalToken).balanceOf(address(this)); // Transfer tokens to MulticallHandler - IERC20(initialToken).safeTransfer(multicallHandler, amount); + IERC20(params.initialToken).safeTransfer(multicallHandler, params.commonParams.amountInEVM); // Build instructions for MulticallHandler bytes memory instructions = _buildMulticallInstructions( compressedCalls, - finalToken, + params.commonParams.finalToken, address(this) // Send leftover tokens back to this contract ); // Execute via MulticallHandler MulticallHandler(payable(multicallHandler)).handleV3AcrossMessage( - initialToken, - amount, + params.initialToken, + params.commonParams.amountInEVM, address(this), instructions ); + uint256 finalAmount; // This means the swap (if one was intended) didn't happen (action failed), so we use the initial token as the final token. - if (initialAmountSnapshot == IERC20(initialToken).balanceOf(address(this))) { - finalToken = initialToken; - finalAmount = amount; + if (initialAmountSnapshot == IERC20(params.initialToken).balanceOf(address(this))) { + params.commonParams.finalToken = params.initialToken; + finalAmount = params.commonParams.amountInEVM; } else { - uint256 finalBalance = IERC20(finalToken).balanceOf(address(this)); + uint256 finalBalance = IERC20(params.commonParams.finalToken).balanceOf(address(this)); if (finalBalance >= finalAmountSnapshot) { // This means the swap did happen, so we check the balance of the output token and send it. finalAmount = finalBalance - finalAmountSnapshot; @@ -98,11 +89,16 @@ abstract contract ArbitraryEVMFlowExecutor { } } - extraFeesToSponsorFinalToken = _calcExtraFeesFinal(amount, extraFeesToSponsorTokenIn, finalAmount); + params.commonParams.extraFeesIncurred = _calcExtraFeesFinal( + params.commonParams.amountInEVM, + params.commonParams.extraFeesIncurred, + finalAmount + ); + params.commonParams.amountInEVM = finalAmount; - emit ArbitraryActionsExecuted(quoteNonce, compressedCalls.length, finalAmount); + emit ArbitraryActionsExecuted(params.commonParams.quoteNonce, compressedCalls.length, finalAmount); - return (finalToken, finalAmount, extraFeesToSponsorFinalToken); + return params.commonParams; } /** diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index ba5ee8946..d32870fac 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -11,6 +11,7 @@ import { FinalTokenInfo } from "./Structs.sol"; import { SwapHandler } from "./SwapHandler.sol"; import { BPS_SCALAR } from "./Constants.sol"; import { Lockable } from "../../Lockable.sol"; +import { CommonFlowParams } from "./Structs.sol"; /** * @title HyperCoreFlowExecutor @@ -349,109 +350,73 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { * @notice This function is to be called by an inheriting contract. It is to be called after the child contract * checked the API signature and made sure that the params passed here have been verified by either the underlying * bridge mechanics, or API signaure, or both. - * @param amountInEVM The real token amount to be used in this flow execution - * @param extraFeesToSponsor Any fees subtracted from the user up until this point (e.g. CCTP bridging fees) */ - function _executeFlow( - uint256 amountInEVM, - bytes32 quoteNonce, - uint256 maxBpsToSponsor, - uint256 maxUserSlippageBps, - address finalRecipient, - address finalToken, - uint256 extraFeesToSponsor - ) internal { - if (finalToken == baseToken) { - _executeSimpleTransferFlow( - amountInEVM, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - extraFeesToSponsor, - finalToken - ); + function _executeFlow(CommonFlowParams memory params, uint256 maxUserSlippageBps) internal { + if (params.finalToken == baseToken) { + _executeSimpleTransferFlow(params); } else { - _initiateSwapFlow( - amountInEVM, - quoteNonce, - finalRecipient, - finalToken, - maxBpsToSponsor, - maxUserSlippageBps, - extraFeesToSponsor - ); + _initiateSwapFlow(params, maxUserSlippageBps); } } /// @notice Execute a simple transfer flow in which we transfer `finalToken` to the user on HyperCore after receiving /// an amount of finalToken from the user on HyperEVM - function _executeSimpleTransferFlow( - uint256 amountInEVM, - bytes32 quoteNonce, - uint256 maxBpsToSponsor, - address finalRecipient, - uint256 extraBridgingFeesEVM, - address finalToken - ) internal virtual { + function _executeSimpleTransferFlow(CommonFlowParams memory params) internal virtual { + address finalToken = params.finalToken; CoreTokenInfo storage coreTokenInfo = coreTokenInfos[finalToken]; - bool isSponsoredFlow = maxBpsToSponsor > 0; - bool userHasNoCoreAccount = !HyperCoreLib.coreUserExists(finalRecipient); - if (userHasNoCoreAccount) { - if (isSponsoredFlow) { - revert AccountNotActivated(finalRecipient); + // Check account activation + if (!HyperCoreLib.coreUserExists(params.finalRecipient)) { + if (params.maxBpsToSponsor > 0) { + revert AccountNotActivated(params.finalRecipient); } else { - _fallbackHyperEVMFlow( - amountInEVM, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - extraBridgingFeesEVM, - finalToken - ); - emit SimpleTransferFallbackAccountActivation(quoteNonce); + _fallbackHyperEVMFlow(params); + emit SimpleTransferFallbackAccountActivation(params.quoteNonce); return; } } - uint256 maxEvmAmountToSponsor = ((amountInEVM + extraBridgingFeesEVM) * maxBpsToSponsor) / BPS_SCALAR; - uint256 amountToSponsor = extraBridgingFeesEVM; - if (amountToSponsor > maxEvmAmountToSponsor) { - amountToSponsor = maxEvmAmountToSponsor; - } + // Calculate sponsorship amount in scope + uint256 amountToSponsor; + { + uint256 maxEvmAmountToSponsor = ((params.amountInEVM + params.extraFeesIncurred) * params.maxBpsToSponsor) / + BPS_SCALAR; + amountToSponsor = params.extraFeesIncurred; + if (amountToSponsor > maxEvmAmountToSponsor) { + amountToSponsor = maxEvmAmountToSponsor; + } - if (amountToSponsor > 0) { - if (!_availableInDonationBox(coreTokenInfo.tokenInfo.evmContract, amountToSponsor)) { - amountToSponsor = 0; + if (amountToSponsor > 0) { + if (!_availableInDonationBox(coreTokenInfo.tokenInfo.evmContract, amountToSponsor)) { + amountToSponsor = 0; + } } } - uint256 finalAmount = amountInEVM + amountToSponsor; - (uint256 quotedEvmAmount, uint64 quotedCoreAmount) = HyperCoreLib.maximumEVMSendAmountToAmounts( - finalAmount, - coreTokenInfo.tokenInfo.evmExtraWeiDecimals - ); - // If there are no funds left on the destination side of the bridge, the funds will be lost in the - // bridge. We check send safety via `isCoreAmountSafeToBridge` - bool isSafe = HyperCoreLib.isCoreAmountSafeToBridge( - coreTokenInfo.coreIndex, - quotedCoreAmount, - coreTokenInfo.bridgeSafetyBufferCore - ); - - // If the amount is not safe to bridge because the bridge doesn't have enough liquidity, - // fall back to sending user funds on HyperEVM. - if (!isSafe) { - _fallbackHyperEVMFlow( - amountInEVM, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - extraBridgingFeesEVM, - finalToken + // Calculate quoted amounts and check safety + uint256 quotedEvmAmount; + uint64 quotedCoreAmount; + { + uint256 finalAmount = params.amountInEVM + amountToSponsor; + (quotedEvmAmount, quotedCoreAmount) = HyperCoreLib.maximumEVMSendAmountToAmounts( + finalAmount, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals ); - emit SimpleTransferFallbackUnsafeToBridge(quoteNonce); - return; + // If there are no funds left on the destination side of the bridge, the funds will be lost in the + // bridge. We check send safety via `isCoreAmountSafeToBridge` + if ( + !HyperCoreLib.isCoreAmountSafeToBridge( + coreTokenInfo.coreIndex, + quotedCoreAmount, + coreTokenInfo.bridgeSafetyBufferCore + ) + ) { + // If the amount is not safe to bridge because the bridge doesn't have enough liquidity, + // fall back to sending user funds on HyperEVM. + _fallbackHyperEVMFlow(params); + emit SimpleTransferFallbackUnsafeToBridge(params.quoteNonce); + return; + } } if (amountToSponsor > 0) { @@ -466,16 +431,16 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { HyperCoreLib.transferERC20EVMToCore( finalToken, coreTokenInfo.coreIndex, - finalRecipient, + params.finalRecipient, quotedEvmAmount, coreTokenInfo.tokenInfo.evmExtraWeiDecimals ); emit SimpleTransferFlowCompleted( - quoteNonce, - finalRecipient, + params.quoteNonce, + params.finalRecipient, finalToken, - amountInEVM, + params.amountInEVM, amountToSponsor, quotedEvmAmount, quotedCoreAmount @@ -485,183 +450,160 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { /// @notice initialized swap flow to eventually forward `finalToken` to the user, starting from `baseToken` (received /// from a user bridge transaction) function _initiateSwapFlow( - // In initialToken - uint256 amountInEVM, - bytes32 quoteNonce, - address finalRecipient, - address finalToken, - uint256 maxBpsToSponsor, + CommonFlowParams memory params, // `maxUserSlippageBps` here means how much token user receives compared to a 1 to 1 - uint256 maxUserSlippageBps, - // In initialToken - uint256 extraBridgingFeesEVM + uint256 maxUserSlippageBps ) internal { - FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); + // Check account activation + if (!HyperCoreLib.coreUserExists(params.finalRecipient)) { + if (params.maxBpsToSponsor > 0) { + revert AccountNotActivated(params.finalRecipient); + } else { + _fallbackHyperEVMFlow(params); + emit SwapFlowFallbackAccountActivation(params.quoteNonce); + return; + } + } + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(params.finalToken); address initialToken = baseToken; CoreTokenInfo memory initialCoreTokenInfo = coreTokenInfos[initialToken]; - CoreTokenInfo memory finalCoreTokenInfo = coreTokenInfos[finalToken]; + CoreTokenInfo memory finalCoreTokenInfo = coreTokenInfos[params.finalToken]; + uint64 tokensToSendCore; + uint64 guaranteedLOOut; + uint64 sizeX1e8; + uint64 finalCoreSendAmount; + uint64 totalCoreAmountToSponsor; + uint256 totalEVMAmountToSponsor; + { + // Calculate limit order amounts and check if feasible + uint64 minAllowableAmountToForwardCore; + { + // In initialToken + (, uint64 amountInEquivalentCore) = HyperCoreLib.maximumEVMSendAmountToAmounts( + params.amountInEVM, + initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); - // In initialToken - (, uint64 amountInEquivalentCore) = HyperCoreLib.maximumEVMSendAmountToAmounts( - amountInEVM, - initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals - ); + // In finalToken + uint64 maxAmountToSponsorCore; + (minAllowableAmountToForwardCore, maxAmountToSponsorCore) = _calculateAllowableAmountsForFlow( + params.amountInEVM + params.extraFeesIncurred, + initialCoreTokenInfo, + finalCoreTokenInfo, + params.maxBpsToSponsor > 0, + params.maxBpsToSponsor, + maxUserSlippageBps + ); - bool isSponsoredFlow = maxBpsToSponsor > 0; - bool userHasNoCoreAccount = !HyperCoreLib.coreUserExists(finalRecipient); - if (userHasNoCoreAccount) { - if (isSponsoredFlow) { - revert AccountNotActivated(finalRecipient); - } else { - _fallbackHyperEVMFlow( - amountInEVM, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - extraBridgingFeesEVM, - initialToken + uint64 limitPriceX1e8 = _getSuggestedPriceX1e8(finalTokenInfo); + (sizeX1e8, tokensToSendCore, guaranteedLOOut) = _calcLOAmounts( + amountInEquivalentCore, + limitPriceX1e8, + finalTokenInfo.isBuy, + finalTokenInfo.feePpm, + initialCoreTokenInfo, + finalCoreTokenInfo ); - emit SwapFlowFallbackAccountActivation(quoteNonce); - return; + + if ( + minAllowableAmountToForwardCore > guaranteedLOOut && + minAllowableAmountToForwardCore - guaranteedLOOut > maxAmountToSponsorCore + ) { + // We can't provide the required slippage in a swap flow, try simple transfer flow instead + params.finalToken = initialToken; + _executeSimpleTransferFlow(params); + emit SwapFlowFallbackTooExpensive( + params.quoteNonce, + minAllowableAmountToForwardCore - guaranteedLOOut, + maxAmountToSponsorCore + ); + return; + } } - } - // In initialToken - uint256 totalAmountBridgedEVM = amountInEVM + extraBridgingFeesEVM; - // In finalToken - (uint64 minAllowableAmountToForwardCore, uint64 maxAmountToSponsorCore) = _calculateAllowableAmountsForFlow( - totalAmountBridgedEVM, - initialCoreTokenInfo, - finalCoreTokenInfo, - isSponsoredFlow, - maxBpsToSponsor, - maxUserSlippageBps - ); + // Calculate sponsorship amounts + finalCoreSendAmount = params.maxBpsToSponsor > 0 ? minAllowableAmountToForwardCore : guaranteedLOOut; + totalCoreAmountToSponsor = finalCoreSendAmount > guaranteedLOOut + ? finalCoreSendAmount - guaranteedLOOut + : 0; + + totalEVMAmountToSponsor = 0; + if (totalCoreAmountToSponsor > 0) { + (totalEVMAmountToSponsor, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + totalCoreAmountToSponsor, + finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + } - uint64 limitPriceX1e8 = _getSuggestedPriceX1e8(finalTokenInfo); - (uint64 sizeX1e8, uint64 tokensToSendCore, uint64 guaranteedLOOut) = _calcLOAmounts( - amountInEquivalentCore, - limitPriceX1e8, - finalTokenInfo.isBuy, - finalTokenInfo.feePpm, - initialCoreTokenInfo, - finalCoreTokenInfo - ); + if (totalEVMAmountToSponsor > 0) { + if (!_availableInDonationBox(params.finalToken, totalEVMAmountToSponsor)) { + // We can't provide the required `totalEVMAmountToSponsor` in a swap flow, try simple transfer flow instead + params.finalToken = initialToken; + _executeSimpleTransferFlow(params); + emit SwapFlowFallbackDonationBox(params.quoteNonce, params.finalToken, totalEVMAmountToSponsor); + return; + } + } - if ( - minAllowableAmountToForwardCore > guaranteedLOOut && - minAllowableAmountToForwardCore - guaranteedLOOut > maxAmountToSponsorCore - ) { - // We can't provide the required slippage in a swap flow, try simple transfer flow instead - _executeSimpleTransferFlow( - amountInEVM, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - extraBridgingFeesEVM, - initialToken - ); - emit SwapFlowFallbackTooExpensive( - quoteNonce, - minAllowableAmountToForwardCore - guaranteedLOOut, - maxAmountToSponsorCore + // Check that we can safely bridge to HCore (for the trade amount actually needed) + bool isSafeToBridgeMainToken = HyperCoreLib.isCoreAmountSafeToBridge( + initialCoreTokenInfo.coreIndex, + tokensToSendCore, + initialCoreTokenInfo.bridgeSafetyBufferCore ); - return; - } - - uint64 finalCoreSendAmount = isSponsoredFlow ? minAllowableAmountToForwardCore : guaranteedLOOut; - uint64 totalCoreAmountToSponsor = finalCoreSendAmount > guaranteedLOOut - ? finalCoreSendAmount - guaranteedLOOut - : 0; - - uint256 totalEVMAmountToSponsor = 0; - if (totalCoreAmountToSponsor > 0) { - (totalEVMAmountToSponsor, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + bool isSafeTobridgeSponsorshipFunds = HyperCoreLib.isCoreAmountSafeToBridge( + finalCoreTokenInfo.coreIndex, totalCoreAmountToSponsor, - finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + finalCoreTokenInfo.bridgeSafetyBufferCore ); - } - if (totalEVMAmountToSponsor > 0) { - if (!_availableInDonationBox(finalToken, totalEVMAmountToSponsor)) { - // We can't provide the required `totalEVMAmountToSponsor` in a swap flow, try simple transfer flow instead - _executeSimpleTransferFlow( - amountInEVM, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - extraBridgingFeesEVM, - initialToken + if (!isSafeToBridgeMainToken || !isSafeTobridgeSponsorshipFunds) { + params.finalToken = initialToken; + _fallbackHyperEVMFlow(params); + emit SwapFlowFallbackUnsafeToBridge( + params.quoteNonce, + isSafeToBridgeMainToken, + params.finalToken, + isSafeTobridgeSponsorshipFunds ); - emit SwapFlowFallbackDonationBox(quoteNonce, finalToken, totalEVMAmountToSponsor); return; } } - // Check that we can safely bridge to HCore (for the trade amount actually needed) - bool isSafeToBridgeMainToken = HyperCoreLib.isCoreAmountSafeToBridge( - initialCoreTokenInfo.coreIndex, - tokensToSendCore, - initialCoreTokenInfo.bridgeSafetyBufferCore - ); - bool isSafeTobridgeSponsorshipFunds = HyperCoreLib.isCoreAmountSafeToBridge( - finalCoreTokenInfo.coreIndex, - totalCoreAmountToSponsor, - finalCoreTokenInfo.bridgeSafetyBufferCore - ); - - if (!isSafeToBridgeMainToken || !isSafeTobridgeSponsorshipFunds) { - _fallbackHyperEVMFlow( - amountInEVM, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - extraBridgingFeesEVM, - initialToken - ); - emit SwapFlowFallbackUnsafeToBridge( - quoteNonce, - isSafeToBridgeMainToken, - finalToken, - isSafeTobridgeSponsorshipFunds - ); - return; - } - + // Finalize swap flow setup by updating state and funding SwapHandler // State changes uint128 cloid = ++nextCloid; - pendingSwaps[quoteNonce] = PendingSwap({ - finalRecipient: finalRecipient, - finalToken: finalToken, + pendingSwaps[params.quoteNonce] = PendingSwap({ + finalRecipient: params.finalRecipient, + finalToken: params.finalToken, minCoreAmountFromLO: guaranteedLOOut, sponsoredCoreAmountPreFunded: totalCoreAmountToSponsor, limitOrderCloid: cloid }); - pendingQueue[finalToken].push(quoteNonce); - cloidToQuoteNonce[cloid] = quoteNonce; - cumulativeSponsoredAmount[finalToken] += totalEVMAmountToSponsor; + pendingQueue[params.finalToken].push(params.quoteNonce); + cloidToQuoteNonce[cloid] = params.quoteNonce; + cumulativeSponsoredAmount[params.finalToken] += totalEVMAmountToSponsor; emit SwapFlowInitialized( - quoteNonce, - finalRecipient, - finalToken, - amountInEVM, + params.quoteNonce, + params.finalRecipient, + params.finalToken, + params.amountInEVM, totalEVMAmountToSponsor, finalCoreSendAmount, finalTokenInfo.assetIndex, cloid ); - (uint256 evmToSendForTrade, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( - tokensToSendCore, - initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals - ); - SwapHandler swapHandler = finalTokenInfo.swapHandler; // Interactions with external contracts // 1. Fund SwapHandler @ core with `initialToken`: use it for the trade // Always: evmToSendForTrade <= amountInEVM because of how it's calculated + (uint256 evmToSendForTrade, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + tokensToSendCore, + initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); IERC20(initialToken).safeTransfer(address(swapHandler), evmToSendForTrade); swapHandler.transferFundsToSelfOnCore( initialToken, @@ -673,18 +615,18 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { // 2. Fund SwapHandler @ core with `finalToken`: use that for sponsorship if (totalEVMAmountToSponsor > 0) { // We checked that this amount is in donationBox before - _getFromDonationBox(finalToken, totalEVMAmountToSponsor); + _getFromDonationBox(params.finalToken, totalEVMAmountToSponsor); // These funds just came from donationBox - IERC20(finalToken).safeTransfer(address(swapHandler), totalEVMAmountToSponsor); + IERC20(params.finalToken).safeTransfer(address(swapHandler), totalEVMAmountToSponsor); swapHandler.transferFundsToSelfOnCore( - finalToken, + params.finalToken, finalCoreTokenInfo.coreIndex, totalEVMAmountToSponsor, finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals ); } - swapHandler.submitLimitOrder(finalTokenInfo, limitPriceX1e8, sizeX1e8, cloid); + swapHandler.submitLimitOrder(finalTokenInfo, _getSuggestedPriceX1e8(finalTokenInfo), sizeX1e8, cloid); } /// @notice Finalizes pending queue of swaps for `finalToken` if a corresponding SwapHandler has enough balance @@ -922,33 +864,27 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { } /// @notice Forwards `amount` plus potential sponsorship funds (for bridging fee) to user on HyperEVM - function _fallbackHyperEVMFlow( - uint256 amount, - bytes32 quoteNonce, - uint256 maxBpsToSponsor, - address finalRecipient, - uint256 extraFeesToSponsor, - address finalToken - ) internal virtual { - uint256 maxEvmAmountToSponsor = ((amount + extraFeesToSponsor) * maxBpsToSponsor) / BPS_SCALAR; - uint256 sponsorshipFundsToForward = extraFeesToSponsor > maxEvmAmountToSponsor + function _fallbackHyperEVMFlow(CommonFlowParams memory params) internal virtual { + uint256 maxEvmAmountToSponsor = ((params.amountInEVM + params.extraFeesIncurred) * params.maxBpsToSponsor) / + BPS_SCALAR; + uint256 sponsorshipFundsToForward = params.extraFeesIncurred > maxEvmAmountToSponsor ? maxEvmAmountToSponsor - : extraFeesToSponsor; + : params.extraFeesIncurred; - if (!_availableInDonationBox(finalToken, sponsorshipFundsToForward)) { + if (!_availableInDonationBox(params.finalToken, sponsorshipFundsToForward)) { sponsorshipFundsToForward = 0; } if (sponsorshipFundsToForward > 0) { - _getFromDonationBox(finalToken, sponsorshipFundsToForward); + _getFromDonationBox(params.finalToken, sponsorshipFundsToForward); } - uint256 totalAmountToForward = amount + sponsorshipFundsToForward; - IERC20(finalToken).safeTransfer(finalRecipient, totalAmountToForward); - cumulativeSponsoredAmount[finalToken] += sponsorshipFundsToForward; + uint256 totalAmountToForward = params.amountInEVM + sponsorshipFundsToForward; + IERC20(params.finalToken).safeTransfer(params.finalRecipient, totalAmountToForward); + cumulativeSponsoredAmount[params.finalToken] += sponsorshipFundsToForward; emit FallbackHyperEVMFlowCompleted( - quoteNonce, - finalRecipient, - finalToken, - amount, + params.quoteNonce, + params.finalRecipient, + params.finalToken, + params.amountInEVM, sponsorshipFundsToForward, totalAmountToForward ); diff --git a/contracts/periphery/mintburn/Structs.sol b/contracts/periphery/mintburn/Structs.sol index 7dd372091..c188483c8 100644 --- a/contracts/periphery/mintburn/Structs.sol +++ b/contracts/periphery/mintburn/Structs.sol @@ -32,3 +32,21 @@ struct FinalTokenInfo { // Contract where the accounting for all baseToken -> finalToken accounting happens. One pre finalToken SwapHandler swapHandler; } + +/// @notice Common parameters shared across flow execution functions +struct CommonFlowParams { + uint256 amountInEVM; + bytes32 quoteNonce; + address finalRecipient; + address finalToken; + uint256 maxBpsToSponsor; + uint256 extraFeesIncurred; +} + +/// @notice Parameters for executing flows with arbitrary EVM actions +struct EVMFlowParams { + CommonFlowParams commonParams; + address initialToken; + bytes actionData; + bool transferToCore; +} diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol index 9c99508a5..f020886c8 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol @@ -9,6 +9,7 @@ import { SponsoredCCTPInterface } from "../../../interfaces/SponsoredCCTPInterfa import { Bytes32ToAddress } from "../../../libraries/AddressConverters.sol"; import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; import { ArbitraryEVMFlowExecutor } from "../ArbitraryEVMFlowExecutor.sol"; +import { CommonFlowParams, EVMFlowParams } from "../Structs.sol"; /** * @title SponsoredCCTPDstPeriphery @@ -98,37 +99,37 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu uint256 amountAfterFees = quote.amount - feeExecuted; + CommonFlowParams memory commonParams = CommonFlowParams({ + amountInEVM: amountAfterFees, + quoteNonce: quote.nonce, + finalRecipient: quote.finalRecipient.toAddress(), + // If the quote is invalid we don't want to swap, so we use the base token as the final token + finalToken: isQuoteValid ? quote.finalToken.toAddress() : baseToken, + // If the quote is invalid we don't sponsor the flow or the extra fees + maxBpsToSponsor: isQuoteValid ? quote.maxBpsToSponsor : 0, + extraFeesIncurred: feeExecuted + }); + // Route to appropriate execution based on executionMode if ( isQuoteValid && (quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) || quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToEVM)) ) { + commonParams.finalToken = quote.finalToken.toAddress(); + // Execute flow with arbitrary evm actions _executeWithEVMFlow( - amountAfterFees, - quote.nonce, - quote.maxBpsToSponsor, - baseToken, // initialToken - quote.finalToken.toAddress(), - quote.finalRecipient.toAddress(), - quote.actionData, - feeExecuted, - quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) + EVMFlowParams({ + commonParams: commonParams, + initialToken: baseToken, + actionData: quote.actionData, + transferToCore: quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) + }) ); } else { // Execute standard HyperCore flow (default) - HyperCoreFlowExecutor._executeFlow( - amountAfterFees, - quote.nonce, - // If the quote is invalid we don't sponsor the flow or the extra fees - isQuoteValid ? quote.maxBpsToSponsor : 0, - quote.maxUserSlippageBps, - quote.finalRecipient.toAddress(), - // If the quote is invalid we don't want to swap, so we use the base token as the final token - isQuoteValid ? quote.finalToken.toAddress() : baseToken, - isQuoteValid ? feeExecuted : 0 - ); + HyperCoreFlowExecutor._executeFlow(commonParams, quote.maxUserSlippageBps); } emit SponsoredMintAndWithdraw( @@ -152,36 +153,10 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu quote.deadline + quoteDeadlineBuffer >= block.timestamp; } - function _executeWithEVMFlow( - uint256 amount, - bytes32 quoteNonce, - uint256 maxBpsToSponsor, - address initialToken, - address finalToken, - address finalRecipient, - bytes memory actionData, - uint256 extraFeesToSponsor, - bool transferToCore - ) internal { - uint256 finalAmount; - uint256 extraFeesToSponsorFinalToken; - (finalToken, finalAmount, extraFeesToSponsorFinalToken) = ArbitraryEVMFlowExecutor._executeFlow( - amount, - quoteNonce, - initialToken, - finalToken, - actionData, - extraFeesToSponsor - ); + function _executeWithEVMFlow(EVMFlowParams memory params) internal { + params.commonParams = ArbitraryEVMFlowExecutor._executeFlow(params); // Route to appropriate destination based on transferToCore flag - (transferToCore ? _executeSimpleTransferFlow : _fallbackHyperEVMFlow)( - finalAmount, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - extraFeesToSponsorFinalToken, - finalToken - ); + (params.transferToCore ? _executeSimpleTransferFlow : _fallbackHyperEVMFlow)(params.commonParams); } } diff --git a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol index 7b0682b58..fcc241830 100644 --- a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol +++ b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol @@ -9,6 +9,7 @@ import { AddressToBytes32, Bytes32ToAddress } from "../../../libraries/AddressCo import { IOFT, IOAppCore } from "../../../interfaces/IOFT.sol"; import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; import { ArbitraryEVMFlowExecutor } from "../ArbitraryEVMFlowExecutor.sol"; +import { CommonFlowParams, EVMFlowParams } from "../Structs.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; @@ -119,6 +120,15 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV uint8 executionMode = composeMsg._getExecutionMode(); bytes memory actionData = composeMsg._getActionData(); + CommonFlowParams memory commonParams = CommonFlowParams({ + amountInEVM: amountLD, + quoteNonce: quoteNonce, + finalRecipient: finalRecipient, + finalToken: finalToken, + maxBpsToSponsor: maxBpsToSponsor, + extraFeesIncurred: EXTRA_FEES_TO_SPONSOR + }); + // Route to appropriate execution based on executionMode if ( executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) || @@ -126,61 +136,24 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV ) { // Execute flow with arbitrary evm actions _executeWithEVMFlow( - amountLD, - quoteNonce, - maxBpsToSponsor, - baseToken, // initialToken - finalToken, - finalRecipient, - actionData, - EXTRA_FEES_TO_SPONSOR, - executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) + EVMFlowParams({ + commonParams: commonParams, + initialToken: baseToken, + actionData: actionData, + transferToCore: executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) + }) ); } else { // Execute standard HyperCore flow (default) - HyperCoreFlowExecutor._executeFlow( - amountLD, - quoteNonce, - maxBpsToSponsor, - maxUserSlippageBps, - finalRecipient, - finalToken, - EXTRA_FEES_TO_SPONSOR - ); + HyperCoreFlowExecutor._executeFlow(commonParams, maxUserSlippageBps); } } - function _executeWithEVMFlow( - uint256 amount, - bytes32 quoteNonce, - uint256 maxBpsToSponsor, - address initialToken, - address finalToken, - address finalRecipient, - bytes memory actionData, - uint256 extraFeesToSponsor, - bool transferToCore - ) internal { - uint256 finalAmount; - uint256 extraFeesToSponsorFinalToken; - (finalToken, finalAmount, extraFeesToSponsorFinalToken) = ArbitraryEVMFlowExecutor._executeFlow( - amount, - quoteNonce, - initialToken, - finalToken, - actionData, - extraFeesToSponsor - ); + function _executeWithEVMFlow(EVMFlowParams memory params) internal { + params.commonParams = ArbitraryEVMFlowExecutor._executeFlow(params); // Route to appropriate destination based on transferToCore flag - (transferToCore ? _executeSimpleTransferFlow : _fallbackHyperEVMFlow)( - finalAmount, - quoteNonce, - maxBpsToSponsor, - finalRecipient, - extraFeesToSponsorFinalToken, - finalToken - ); + (params.transferToCore ? _executeSimpleTransferFlow : _fallbackHyperEVMFlow)(params.commonParams); } /// @notice Checks that message was authorized by LayerZero's identity system and that it came from authorized src periphery diff --git a/foundry.toml b/foundry.toml index 5bccbae62..ce59fa1f0 100644 --- a/foundry.toml +++ b/foundry.toml @@ -29,11 +29,11 @@ remappings = [ ] via_ir = true optimizer_runs = 800 -solc_version = "0.8.23" +solc_version = "0.8.24" revert_strings = "strip" fs_permissions = [{ access = "read", path = "./"}] -solc = "0.8.23" +solc = "0.8.24" evm_version = "prague" [profile.zksync] diff --git a/hardhat.config.ts b/hardhat.config.ts index 62a0ddd1e..781bd199b 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -56,7 +56,7 @@ const isTest = process.env.IS_TEST === "true" || process.env.CI === "true"; // the following config is true. const compileZk = process.env.COMPILE_ZK === "true"; -const solcVersion = "0.8.23"; +const solcVersion = "0.8.24"; // Compilation settings are overridden for large contracts to allow them to compile without going over the bytecode // limit. diff --git a/package.json b/package.json index 63b681ea7..fec61c6ce 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@ethersproject/bignumber": "5.7.0", "@openzeppelin/contracts": "4.9.6", "@openzeppelin/contracts-upgradeable": "4.9.6", + "@openzeppelin/contracts-v5": "npm:@openzeppelin/contracts@5", "@openzeppelin/foundry-upgrades": "^0.4.0", "@safe-global/protocol-kit": "^6.1.1", "@scroll-tech/contracts": "^0.1.0", diff --git a/yarn.lock b/yarn.lock index 5c5974a4f..13b48e474 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2557,6 +2557,11 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.6.tgz#38b21708a719da647de4bb0e4802ee235a0d24df" integrity sha512-m4iHazOsOCv1DgM7eD7GupTJ+NFVujRZt1wzddDPSVGpWdKq1SKkla5htKG7+IS4d2XOCtzkUNwRZ7Vq5aEUMA== +"@openzeppelin/contracts-v5@npm:@openzeppelin/contracts@5": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.4.0.tgz#177594bdb2d86c71f5d1052fe40cb4edb95fb20f" + integrity sha512-eCYgWnLg6WO+X52I16TZt8uEjbtdkgLC0SUX/xnAksjjrQI4Xfn4iBRoI5j55dmlOhDv1Y7BoR3cU7e3WWhC6A== + "@openzeppelin/contracts@3.4.1-solc-0.7-2": version "3.4.1-solc-0.7-2" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.1-solc-0.7-2.tgz#371c67ebffe50f551c3146a9eec5fe6ffe862e92" From 6b7a8a43ce313ef0c1b4c0b74471dd9a438153fc Mon Sep 17 00:00:00 2001 From: Ihor Farion <65650773+grasphoper@users.noreply.github.com> Date: Thu, 23 Oct 2025 02:26:06 -0700 Subject: [PATCH 28/47] feat: remove swap calcs form contracts (#1158) * remove Signed-off-by: Ihor Farion * polish Signed-off-by: Ihor Farion * add _finalizeSwapFlows Signed-off-by: Ihor Farion * complete finalization flow Signed-off-by: Ihor Farion * add a comment Signed-off-by: Ihor Farion * add Signed-off-by: Ihor Farion * Update events (#1159) * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice --------- Signed-off-by: Matt Rice * fix cumulativeSponsoredAmount + add event Signed-off-by: Ihor Farion * update comment Signed-off-by: Ihor Farion * Update contracts/periphery/mintburn/HyperCoreFlowExecutor.sol Co-authored-by: Matt Rice --------- Signed-off-by: Ihor Farion Signed-off-by: Matt Rice Co-authored-by: Matt Rice --- .../interfaces/SponsoredCCTPInterface.sol | 10 - .../mintburn/ArbitraryEVMFlowExecutor.sol | 16 +- .../mintburn/HyperCoreFlowExecutor.sol | 990 +++++++----------- .../SponsoredCCTPDstPeriphery.sol | 12 - 4 files changed, 397 insertions(+), 631 deletions(-) diff --git a/contracts/interfaces/SponsoredCCTPInterface.sol b/contracts/interfaces/SponsoredCCTPInterface.sol index fe58ea078..5c5b5f6d1 100644 --- a/contracts/interfaces/SponsoredCCTPInterface.sol +++ b/contracts/interfaces/SponsoredCCTPInterface.sol @@ -30,16 +30,6 @@ interface SponsoredCCTPInterface { bytes signature ); - event SponsoredMintAndWithdraw( - bytes32 indexed quoteNonce, - bytes32 indexed finalRecipient, - bytes32 indexed finalToken, - uint256 finalAmount, - uint256 quoteDeadline, - uint256 maxBpsToSponsor, - uint256 maxUserSlippageBps - ); - event SimpleTansferToCore( address indexed finalToken, address indexed finalRecipient, diff --git a/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol b/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol index 1072aac06..b8188b19e 100644 --- a/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol +++ b/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol @@ -28,7 +28,13 @@ abstract contract ArbitraryEVMFlowExecutor { address public immutable multicallHandler; /// @notice Emitted when arbitrary actions are executed successfully - event ArbitraryActionsExecuted(bytes32 indexed quoteNonce, uint256 callCount, uint256 finalAmount); + event ArbitraryActionsExecuted( + bytes32 indexed quoteNonce, + address indexed initialToken, + uint256 initialAmount, + address indexed finalToken, + uint256 finalAmount + ); /// @notice Error thrown when final balance is insufficient error InsufficientFinalBalance(address token, uint256 expected, uint256 actual); @@ -96,7 +102,13 @@ abstract contract ArbitraryEVMFlowExecutor { ); params.commonParams.amountInEVM = finalAmount; - emit ArbitraryActionsExecuted(params.commonParams.quoteNonce, compressedCalls.length, finalAmount); + emit ArbitraryActionsExecuted( + params.commonParams.quoteNonce, + params.initialToken, + params.commonParams.amountInEVM, + params.commonParams.finalToken, + finalAmount + ); return params.commonParams; } diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index d32870fac..5021d5be4 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -9,7 +9,7 @@ import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol"; import { CoreTokenInfo } from "./Structs.sol"; import { FinalTokenInfo } from "./Structs.sol"; import { SwapHandler } from "./SwapHandler.sol"; -import { BPS_SCALAR } from "./Constants.sol"; +import { BPS_SCALAR, BPS_DECIMALS } from "./Constants.sol"; import { Lockable } from "../../Lockable.sol"; import { CommonFlowParams } from "./Structs.sol"; @@ -27,6 +27,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { uint256 public constant PPM_SCALAR = 10 ** PPM_DECIMALS; // Decimals to use for Price calculations in limit order-related calculation functions uint8 public constant PX_D = 8; + uint64 public constant ONEX1e8 = 10 ** 8; // Roles bytes32 public constant PERMISSIONED_BOT_ROLE = keccak256("PERMISSIONED_BOT_ROLE"); @@ -49,42 +50,32 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { mapping(address finalToken => uint256 lastPullFundsBlock) public lastPullFundsBlock; /// @notice A struct used for storing state of a swap flow that has been initialized, but not yet finished - struct PendingSwap { + struct SwapFlowState { address finalRecipient; address finalToken; - /// @notice totalCoreAmountToForwardToUser = minCoreAmountFromLO + sponsoredCoreAmountPreFunded always. - uint64 minCoreAmountFromLO; - uint64 sponsoredCoreAmountPreFunded; - uint128 limitOrderCloid; + uint64 minAmountToSend; // for sponsored: one to one, non-sponsored: one to one minus slippage + uint64 maxAmountToSend; // for sponsored: one to one (from total bridged amt), for non-sponsored: one to one, less bridging fees incurred + bool isSponsored; + bool finalized; } /// @notice A mapping containing the pending state between initializing the swap flow and finalizing it - mapping(bytes32 quoteNonce => PendingSwap pendingSwap) public pendingSwaps; - /// @notice A FCFS queue of pending swap flows to be executed. Per finalToken - mapping(address finalToken => bytes32[] quoteNonces) public pendingQueue; - /// @notice An index of the first unexecuted pending swap flow. Or equal to pendingQueue length if currently empty - mapping(address => uint256) public pendingQueueHead; + mapping(bytes32 quoteNonce => SwapFlowState swap) public swaps; /// @notice The cumulative amount of funds sponsored for each final token. mapping(address => uint256) public cumulativeSponsoredAmount; /// @notice The cumulative amount of activation fees sponsored for each final token. mapping(address => uint256) public cumulativeSponsoredActivationFee; - /// @notice Used for uniquely identifying Limit Orders this contract submits. Monotonically increasing - uint128 public nextCloid; - - /// @notice A mapping from limit order cliod to the quoteNonce (user order id) responsible for submitting the LO - mapping(uint128 => bytes32) public cloidToQuoteNonce; + /************************************** + * EVENTS * + **************************************/ /// @notice Emitted when the donation box is insufficient funds. - event DonationBoxInsufficientFunds(address token, uint256 amount); + event DonationBoxInsufficientFunds(bytes32 indexed quoteNonce, address token, uint256 amount, uint256 balance); - /// @notice Emitted when the donation box is insufficient funds and we can't proceed. - error DonationBoxInsufficientFundsError(address token, uint256 amount); - - /// @notice Emitted when we're inside the sponsored flow and a user doesn't have a HyperCore account activated. The - /// bot should activate user's account first by calling `activateUserAccount` - error AccountNotActivated(address user); + /// @notice Emitted whenever the account is not activated in the non-sponsored flow. We fall back to HyperEVM flow in that case + event AccountNotActivated(bytes32 indexed quoteNonce, address user); /// @notice Emitted when a simple transfer to core is executed. event SimpleTransferFlowCompleted( @@ -92,10 +83,9 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { address indexed finalRecipient, address indexed finalToken, // All amounts are in finalToken - uint256 evmAmountReceived, - uint256 evmAmountSponsored, - uint256 evmAmountTransferred, - uint64 coreAmountTransferred + uint256 evmAmountIn, + uint256 bridgingFeesIncurred, + uint256 evmAmountSponsored ); /// @notice Emitted upon successful completion of fallback HyperEVM flow @@ -104,9 +94,9 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { address indexed finalRecipient, address indexed finalToken, // All amounts are in finalToken - uint256 evmAmountReceived, - uint256 evmAmountSponsored, - uint256 evmAmountTransferred + uint256 evmAmountIn, + uint256 bridgingFeesIncurred, + uint256 evmAmountSponsored ); /// @notice Emitted when a swap flow is initialized @@ -115,79 +105,48 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { address indexed finalRecipient, address indexed finalToken, // In baseToken - uint256 evmAmountReceived, - // Two below in finalToken - uint256 evmAmountSponsored, - uint64 targetCoreAmountToTransfer, - uint32 asset, - uint128 cloid + uint256 evmAmountIn, + uint256 bridgingFeesIncurred, + // In finalToken + uint256 coreAmountIn, + uint64 minAmountToSend, + uint64 maxAmountToSend ); - /// @notice Emitted upon successful completion of swap flow - event SwapFlowCompleted( + /// @notice Emitted when a swap flow is finalized + event SwapFlowFinalized( bytes32 indexed quoteNonce, address indexed finalRecipient, address indexed finalToken, - // Two below in finalToken - uint256 coreAmountSponsored, - uint64 coreAmountTransferred + // In finalToken + uint64 totalSent, + // In EVM finalToken + uint256 evmAmountSponsored ); - /// @notice Emitted upon cancelling a Limit order associated with an active swap flow - event CancelledLimitOrder(bytes32 indexed quoteNonce, uint32 indexed asset, uint128 indexed cloid); - - /// @notice Emitted upon submitting a new Limit order in place of a cancelled one - event ReplacedOldLimitOrder( - bytes32 indexed quoteNonce, - uint128 indexed cloid, - uint64 priceX1e8, - uint64 sizeX1e8, - uint64 oldPriceX1e8, - uint64 oldSizeX1e8Left - ); - - /// @notice Emitted when the replacing Limit order has better price than the old one - event BetterPricedLOSubmitted(bytes32 indexed quoteNonce, uint64 oldPriceX1e8, uint64 priceX1e8); - - /// @notice Emitted from the simple trasnfer flow when we fall back to HyperEVM flow because a non-sponsored transfer's recipient has no HyperCore account - event SimpleTransferFallbackAccountActivation(bytes32 indexed quoteNonce); - - /// @notice Emitted from the simple trasnfer flow when we fall back to HyperEVM flow because bridging would be unsafe - event SimpleTransferFallbackUnsafeToBridge(bytes32 indexed quoteNonce); + /// @notice Emitted upon cancelling a Limit order + event CancelledLimitOrder(address indexed token, uint128 indexed cloid); - /// @notice Emitted from the swap flow when we fall back to HyperEVM flow because a non-sponsored transfer's recipient has no HyperCore account - event SwapFlowFallbackAccountActivation(bytes32 indexed quoteNonce); + /// @notice Emitted upon cancelling a Limit order + event SubmittedLimitOrder(address indexed token, uint64 priceX1e8, uint64 sizeX1e8, uint128 indexed cloid); - /// @notice Emitted from the swap flow when falling back to the other flow becase the cost is to high compared to sponsored settings - event SwapFlowFallbackTooExpensive( - bytes32 indexed quoteNonce, - // Based on minimum out requirements for sponsored / non-sponsored flows - uint64 requiredCoreAmountToSponsor, - // Based on maxBpsToSponsor - uint64 maxCoreAmountToSponsor - ); - - /// @notice Emitted from the swap flow when falling back to the other flow becase donation box doesn't have enough funds to sponsor the flow - event SwapFlowFallbackDonationBox( + /// @notice Emitted when we have to fall back from the swap flow because it's too expensive (either to sponsor or the slippage is too big) + event SwapFlowTooExpensive( bytes32 indexed quoteNonce, address indexed finalToken, - uint256 totalEVMAmountToSponsor + uint256 estBpsSlippage, + uint256 maxAllowableBpsSlippage ); - /// @notice Emitted from the swap flow when falling back to the other flow becase bridging to core was unsafe (spot bridge didn't have enough funds) - event SwapFlowFallbackUnsafeToBridge( - bytes32 indexed quoteNonce, - bool initTokenUnsafe, - address indexed finalToken, - bool finalTokenUnsafe - ); + /// @notice Emitted when we can't bridge some token from HyperEVM to HyperCore + event UnsafeToBridge(bytes32 indexed quoteNonce, address indexed token, uint64 amount); /// @notice Emitted whenever donationBox funds are used for activating a user account event SponsoredAccountActivation( bytes32 indexed quoteNonce, address indexed finalRecipient, address indexed fundingToken, - uint256 evmAmount + uint256 evmAmountSponsored ); /// @notice Emitted whenever a new CoreTokenInfo is configured @@ -199,6 +158,32 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { uint64 bridgeSafetyBufferCore ); + /// @notice Emitted when we do an ad-hoc send of sponsorship funds to one of the Swap Handlers + event SentSponsorshipFundsToSwapHandler(address indexed token, uint256 evmAmountSponsored); + + /************************************** + * ERRORS * + **************************************/ + + /// @notice Thrown when an attempt to finalize a non-existing swap is made + error SwapDoesNotExist(); + + /// @notice Thrown when an attemp to finalize an already finalized swap is made + error SwapAlreadyFinalized(); + + /// @notice Thrown when trying to finalize a quoteNonce, calling a finalizeSwapFlows with an incorrect token + error WrongSwapFinalizationToken(bytes32 quoteNonce); + + /// @notice Emitted when the donation box is insufficient funds and we can't proceed. + error DonationBoxInsufficientFundsError(address token, uint256 amount); + + /// @notice Emitted when we're inside the sponsored flow and a user doesn't have a HyperCore account activated. The + /// bot should activate user's account first by calling `activateUserAccount` + error AccountNotActivatedError(address user); + + /// @notice Thrown when we can't bridge some token from HyperEVM to HyperCore + error UnsafeToBridgeError(address token, uint64 amount); + /************************************** * MODIFIERS * **************************************/ @@ -249,9 +234,6 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { */ constructor(address _donationBox, address _baseToken) { donationBox = DonationBox(_donationBox); - // Initialize this to 1 as to save 0 for special events when "no cloid is set" = no associated limit order - nextCloid = 1; - baseToken = _baseToken; // AccessControl setup @@ -368,10 +350,10 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { // Check account activation if (!HyperCoreLib.coreUserExists(params.finalRecipient)) { if (params.maxBpsToSponsor > 0) { - revert AccountNotActivated(params.finalRecipient); + revert AccountNotActivatedError(params.finalRecipient); } else { + emit AccountNotActivated(params.quoteNonce, params.finalRecipient); _fallbackHyperEVMFlow(params); - emit SimpleTransferFallbackAccountActivation(params.quoteNonce); return; } } @@ -387,7 +369,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { } if (amountToSponsor > 0) { - if (!_availableInDonationBox(coreTokenInfo.tokenInfo.evmContract, amountToSponsor)) { + if (!_availableInDonationBox(params.quoteNonce, coreTokenInfo.tokenInfo.evmContract, amountToSponsor)) { amountToSponsor = 0; } } @@ -414,14 +396,14 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { // If the amount is not safe to bridge because the bridge doesn't have enough liquidity, // fall back to sending user funds on HyperEVM. _fallbackHyperEVMFlow(params); - emit SimpleTransferFallbackUnsafeToBridge(params.quoteNonce); + emit UnsafeToBridge(params.quoteNonce, finalToken, quotedCoreAmount); return; } } if (amountToSponsor > 0) { // This will succeed because we checked the balance earlier - _getFromDonationBox(coreTokenInfo.tokenInfo.evmContract, amountToSponsor); + donationBox.withdraw(IERC20(coreTokenInfo.tokenInfo.evmContract), amountToSponsor); } cumulativeSponsoredAmount[finalToken] += amountToSponsor; @@ -441,256 +423,279 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { params.finalRecipient, finalToken, params.amountInEVM, - amountToSponsor, - quotedEvmAmount, - quotedCoreAmount + params.extraFeesIncurred, + amountToSponsor ); } - /// @notice initialized swap flow to eventually forward `finalToken` to the user, starting from `baseToken` (received - /// from a user bridge transaction) - function _initiateSwapFlow( - CommonFlowParams memory params, - // `maxUserSlippageBps` here means how much token user receives compared to a 1 to 1 - uint256 maxUserSlippageBps - ) internal { + /** + * @notice Initiates the swap flow. Sends the funds received on EVM side over to a SwapHandler corresponding to a + * finalToken. This is the first leg of the swap flow. Next, the bot should submit a limit order through a `submitLimitOrder` + * function, and then settle the flow via a `finalizeSwapFlows` function + * @dev Only works for stable -> stable swap flows (or equivalent token flows. Price between tokens is supposed to be approximately one to one) + * @param maxUserSlippageBps Describes a configured user setting. Slippage here is wrt the one to one exchange + */ + function _initiateSwapFlow(CommonFlowParams memory params, uint256 maxUserSlippageBps) internal { // Check account activation if (!HyperCoreLib.coreUserExists(params.finalRecipient)) { if (params.maxBpsToSponsor > 0) { - revert AccountNotActivated(params.finalRecipient); + revert AccountNotActivatedError(params.finalRecipient); } else { + emit AccountNotActivated(params.quoteNonce, params.finalRecipient); _fallbackHyperEVMFlow(params); - emit SwapFlowFallbackAccountActivation(params.quoteNonce); return; } } - FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(params.finalToken); address initialToken = baseToken; CoreTokenInfo memory initialCoreTokenInfo = coreTokenInfos[initialToken]; CoreTokenInfo memory finalCoreTokenInfo = coreTokenInfos[params.finalToken]; - uint64 tokensToSendCore; - uint64 guaranteedLOOut; - uint64 sizeX1e8; - uint64 finalCoreSendAmount; - uint64 totalCoreAmountToSponsor; - uint256 totalEVMAmountToSponsor; - { - // Calculate limit order amounts and check if feasible - uint64 minAllowableAmountToForwardCore; - { - // In initialToken - (, uint64 amountInEquivalentCore) = HyperCoreLib.maximumEVMSendAmountToAmounts( - params.amountInEVM, - initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals - ); - - // In finalToken - uint64 maxAmountToSponsorCore; - (minAllowableAmountToForwardCore, maxAmountToSponsorCore) = _calculateAllowableAmountsForFlow( - params.amountInEVM + params.extraFeesIncurred, - initialCoreTokenInfo, - finalCoreTokenInfo, - params.maxBpsToSponsor > 0, - params.maxBpsToSponsor, - maxUserSlippageBps - ); + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(params.finalToken); - uint64 limitPriceX1e8 = _getSuggestedPriceX1e8(finalTokenInfo); - (sizeX1e8, tokensToSendCore, guaranteedLOOut) = _calcLOAmounts( - amountInEquivalentCore, - limitPriceX1e8, - finalTokenInfo.isBuy, - finalTokenInfo.feePpm, - initialCoreTokenInfo, - finalCoreTokenInfo - ); + // Calculate limit order amounts and check if feasible + uint64 minAllowableAmountToForwardCore; + uint64 maxAllowableAmountToForwardCore; + // Estimated slippage in ppm, as compared to a one-to-one totalBridgedAmount -> finalAmount conversion + uint256 estSlippagePpm; + { + // In finalToken + (minAllowableAmountToForwardCore, maxAllowableAmountToForwardCore) = _calcAllowableAmtsSwapFlow( + params.amountInEVM, + params.extraFeesIncurred, + initialCoreTokenInfo, + finalCoreTokenInfo, + params.maxBpsToSponsor > 0, + maxUserSlippageBps + ); - if ( - minAllowableAmountToForwardCore > guaranteedLOOut && - minAllowableAmountToForwardCore - guaranteedLOOut > maxAmountToSponsorCore - ) { - // We can't provide the required slippage in a swap flow, try simple transfer flow instead - params.finalToken = initialToken; - _executeSimpleTransferFlow(params); - emit SwapFlowFallbackTooExpensive( - params.quoteNonce, - minAllowableAmountToForwardCore - guaranteedLOOut, - maxAmountToSponsorCore - ); - return; + uint64 approxExecutionPriceX1e8 = _getApproxRealizedPrice(finalTokenInfo); + uint256 maxAllowableBpsDeviation = params.maxBpsToSponsor > 0 ? params.maxBpsToSponsor : maxUserSlippageBps; + if (finalTokenInfo.isBuy) { + if (approxExecutionPriceX1e8 < ONEX1e8) { + estSlippagePpm = 0; + } else { + // ceil + estSlippagePpm = ((approxExecutionPriceX1e8 - ONEX1e8) * PPM_SCALAR + (ONEX1e8 - 1)) / ONEX1e8; } - } - - // Calculate sponsorship amounts - finalCoreSendAmount = params.maxBpsToSponsor > 0 ? minAllowableAmountToForwardCore : guaranteedLOOut; - totalCoreAmountToSponsor = finalCoreSendAmount > guaranteedLOOut - ? finalCoreSendAmount - guaranteedLOOut - : 0; - - totalEVMAmountToSponsor = 0; - if (totalCoreAmountToSponsor > 0) { - (totalEVMAmountToSponsor, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( - totalCoreAmountToSponsor, - finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals - ); - } - - if (totalEVMAmountToSponsor > 0) { - if (!_availableInDonationBox(params.finalToken, totalEVMAmountToSponsor)) { - // We can't provide the required `totalEVMAmountToSponsor` in a swap flow, try simple transfer flow instead - params.finalToken = initialToken; - _executeSimpleTransferFlow(params); - emit SwapFlowFallbackDonationBox(params.quoteNonce, params.finalToken, totalEVMAmountToSponsor); - return; + } else { + if (approxExecutionPriceX1e8 > ONEX1e8) { + estSlippagePpm = 0; + } else { + // ceil + estSlippagePpm = ((ONEX1e8 - approxExecutionPriceX1e8) * PPM_SCALAR + (ONEX1e8 - 1)) / ONEX1e8; } } + // Add `extraFeesIncurred` to "slippage from one to one" + estSlippagePpm += + (params.extraFeesIncurred * PPM_SCALAR + (params.amountInEVM + params.extraFeesIncurred) - 1) / + (params.amountInEVM + params.extraFeesIncurred); - // Check that we can safely bridge to HCore (for the trade amount actually needed) - bool isSafeToBridgeMainToken = HyperCoreLib.isCoreAmountSafeToBridge( - initialCoreTokenInfo.coreIndex, - tokensToSendCore, - initialCoreTokenInfo.bridgeSafetyBufferCore - ); - bool isSafeTobridgeSponsorshipFunds = HyperCoreLib.isCoreAmountSafeToBridge( - finalCoreTokenInfo.coreIndex, - totalCoreAmountToSponsor, - finalCoreTokenInfo.bridgeSafetyBufferCore - ); - - if (!isSafeToBridgeMainToken || !isSafeTobridgeSponsorshipFunds) { - params.finalToken = initialToken; - _fallbackHyperEVMFlow(params); - emit SwapFlowFallbackUnsafeToBridge( + if (estSlippagePpm > maxAllowableBpsDeviation * 10 ** (PPM_DECIMALS - BPS_DECIMALS)) { + emit SwapFlowTooExpensive( params.quoteNonce, - isSafeToBridgeMainToken, params.finalToken, - isSafeTobridgeSponsorshipFunds + (estSlippagePpm + 10 ** (PPM_DECIMALS - BPS_DECIMALS) - 1) / 10 ** (PPM_DECIMALS - BPS_DECIMALS), + maxAllowableBpsDeviation ); + params.finalToken = initialToken; + _executeSimpleTransferFlow(params); return; } } + (uint256 tokensToSendEvm, uint64 coreAmountIn) = HyperCoreLib.maximumEVMSendAmountToAmounts( + params.amountInEVM, + initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + + // Check that we can safely bridge to HCore (for the trade amount actually needed) + bool isSafeToBridgeMainToken = HyperCoreLib.isCoreAmountSafeToBridge( + initialCoreTokenInfo.coreIndex, + coreAmountIn, + initialCoreTokenInfo.bridgeSafetyBufferCore + ); + + if (!isSafeToBridgeMainToken) { + emit UnsafeToBridge(params.quoteNonce, initialToken, coreAmountIn); + params.finalToken = initialToken; + _fallbackHyperEVMFlow(params); + return; + } + // Finalize swap flow setup by updating state and funding SwapHandler // State changes - uint128 cloid = ++nextCloid; - pendingSwaps[params.quoteNonce] = PendingSwap({ + swaps[params.quoteNonce] = SwapFlowState({ finalRecipient: params.finalRecipient, finalToken: params.finalToken, - minCoreAmountFromLO: guaranteedLOOut, - sponsoredCoreAmountPreFunded: totalCoreAmountToSponsor, - limitOrderCloid: cloid + minAmountToSend: minAllowableAmountToForwardCore, + maxAmountToSend: maxAllowableAmountToForwardCore, + isSponsored: params.maxBpsToSponsor > 0, + finalized: false }); - pendingQueue[params.finalToken].push(params.quoteNonce); - cloidToQuoteNonce[cloid] = params.quoteNonce; - cumulativeSponsoredAmount[params.finalToken] += totalEVMAmountToSponsor; emit SwapFlowInitialized( params.quoteNonce, params.finalRecipient, params.finalToken, params.amountInEVM, - totalEVMAmountToSponsor, - finalCoreSendAmount, - finalTokenInfo.assetIndex, - cloid + params.extraFeesIncurred, + coreAmountIn, + minAllowableAmountToForwardCore, + maxAllowableAmountToForwardCore ); + // Send amount received form user to a corresponding SwapHandler SwapHandler swapHandler = finalTokenInfo.swapHandler; - // Interactions with external contracts - // 1. Fund SwapHandler @ core with `initialToken`: use it for the trade - // Always: evmToSendForTrade <= amountInEVM because of how it's calculated - (uint256 evmToSendForTrade, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( - tokensToSendCore, - initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals - ); - IERC20(initialToken).safeTransfer(address(swapHandler), evmToSendForTrade); + IERC20(initialToken).safeTransfer(address(swapHandler), tokensToSendEvm); swapHandler.transferFundsToSelfOnCore( initialToken, initialCoreTokenInfo.coreIndex, - evmToSendForTrade, + tokensToSendEvm, initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals ); - - // 2. Fund SwapHandler @ core with `finalToken`: use that for sponsorship - if (totalEVMAmountToSponsor > 0) { - // We checked that this amount is in donationBox before - _getFromDonationBox(params.finalToken, totalEVMAmountToSponsor); - // These funds just came from donationBox - IERC20(params.finalToken).safeTransfer(address(swapHandler), totalEVMAmountToSponsor); - swapHandler.transferFundsToSelfOnCore( - params.finalToken, - finalCoreTokenInfo.coreIndex, - totalEVMAmountToSponsor, - finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals - ); - } - - swapHandler.submitLimitOrder(finalTokenInfo, _getSuggestedPriceX1e8(finalTokenInfo), sizeX1e8, cloid); } - /// @notice Finalizes pending queue of swaps for `finalToken` if a corresponding SwapHandler has enough balance - function finalizePendingSwaps( + /** + * @notice Finalizes multiple swap flows associated with a final token, subject to the L1 Hyperliquid balance + * @dev Caller is responsible for providing correct limitOrderOutput amounts per assosicated swap flow. The caller + * has to estimate how much final tokens it received on core based on the input of the corresponding quote nonce + * swap flow + */ + function finalizeSwapFlows( address finalToken, - uint256 maxSwapCountToFinalize - ) - external - nonReentrant - returns (uint256 finalizedSwapsCount, uint256 finalizedSwapsAmount, uint256 totalPendingSwapsRemaining) - { - FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); - CoreTokenInfo memory coreTokenInfo = coreTokenInfos[finalToken]; + bytes32[] calldata quoteNonces, + uint64[] calldata limitOrderOuts + ) external onlyPermissionedBot returns (uint256 finalized) { + require(quoteNonces.length == limitOrderOuts.length, "length"); + require(lastPullFundsBlock[finalToken] < block.number, "too soon"); - require(lastPullFundsBlock[finalToken] < block.number, "Can't finalize twice in the same block"); + CoreTokenInfo memory finalCoreTokenInfo = _getExistingCoreTokenInfo(finalToken); + FinalTokenInfo memory finalTokenInfo = finalTokenInfos[finalToken]; - uint256 head = pendingQueueHead[finalToken]; - bytes32[] storage queue = pendingQueue[finalToken]; - if (head >= queue.length) return (0, 0, 0); - if (maxSwapCountToFinalize == 0) return (0, 0, queue.length - head); + uint64 availableBalance = HyperCoreLib.spotBalance( + address(finalTokenInfo.swapHandler), + finalCoreTokenInfo.coreIndex + ); + uint64 totalAdditionalToSend = 0; + for (; finalized < quoteNonces.length; ++finalized) { + bool success; + uint64 additionalToSend; + (success, additionalToSend, availableBalance) = _finalizeSingleSwap( + quoteNonces[finalized], + limitOrderOuts[finalized], + finalCoreTokenInfo, + availableBalance + ); + if (!success) { + break; + } + totalAdditionalToSend += additionalToSend; + } - // Note: `availableCore` is the SwapHandler's Core balance for `finalToken`, which monotonically increases - uint64 availableCore = HyperCoreLib.spotBalance(address(finalTokenInfo.swapHandler), coreTokenInfo.coreIndex); + if (finalized > 0) { + lastPullFundsBlock[finalToken] = block.number; + } else { + return 0; + } - while (head < queue.length && finalizedSwapsCount < maxSwapCountToFinalize) { - bytes32 nonce = queue[head]; + if (totalAdditionalToSend > 0) { + (uint256 totalAdditionalToSendEVM, uint64 totalAdditionalReceivedCore) = HyperCoreLib + .minimumCoreReceiveAmountToAmounts( + totalAdditionalToSend, + finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); - PendingSwap storage pendingSwap = pendingSwaps[nonce]; - uint64 totalAmountToForwardToUser = pendingSwap.minCoreAmountFromLO + - pendingSwap.sponsoredCoreAmountPreFunded; - if (availableCore < totalAmountToForwardToUser) { - break; + if ( + !HyperCoreLib.isCoreAmountSafeToBridge( + finalCoreTokenInfo.coreIndex, + totalAdditionalReceivedCore, + finalCoreTokenInfo.bridgeSafetyBufferCore + ) + ) { + // We expect this situation to be so rare and / or intermittend that we're willing to rely on admin to sweep the funds if this leads to + // swaps being impossible to finalize + revert UnsafeToBridgeError(finalCoreTokenInfo.tokenInfo.evmContract, totalAdditionalToSend); } - finalTokenInfo.swapHandler.transferFundsToUserOnCore( - finalTokenInfo.assetIndex, - pendingSwap.finalRecipient, - totalAmountToForwardToUser - ); + cumulativeSponsoredAmount[finalToken] += totalAdditionalToSendEVM; - emit SwapFlowCompleted( - nonce, - pendingSwap.finalRecipient, - pendingSwap.finalToken, - pendingSwap.sponsoredCoreAmountPreFunded, - totalAmountToForwardToUser + // ! Notice: as per HyperEVM <> HyperCore rules, this amount will land on HyperCore *before* all of the core > core sends get executed + // Get additional amount to send from donation box, and send it to self on core + donationBox.withdraw(IERC20(finalToken), totalAdditionalToSendEVM); + IERC20(finalToken).safeTransfer(address(finalTokenInfo.swapHandler), totalAdditionalToSendEVM); + finalTokenInfo.swapHandler.transferFundsToSelfOnCore( + finalToken, + finalCoreTokenInfo.coreIndex, + totalAdditionalToSendEVM, + finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals ); + } + } - availableCore -= totalAmountToForwardToUser; + /// @notice Finalizes a single swap flow, sending the tokens to user on core. Relies on caller to send the `additionalToSend` + function _finalizeSingleSwap( + bytes32 quoteNonce, + uint64 limitOrderOut, + CoreTokenInfo memory finalCoreTokenInfo, + uint64 availableBalance + ) internal returns (bool success, uint64 additionalToSend, uint64 balanceRemaining) { + SwapFlowState storage swap = swaps[quoteNonce]; + if (swap.finalRecipient == address(0)) revert SwapDoesNotExist(); + if (swap.finalized) revert SwapAlreadyFinalized(); + if (swap.finalToken != finalCoreTokenInfo.tokenInfo.evmContract) revert WrongSwapFinalizationToken(quoteNonce); + + uint64 totalToSend; + (totalToSend, additionalToSend) = _calcSwapFlowSendAmounts( + limitOrderOut, + swap.minAmountToSend, + swap.maxAmountToSend, + swap.isSponsored + ); - // We don't delete `pendingSwaps` state, because we might require it for accounting purposes if we need to - // update the associated limit order - head += 1; - finalizedSwapsCount += 1; - finalizedSwapsAmount += totalAmountToForwardToUser; + // `additionalToSend` will land on HCore before this core > core send will need to be executed + balanceRemaining = availableBalance + additionalToSend; + if (totalToSend > balanceRemaining) { + return (false, 0, availableBalance); } - pendingQueueHead[finalToken] = head; + swap.finalized = true; + success = true; + balanceRemaining -= totalToSend; - if (finalizedSwapsCount > 0) { - lastPullFundsBlock[finalToken] = block.number; - } + (uint256 additionalToSendEVM, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + additionalToSend, + finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); - return (finalizedSwapsCount, finalizedSwapsAmount, queue.length - head); + HyperCoreLib.transferERC20CoreToCore(finalCoreTokenInfo.coreIndex, swap.finalRecipient, totalToSend); + emit SwapFlowFinalized(quoteNonce, swap.finalRecipient, swap.finalToken, totalToSend, additionalToSendEVM); + } + + /// @notice Forwards `amount` plus potential sponsorship funds (for bridging fee) to user on HyperEVM + function _fallbackHyperEVMFlow(CommonFlowParams memory params) internal virtual { + uint256 maxEvmAmountToSponsor = ((params.amountInEVM + params.extraFeesIncurred) * params.maxBpsToSponsor) / + BPS_SCALAR; + uint256 sponsorshipFundsToForward = params.extraFeesIncurred > maxEvmAmountToSponsor + ? maxEvmAmountToSponsor + : params.extraFeesIncurred; + + if (!_availableInDonationBox(params.quoteNonce, params.finalToken, sponsorshipFundsToForward)) { + sponsorshipFundsToForward = 0; + } + if (sponsorshipFundsToForward > 0) { + donationBox.withdraw(IERC20(params.finalToken), sponsorshipFundsToForward); + } + uint256 totalAmountToForward = params.amountInEVM + sponsorshipFundsToForward; + IERC20(params.finalToken).safeTransfer(params.finalRecipient, totalAmountToForward); + cumulativeSponsoredAmount[params.finalToken] += sponsorshipFundsToForward; + emit FallbackHyperEVMFlowCompleted( + params.quoteNonce, + params.finalRecipient, + params.finalToken, + params.amountInEVM, + params.extraFeesIncurred, + sponsorshipFundsToForward + ); } /** @@ -718,7 +723,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { cumulativeSponsoredActivationFee[fundingToken] += activationFeeEvm; // donationBox @ evm -> Handler @ evm - _getFromDonationBox(fundingToken, activationFeeEvm); + donationBox.withdraw(IERC20(fundingToken), activationFeeEvm); // Handler @ evm -> Handler @ core -> finalRecipient @ core HyperCoreLib.transferERC20EVMToCore( fundingToken, @@ -733,161 +738,23 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { /// @notice Cancells a pending limit order by `cloid` with an intention to submit a new limit order in its place. To /// be used for stale limit orders to speed up executing user transactions - function cancelLimitOrderByCloid( - uint128 cloid - ) external nonReentrant onlyPermissionedBot returns (bytes32 quoteNonce) { - quoteNonce = cloidToQuoteNonce[cloid]; - PendingSwap storage pendingSwap = pendingSwaps[quoteNonce]; - // A pending swap was enqueued with this Final token, so it had to be set. Unsetting the final token config is not - // a valid configuration action so we can rely on finalToken being set here - FinalTokenInfo memory finalTokenInfo = finalTokenInfos[pendingSwap.finalToken]; - - // Here, cloid == pendingSwap.limitOrderCloid + function cancelLimitOrderByCloid(address finalToken, uint128 cloid) external nonReentrant onlyPermissionedBot { + FinalTokenInfo memory finalTokenInfo = finalTokenInfos[finalToken]; finalTokenInfo.swapHandler.cancelOrderByCloid(finalTokenInfo.assetIndex, cloid); - // Clear out the cloid. `submitUpdatedLimitOrder` function requires that this is empty. Means that no tracked - // associated limit order is present - delete pendingSwap.limitOrderCloid; - emit CancelledLimitOrder(quoteNonce, finalTokenInfo.assetIndex, cloid); + emit CancelledLimitOrder(finalToken, cloid); } - /** - * @notice This function is to be used in situations when the limit order that's on the books has become stale and - * we want to speed up the execution. - * @dev This function should be called as a second step after the `cancelLimitOrderByCloid` was already called and - * the order was fully cancelled. It is the responsibility of this function's caller to supply oldPriceX1e8 and - * oldSizeX1e8Left associated with the previous cancelled order. They act as a safeguarding policy that don't allow - * to spend more tokens then the previous limit order wanted to spend to protect the accounting assumptions of the - * current contract. Although the values provided as still fully trusted. - * @dev This functions chooses to ignore the `maxBpsToSponsor` param as it is supposed to be - * used only in rare cases. We choose to pay for the adjusted limit order price completely. - * @param quoteNonce quote nonce is used to uniquely identify user order (PendingSwap) - * @param priceX1e8 price to set for new limit order - * @param oldPriceX1e8 price that was set for the cancelled order - * @param oldSizeX1e8Left size that was remaining on the order that was cancelled - */ - function submitUpdatedLimitOrder( - // old order with some price and some out amount expectations: pendingSwaps (minAmountOutCore, totalSponsoredCore) - // new order with some price and new amount expectations. minAmountOutCore2, totalSponsoredCore + (minAmountOutCore2 - minAmountOutCore) - // oldPriceX1e8, oldSizeX1e8Left -> partial Limit order that we cancelled - // how much tokens that we sent in are still there for us to trade? - // priceX1e8: sz ? - // partialBudgetRemaining - bytes32 quoteNonce, + function submitLimitOrderFromBot( + address finalToken, uint64 priceX1e8, - uint64 oldPriceX1e8, - uint64 oldSizeX1e8Left + uint64 sizeX1e8, + uint128 cloid ) external nonReentrant onlyPermissionedBot { - PendingSwap storage pendingSwap = pendingSwaps[quoteNonce]; - require(pendingSwap.limitOrderCloid == 0, "Cannot resubmit LO for non-empty cloid"); - - address finalToken = pendingSwap.finalToken; FinalTokenInfo memory finalTokenInfo = finalTokenInfos[finalToken]; - CoreTokenInfo memory initialTokenInfo = coreTokenInfos[baseToken]; - CoreTokenInfo memory finalTokenCoreInfo = coreTokenInfos[finalToken]; - - // Remaining budget of tokens attributable to the "old limit order" (now cancelled) - uint64 coreBudgetRemaining = _calcRemainingLOBudget( - oldPriceX1e8, - oldSizeX1e8Left, - finalTokenInfo.isBuy, - finalTokenInfo.feePpm, - initialTokenInfo, - finalTokenCoreInfo - ); - (uint64 szX1e8, , uint64 guaranteedCoreOut) = _calcLOAmounts( - coreBudgetRemaining, - priceX1e8, - finalTokenInfo.isBuy, - finalTokenInfo.feePpm, - initialTokenInfo, - finalTokenCoreInfo - ); - - (, , uint64 guaranteedCoreOutOld) = _calcLOAmounts( - coreBudgetRemaining, - oldPriceX1e8, - finalTokenInfo.isBuy, - finalTokenInfo.feePpm, - initialTokenInfo, - finalTokenCoreInfo - ); - - uint64 sponsorDeltaCore; - if (guaranteedCoreOut < guaranteedCoreOutOld) { - sponsorDeltaCore = guaranteedCoreOutOld - guaranteedCoreOut; - } else { - sponsorDeltaCore = 0; - emit BetterPricedLOSubmitted(quoteNonce, oldPriceX1e8, priceX1e8); - } - - // Submit new Limit Order - uint128 cloid = ++nextCloid; - SwapHandler swapHandler = finalTokenInfo.swapHandler; - swapHandler.submitLimitOrder(finalTokenInfo, priceX1e8, szX1e8, cloid); - pendingSwap.limitOrderCloid = cloid; - - // Send extra sponsorship money to cover for the guaranteed amount out difference - if (sponsorDeltaCore > 0) { - uint256 sponsorDeltaEvm; - (sponsorDeltaEvm, sponsorDeltaCore) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( - sponsorDeltaCore, - finalTokenCoreInfo.tokenInfo.evmExtraWeiDecimals - ); + finalTokenInfo.swapHandler.submitLimitOrder(finalTokenInfo, priceX1e8, sizeX1e8, cloid); - _getFromDonationBox(finalToken, sponsorDeltaEvm); - IERC20(finalToken).safeTransfer(address(swapHandler), sponsorDeltaEvm); - cumulativeSponsoredAmount[finalToken] += sponsorDeltaEvm; - if ( - !HyperCoreLib.isCoreAmountSafeToBridge( - finalTokenCoreInfo.coreIndex, - sponsorDeltaCore, - finalTokenCoreInfo.bridgeSafetyBufferCore - ) - ) { - // Can't add required sponsored funds to balance out the accounting. Have to revert - revert("Bridging is unsafe"); - } - swapHandler.transferFundsToSelfOnCore( - finalToken, - finalTokenCoreInfo.coreIndex, - sponsorDeltaEvm, - finalTokenCoreInfo.tokenInfo.evmExtraWeiDecimals - ); - - uint64 fullOldGuranteedOut = pendingSwap.minCoreAmountFromLO; - pendingSwap.minCoreAmountFromLO = fullOldGuranteedOut + guaranteedCoreOut - guaranteedCoreOutOld; - pendingSwap.sponsoredCoreAmountPreFunded = pendingSwap.sponsoredCoreAmountPreFunded + sponsorDeltaCore; - } - - emit ReplacedOldLimitOrder(quoteNonce, cloid, priceX1e8, szX1e8, oldPriceX1e8, oldSizeX1e8Left); - } - - /// @notice Forwards `amount` plus potential sponsorship funds (for bridging fee) to user on HyperEVM - function _fallbackHyperEVMFlow(CommonFlowParams memory params) internal virtual { - uint256 maxEvmAmountToSponsor = ((params.amountInEVM + params.extraFeesIncurred) * params.maxBpsToSponsor) / - BPS_SCALAR; - uint256 sponsorshipFundsToForward = params.extraFeesIncurred > maxEvmAmountToSponsor - ? maxEvmAmountToSponsor - : params.extraFeesIncurred; - - if (!_availableInDonationBox(params.finalToken, sponsorshipFundsToForward)) { - sponsorshipFundsToForward = 0; - } - if (sponsorshipFundsToForward > 0) { - _getFromDonationBox(params.finalToken, sponsorshipFundsToForward); - } - uint256 totalAmountToForward = params.amountInEVM + sponsorshipFundsToForward; - IERC20(params.finalToken).safeTransfer(params.finalRecipient, totalAmountToForward); - cumulativeSponsoredAmount[params.finalToken] += sponsorshipFundsToForward; - emit FallbackHyperEVMFlowCompleted( - params.quoteNonce, - params.finalRecipient, - params.finalToken, - params.amountInEVM, - sponsorshipFundsToForward, - totalAmountToForward - ); + emit SubmittedLimitOrder(finalToken, priceX1e8, sizeX1e8, cloid); } function _setCoreTokenInfo( @@ -923,37 +790,64 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { ); } - /// @notice Gets `amount` of `token` from donationBox. Reverts if unsuccessful - function _getFromDonationBox(address token, uint256 amount) internal { - if (!_availableInDonationBox(token, amount)) { - revert DonationBoxInsufficientFundsError(token, amount); + /** + * @notice Used for ad-hoc sends of sponsorship funds to associated SwapHandler @ HyperCore + * @param token The final token for which we want to fund the SwapHandler + */ + function sendSponsorshipFundsToSwapHandler(address token, uint256 amount) external onlyPermissionedBot { + CoreTokenInfo memory coreTokenInfo = _getExistingCoreTokenInfo(token); + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(token); + (uint256 amountEVMToSend, uint64 amountCoreToReceive) = HyperCoreLib.maximumEVMSendAmountToAmounts( + amount, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + if ( + !HyperCoreLib.isCoreAmountSafeToBridge( + coreTokenInfo.coreIndex, + amountCoreToReceive, + coreTokenInfo.bridgeSafetyBufferCore + ) + ) { + revert UnsafeToBridgeError(token, amountCoreToReceive); } - donationBox.withdraw(IERC20(token), amount); + + cumulativeSponsoredAmount[token] += amountEVMToSend; + + emit SentSponsorshipFundsToSwapHandler(token, amountEVMToSend); + + donationBox.withdraw(IERC20(token), amountEVMToSend); + IERC20(token).safeTransfer(address(finalTokenInfo.swapHandler), amountEVMToSend); + finalTokenInfo.swapHandler.transferFundsToSelfOnCore( + token, + coreTokenInfo.coreIndex, + amountEVMToSend, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); } /// @notice Checks if `amount` of `token` is available to withdraw from donationBox - function _availableInDonationBox(address token, uint256 amount) internal returns (bool available) { - available = IERC20(token).balanceOf(address(donationBox)) >= amount; + function _availableInDonationBox( + bytes32 quoteNonce, + address token, + uint256 amount + ) internal returns (bool available) { + uint256 balance = IERC20(token).balanceOf(address(donationBox)); + available = balance >= amount; if (!available) { - emit DonationBoxInsufficientFunds(token, amount); + emit DonationBoxInsufficientFunds(quoteNonce, token, amount, balance); } } - function _getAccountActivationFeeEVM(address token, address recipient) internal view returns (uint256) { - bool accountActivated = HyperCoreLib.coreUserExists(recipient); - return accountActivated ? 0 : coreTokenInfos[token].accountActivationFeeEVM; - } - - function _calculateAllowableAmountsForFlow( - uint256 totalAmountBridgedEVM, + function _calcAllowableAmtsSwapFlow( + uint256 amount, + uint256 extraFeesIncurred, CoreTokenInfo memory initialCoreTokenInfo, CoreTokenInfo memory finalCoreTokenInfo, bool isSponsoredFlow, - uint256 maxBpsToSponsor, uint256 maxUserSlippageBps - ) internal pure returns (uint64 minAllowableAmountToForwardCore, uint64 maxAmountToSponsorCore) { + ) internal pure returns (uint64 minAllowableAmountToForwardCore, uint64 maxAllowableAmountToForwardCore) { (, uint64 feelessAmountCoreInitialToken) = HyperCoreLib.maximumEVMSendAmountToAmounts( - totalAmountBridgedEVM, + amount + extraFeesIncurred, initialCoreTokenInfo.tokenInfo.evmExtraWeiDecimals ); uint64 feelessAmountCoreFinalToken = HyperCoreLib.convertCoreDecimalsSimple( @@ -962,16 +856,58 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { finalCoreTokenInfo.tokenInfo.weiDecimals ); if (isSponsoredFlow) { - maxAmountToSponsorCore = uint64((feelessAmountCoreFinalToken * maxBpsToSponsor) / BPS_SCALAR); minAllowableAmountToForwardCore = feelessAmountCoreFinalToken; + maxAllowableAmountToForwardCore = feelessAmountCoreFinalToken; } else { - maxAmountToSponsorCore = 0; minAllowableAmountToForwardCore = uint64( (feelessAmountCoreFinalToken * (BPS_SCALAR - maxUserSlippageBps)) / BPS_SCALAR ); + // Maximum allowable amount to forward is a one-to-one equivalent of `amount` + maxAllowableAmountToForwardCore = uint64( + (feelessAmountCoreFinalToken * amount) / (amount + extraFeesIncurred) + ); } } + /** + * @return totalToSend What we will forward to user on HCore + * @return additionalToSend What we will send from donationBox right now + */ + function _calcSwapFlowSendAmounts( + uint64 limitOrderOut, + uint64 minAmountToSend, + uint64 maxAmountToSend, + bool isSponsored + ) internal pure returns (uint64 totalToSend, uint64 additionalToSend) { + // What we will send from donationBox right now + // What we will forward to user on HCore + if (limitOrderOut >= maxAmountToSend || isSponsored) { + totalToSend = maxAmountToSend; + } else { + if (limitOrderOut < minAmountToSend) { + additionalToSend = minAmountToSend - limitOrderOut; + } + + // Give user a fair deal, which is the max of: + // - limitOrderOut + // - minAmountToSend + totalToSend = limitOrderOut + additionalToSend; + } + } + + /// @notice Reads the current spot price from HyperLiquid and applies a configured suggested discount for faster execution + /// @dev Includes HyperLiquid fees + function _getApproxRealizedPrice( + FinalTokenInfo memory finalTokenInfo + ) internal view returns (uint64 limitPriceX1e8) { + uint64 spotX1e8 = HyperCoreLib.spotPx(finalTokenInfo.assetIndex); + // Buy above spot, sell below spot + uint256 adjPpm = finalTokenInfo.isBuy + ? (PPM_SCALAR + finalTokenInfo.suggestedDiscountBps * 10 ** 2 + finalTokenInfo.feePpm) + : (PPM_SCALAR - finalTokenInfo.suggestedDiscountBps * 10 ** 2 - finalTokenInfo.feePpm); + limitPriceX1e8 = uint64((uint256(spotX1e8) * adjPpm) / PPM_SCALAR); + } + /************************************** * SWEEP FUNCTIONS * **************************************/ @@ -981,7 +917,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { } function sweepErc20FromDonationBox(address token, uint256 amount) external nonReentrant onlyFundsSweeper { - _getFromDonationBox(token, amount); + donationBox.withdraw(IERC20(token), amount); IERC20(token).safeTransfer(msg.sender, amount); } @@ -995,14 +931,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { HyperCoreLib.transferERC20CoreToCore(coreTokenInfos[token].coreIndex, msg.sender, amount); } - // TODO? Alternative flow: make this permissionless, send money SwapHandler @ core -> DonationBox @ core => DonationBox pulls money from Core to Self (needs DonationBox code change) - function sweepOnCoreFromSwapHandler(address token, uint64 amount) external nonReentrant onlyPermissionedBot { - // We first want to make sure there are not pending limit orders for this token - uint256 head = pendingQueueHead[token]; - if (head < pendingQueue[token].length) { - revert("Cannot sweep on core if there are pending limit orders"); - } - + function sweepOnCoreFromSwapHandler(address token, uint64 amount) external nonReentrant onlyDefaultAdmin { // Prevent pulling fantom funds (e.g. if finalizePendingSwaps reads stale balance because of this fund pull) require(lastPullFundsBlock[token] < block.number, "Can't pull funds twice in the same block"); lastPullFundsBlock[token] = block.number; @@ -1010,157 +939,4 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { SwapHandler swapHandler = finalTokenInfos[token].swapHandler; swapHandler.transferFundsToUserOnCore(finalTokenInfos[token].assetIndex, msg.sender, amount); } - - /// @notice Reads the current spot price from HyperLiquid and applies a configured suggested discount for faster execution - function _getSuggestedPriceX1e8( - FinalTokenInfo memory finalTokenInfo - ) internal view returns (uint64 limitPriceX1e8) { - uint64 spotX1e8 = HyperCoreLib.spotPx(finalTokenInfo.assetIndex); - // Buy above spot, sell below spot - uint256 adjBps = finalTokenInfo.isBuy - ? (BPS_SCALAR + finalTokenInfo.suggestedDiscountBps) - : (BPS_SCALAR - finalTokenInfo.suggestedDiscountBps); - limitPriceX1e8 = uint64((uint256(spotX1e8) * adjBps) / BPS_SCALAR); - } - - /************************************** - * LIMIT ORDER CALCULATION UTILS * - **************************************/ - - /// @notice Given the size and price of a limit order, returns the remaining `budget` that Limit order expects to spend - function _calcRemainingLOBudget( - uint64 pxX1e8, - uint64 szX1e8, - bool isBuy, - uint64 feePpm, - CoreTokenInfo memory tokenHave, - CoreTokenInfo memory tokenWant - ) internal pure returns (uint64 budget) { - CoreTokenInfo memory _quoteToken = isBuy ? tokenHave : tokenWant; - CoreTokenInfo memory _baseToken = isBuy ? tokenWant : tokenHave; - - if (isBuy) { - // We have quoteTokens. Estimate how many quoteTokens we are GUARANTEED to have had to enqueue the LO in the first place (proportional) - // qTR is quote tokens real. qTD quote token decimals. - // szX1e8 * pxX1e8 / 10 ** 8 = qTX1e8Net - // qTR * 10 ** 8 * (10 ** 6 - feePpm) / (10 ** 6 * 10 ** qTD) = qTX1e8Net - // qTR = szX1e8 * pxX1e8 * 10 ** 6 * 10 ** qTD / (10 ** 8 * 10 ** 8 * (10 ** 6 - feePpm)) - budget = uint64( - (uint256(szX1e8) * uint256(pxX1e8) * PPM_SCALAR * 10 ** (_quoteToken.tokenInfo.weiDecimals)) / - (10 ** 16 * (PPM_SCALAR - feePpm)) - ); - } else { - // We have baseTokens. Convert `szX1e8` to base token budget. A simple decimals conversion here - budget = uint64((szX1e8 * 10 ** (_baseToken.tokenInfo.weiDecimals)) / 10 ** 8); - } - } - - /** - * @notice The purpose of this function is best described by its return params. Given a budget and a price, determines - * size to set, tokens to send, and min amount received. - * @return szX1e8 size value to supply when sending a limit order to HyperCore - * @return coreToSend the number of tokens to send for this trade to suceed; <= coreBudget - * @return guaranteedCoreOut the ABSOLUTE MINIMUM that we're guaranteed to receive when the limit order fully settles - */ - function _calcLOAmounts( - uint64 coreBudget, - uint64 pxX1e8, - bool isBuy, - uint64 feePpm, - CoreTokenInfo memory tokenHave, - CoreTokenInfo memory tokenWant - ) internal pure returns (uint64 szX1e8, uint64 coreToSend, uint64 guaranteedCoreOut) { - if (isBuy) { - return - _calcLOAmountsBuy( - coreBudget, - pxX1e8, - tokenHave.tokenInfo.weiDecimals, - tokenHave.tokenInfo.szDecimals, - tokenWant.tokenInfo.weiDecimals, - tokenWant.tokenInfo.szDecimals, - feePpm - ); - } else { - return - _calcLOAmountsSell( - coreBudget, - pxX1e8, - tokenWant.tokenInfo.weiDecimals, - tokenWant.tokenInfo.szDecimals, - tokenHave.tokenInfo.weiDecimals, - tokenHave.tokenInfo.szDecimals, - feePpm - ); - } - } - - /** - * @notice Given the quote budget and the price, this function calculates the size of the buy limit order to set - * as well as the minimum amount of out token to expect. This calculation is based on the HIP-1 spot trading formula. - * Source: https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-1-native-token-standard#spot-trading - * @param quoteBudget The budget of the quote in base token. - * @param pxX1e8 The price of the quote token in base token. - * @param quoteD The decimals of the quote token. - * @param quoteSz The size decimals of the quote token. - * @param baseD The decimals of the base token. - * @param baseSz The size decimals of the base token. - * @param feePpm The fee in ppm that is applied to the quote. - * @return szX1e8 The size of the limit order to set. - * @return tokensToSendCore The number of tokens to send for this trade to suceed. - * @return minAmountOutCore The minimum amount of out token to expect. - */ - function _calcLOAmountsBuy( - uint64 quoteBudget, - uint64 pxX1e8, - uint8 quoteD, - uint8 quoteSz, - uint8 baseD, - uint8 baseSz, - uint64 feePpm - ) internal pure returns (uint64 szX1e8, uint64 tokensToSendCore, uint64 minAmountOutCore) { - uint256 px = (pxX1e8 * 10 ** (PX_D + quoteSz)) / 10 ** (8 + baseSz); - // quoteD >= quoteSz always - uint256 sz = (quoteBudget * (PPM_SCALAR - feePpm) * 10 ** PX_D) / (PPM_SCALAR * px * 10 ** (quoteD - quoteSz)); - // baseD >= baseSz always - uint64 outBaseNet = uint64(sz * 10 ** (baseD - baseSz)); - szX1e8 = uint64((uint256(outBaseNet) * 10 ** 8) / 10 ** baseD); - tokensToSendCore = quoteBudget; - minAmountOutCore = outBaseNet; - } - - /** - * @notice Given the quote budget and the price, this function calculates the size of the sell limit order to set - * as well as the minimum amount of out token to expect. This calculation is based on the HIP-1 spot trading formula. - * Source: https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-1-native-token-standard#spot-trading - * @param baseBudget The budget of the quote in base token. - * @param pxX1e8 The price of the quote token in base token. - * @param quoteD The decimals of the quote token. - * @param quoteSz The size decimals of the quote token. - * @param baseD The decimals of the base token. - * @param baseSz The size decimals of the base token. - * @param feePpm The fee in ppm that is applied to the quote. - * @return szX1e8 The size of the limit order to set. - * @return tokensToSendCore The number of tokens to send for this trade to suceed. - * @return minAmountOutCore The minimum amount of out token to expect. - */ - function _calcLOAmountsSell( - uint64 baseBudget, - uint64 pxX1e8, - uint8 quoteD, - uint8 quoteSz, - uint8 baseD, - uint8 baseSz, - uint64 feePpm - ) internal pure returns (uint64 szX1e8, uint64 tokensToSendCore, uint64 minAmountOutCore) { - uint64 sz = uint64(baseBudget / 10 ** (baseD - baseSz)); - uint256 px = (pxX1e8 * 10 ** (PX_D + quoteSz)) / 10 ** (8 + baseSz); - - // quoteD >= quoteSz always - uint64 outQuoteGross = uint64((px * sz * 10 ** (quoteD - quoteSz)) / 10 ** PX_D); - uint64 outQuoteNet = uint64((outQuoteGross * (PPM_SCALAR - feePpm)) / PPM_SCALAR); - szX1e8 = uint64((sz * 10 ** 8) / 10 ** baseSz); - tokensToSendCore = baseBudget; - minAmountOutCore = outQuoteNet; - } } diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol index f020886c8..d71a6c683 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol @@ -116,8 +116,6 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu (quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToCore) || quote.executionMode == uint8(ExecutionMode.ArbitraryActionsToEVM)) ) { - commonParams.finalToken = quote.finalToken.toAddress(); - // Execute flow with arbitrary evm actions _executeWithEVMFlow( EVMFlowParams({ @@ -131,16 +129,6 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu // Execute standard HyperCore flow (default) HyperCoreFlowExecutor._executeFlow(commonParams, quote.maxUserSlippageBps); } - - emit SponsoredMintAndWithdraw( - quote.nonce, - quote.finalRecipient, - quote.finalToken, - quote.amount, - quote.deadline, - quote.maxBpsToSponsor, - quote.maxUserSlippageBps - ); } function _isQuoteValid( From ec9bd791ea7fd59457ad315e8a2f8fb3765059e3 Mon Sep 17 00:00:00 2001 From: Ihor Farion <65650773+grasphoper@users.noreply.github.com> Date: Thu, 23 Oct 2025 07:11:27 -0700 Subject: [PATCH 29/47] add Bytes from OZ (#1160) --- contracts/external/libraries/BytesLib.sol | 36 +++-------------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/contracts/external/libraries/BytesLib.sol b/contracts/external/libraries/BytesLib.sol index 31777de49..483a1c9bd 100644 --- a/contracts/external/libraries/BytesLib.sol +++ b/contracts/external/libraries/BytesLib.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; +import { Bytes } from "@openzeppelin/contracts-v5/utils/Bytes.sol"; + library BytesLib { /************************************** * ERRORS * @@ -85,43 +87,13 @@ library BytesLib { /** * @notice Reads a bytes array from a bytes array at a given start index and length - * Source: https://github.com/Vectorized/solady/blob/21202175063a0010826bf42697b5aa2ff0c27f9f/src/utils/LibBytes.sol#L369C5-L396C6 - * Code was copied, was not modified + * Source: OpenZeppelin Contracts v5 (utils/Bytes.sol) * @param _bytes The bytes array to convert * @param _start The start index of the bytes array * @param _end The end index of the bytes array * @return result The bytes array result */ function slice(bytes memory _bytes, uint256 _start, uint256 _end) internal pure returns (bytes memory result) { - // solhint-disable-next-line no-inline-assembly - assembly { - let l := mload(_bytes) // _bytes length. - if iszero(gt(l, _end)) { - _end := l - } - if iszero(gt(l, _start)) { - _start := l - } - if lt(_start, _end) { - result := mload(0x40) - let n := sub(_end, _start) - let i := add(_bytes, _start) - let w := not(0x1f) - // Copy the `_bytes` one word at a time, backwards. - for { - let j := and(add(n, 0x1f), w) - } 1 {} { - mstore(add(result, j), mload(add(i, j))) - j := add(j, w) // `sub(j, 0x20)`. - if iszero(j) { - break - } - } - let o := add(add(result, 0x20), n) - mstore(o, 0) // Zeroize the slot after the bytes. - mstore(0x40, add(o, 0x20)) // Allocate memory. - mstore(result, n) // Store the length. - } - } + return Bytes.slice(_bytes, _start, _end); } } From 52d01629b28f55d26144e129dbfe7bc9ebd73f2b Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Thu, 23 Oct 2025 16:21:39 -0400 Subject: [PATCH 30/47] fix: Set version to cancun (#1162) * fix: Set version to cancun Signed-off-by: Faisal Usmani * remove evmVersion on overrides Signed-off-by: Faisal Usmani --------- Signed-off-by: Faisal Usmani --- foundry.toml | 2 +- hardhat.config.ts | 15 +++++++++++++++ package.json | 4 ++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/foundry.toml b/foundry.toml index ce59fa1f0..d2900f66f 100644 --- a/foundry.toml +++ b/foundry.toml @@ -34,7 +34,7 @@ revert_strings = "strip" fs_permissions = [{ access = "read", path = "./"}] solc = "0.8.24" -evm_version = "prague" +evm_version = "cancun" [profile.zksync] src = "contracts/Lens_SpokePool.sol" diff --git a/hardhat.config.ts b/hardhat.config.ts index 781bd199b..7c8d15320 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -57,6 +57,7 @@ const isTest = process.env.IS_TEST === "true" || process.env.CI === "true"; const compileZk = process.env.COMPILE_ZK === "true"; const solcVersion = "0.8.24"; +const evmVersion = "cancun"; // Compilation settings are overridden for large contracts to allow them to compile without going over the bytecode // limit. @@ -65,6 +66,7 @@ const LARGE_CONTRACT_COMPILER_SETTINGS = { settings: { optimizer: { enabled: true, runs: 800 }, viaIR: true, + evmVersion, debug: { revertStrings: isTest ? "debug" : "strip" }, }, }; @@ -73,6 +75,7 @@ const DEFAULT_CONTRACT_COMPILER_SETTINGS = { settings: { optimizer: { enabled: true, runs: 1000000 }, viaIR: true, + evmVersion, // Only strip revert strings if not testing or in ci. debug: { revertStrings: isTest ? "debug" : "strip" }, }, @@ -83,6 +86,7 @@ const LARGEST_CONTRACT_COMPILER_SETTINGS = { settings: { optimizer: { enabled: true, runs: 50 }, viaIR: true, + evmVersion, debug: { revertStrings: isTest ? "debug" : "strip" }, }, }; @@ -97,12 +101,23 @@ const config: HardhatUserConfig = { // NOTE: Linea only supports 0.8.19. // See https://docs.linea.build/build-on-linea/ethereum-differences#evm-opcodes version: "0.8.19", + settings: { + optimizer: { enabled: true, runs: 50 }, + viaIR: true, + debug: { revertStrings: isTest ? "debug" : "strip" }, + }, }, "contracts/SpokePoolVerifier.sol": { ...DEFAULT_CONTRACT_COMPILER_SETTINGS, // NOTE: Linea only supports 0.8.19. // See https://docs.linea.build/build-on-linea/ethereum-differences#evm-opcodes version: "0.8.19", + settings: { + optimizer: { enabled: true, runs: 1000000 }, + viaIR: true, + // Only strip revert strings if not testing or in ci. + debug: { revertStrings: isTest ? "debug" : "strip" }, + }, }, "contracts/Universal_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS, "contracts/Arbitrum_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS, diff --git a/package.json b/package.json index fec61c6ce..05c10dac7 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,8 @@ "lint-js": "yarn prettier --list-different **/*.js **/*.ts", "lint-rust": "cargo +nightly fmt --all -- --check && cargo clippy", "lint-fix": "yarn prettier --write **/*.js **/*.ts ./programs/**/*.rs ./contracts**/*.sol && cargo +nightly fmt --all && cargo clippy", - "clean-fast": "for dir in node_modules cache cache-zk artifacts artifacts-zk dist typechain out zkout; do mv \"${dir}\" \"_${dir}\"; rm -rf \"_${dir}\" &; done", - "clean": "rm -rf node_modules cache cache-zk artifacts artifacts-zk dist typechain out zkout", + "clean-fast": "for dir in node_modules cache cache-zk artifacts artifacts-zk dist typechain cache-foundry out zkout; do mv \"${dir}\" \"_${dir}\"; rm -rf \"_${dir}\" &; done", + "clean": "rm -rf node_modules cache cache-zk artifacts artifacts-zk dist typechain cache-foundry out zkout", "generate-svm-artifacts": "bash ./scripts/svm/buildHelpers/buildIdl.sh && sh ./scripts/svm/buildHelpers/generateSvmAssets.sh && yarn generate-svm-clients", "generate-svm-clients": "yarn ts-node ./scripts/svm/buildHelpers/generateSvmClients.ts && yarn ts-node ./scripts/svm/buildHelpers/renameClientsImports.ts", "build-evm": "hardhat compile", From 598378ea56c57bb83f54b79ea24e40e1e3a9ec88 Mon Sep 17 00:00:00 2001 From: Ihor Farion <65650773+grasphoper@users.noreply.github.com> Date: Wed, 3 Dec 2025 11:36:38 -0800 Subject: [PATCH 31/47] fix: contract sizes (#13) Signed-off-by: Ihor Farion Signed-off-by: Matt Rice Signed-off-by: Faisal Usmani Signed-off-by: Taylor Webb Co-authored-by: Matt Rice Co-authored-by: Faisal Usmani Co-authored-by: Taylor Webb <84364476+tbwebb22@users.noreply.github.com> Co-authored-by: Taylor Webb --- contracts/handlers/MulticallHandler.sol | 3 +- .../handlers/PermissionedMulticallHandler.sol | 33 +- .../interfaces/SponsoredCCTPInterface.sol | 12 +- contracts/libraries/HyperCoreLib.sol | 2 - contracts/libraries/SponsoredCCTPQuoteLib.sol | 2 +- .../mintburn/ArbitraryEVMFlowExecutor.sol | 21 +- .../mintburn/AuthorizedFundedFlow.sol | 35 ++ .../periphery/mintburn/BaseModuleHandler.sol | 48 ++ .../mintburn/HyperCoreFlowExecutor.sol | 513 ++++++++++++------ .../periphery/mintburn/HyperCoreFlowRoles.sol | 7 + contracts/periphery/mintburn/Structs.sol | 2 +- contracts/periphery/mintburn/SwapHandler.sol | 15 +- .../SponsoredCCTPDstPeriphery.sol | 165 +++++- .../SponsoredCCTPSrcPeriphery.sol | 47 +- .../mintburn/sponsored-oft/DstOFTHandler.sol | 101 +++- .../SponsoredOFTSrcPeriphery.sol | 82 ++- .../mintburn/sponsored-oft/Structs.sol | 2 - hardhat.config.ts | 1 + package.json | 1 + yarn.lock | 7 + 20 files changed, 808 insertions(+), 291 deletions(-) create mode 100644 contracts/periphery/mintburn/AuthorizedFundedFlow.sol create mode 100644 contracts/periphery/mintburn/BaseModuleHandler.sol create mode 100644 contracts/periphery/mintburn/HyperCoreFlowRoles.sol diff --git a/contracts/handlers/MulticallHandler.sol b/contracts/handlers/MulticallHandler.sol index 2a0edf440..239a3287e 100644 --- a/contracts/handlers/MulticallHandler.sol +++ b/contracts/handlers/MulticallHandler.sol @@ -58,6 +58,7 @@ contract MulticallHandler is AcrossMessageHandler, ReentrancyGuard { * @dev This will execute all calls encoded in the msg. The caller is responsible for making sure all tokens are * drained from this contract by the end of the series of calls. If not, they can be stolen. * A drainLeftoverTokens call can be included as a way to drain any remaining tokens from this contract. + * @param token The token address that was received from the relay * @param message abi encoded array of Call structs, containing a target, callData, and value for each call that * the contract should make. */ @@ -130,7 +131,7 @@ contract MulticallHandler is AcrossMessageHandler, ReentrancyGuard { uint256 value, Replacement[] calldata replacement ) external onlySelf { - for (uint256 i = 0; i < replacement.length; i++) { + for (uint256 i = 0; i < replacement.length; ++i) { uint256 bal = 0; if (replacement[i].token != address(0)) { bal = IERC20(replacement[i].token).balanceOf(address(this)); diff --git a/contracts/handlers/PermissionedMulticallHandler.sol b/contracts/handlers/PermissionedMulticallHandler.sol index 36ae2a749..5b9a4cc4c 100644 --- a/contracts/handlers/PermissionedMulticallHandler.sol +++ b/contracts/handlers/PermissionedMulticallHandler.sol @@ -14,15 +14,6 @@ contract PermissionedMulticallHandler is MulticallHandler, AccessControl { /// @notice Role identifier for whitelisted callers bytes32 public constant WHITELISTED_CALLER_ROLE = keccak256("WHITELISTED_CALLER_ROLE"); - /// @notice Emitted when a caller is whitelisted - event CallerWhitelisted(address indexed caller); - - /// @notice Emitted when a caller is removed from whitelist - event CallerRemovedFromWhitelist(address indexed caller); - - /// @notice Error thrown when caller is not whitelisted - error CallerNotWhitelisted(address caller); - /** * @notice Constructor that sets up the initial admin * @param admin Address that will have DEFAULT_ADMIN_ROLE @@ -44,30 +35,8 @@ contract PermissionedMulticallHandler is MulticallHandler, AccessControl { uint256 amount, address relayer, bytes memory message - ) public override { - if (!hasRole(WHITELISTED_CALLER_ROLE, msg.sender)) { - revert CallerNotWhitelisted(msg.sender); - } - + ) public override onlyRole(WHITELISTED_CALLER_ROLE) { // Call parent implementation super.handleV3AcrossMessage(token, amount, relayer, message); } - - /** - * @notice Add a caller to the whitelist - * @param caller Address to whitelist - */ - function whitelistCaller(address caller) external onlyRole(DEFAULT_ADMIN_ROLE) { - grantRole(WHITELISTED_CALLER_ROLE, caller); - emit CallerWhitelisted(caller); - } - - /** - * @notice Remove a caller from the whitelist - * @param caller Address to remove from whitelist - */ - function removeCallerFromWhitelist(address caller) external onlyRole(DEFAULT_ADMIN_ROLE) { - revokeRole(WHITELISTED_CALLER_ROLE, caller); - emit CallerRemovedFromWhitelist(caller); - } } diff --git a/contracts/interfaces/SponsoredCCTPInterface.sol b/contracts/interfaces/SponsoredCCTPInterface.sol index 5c5b5f6d1..0eec4b37c 100644 --- a/contracts/interfaces/SponsoredCCTPInterface.sol +++ b/contracts/interfaces/SponsoredCCTPInterface.sol @@ -19,6 +19,9 @@ interface SponsoredCCTPInterface { // Error thrown when the source domain is invalid. error InvalidSourceDomain(); + // Error thrown when the CCTP message transmitter receive message fails. + error CCTPMessageTransmitterFailed(); + event SponsoredDepositForBurn( bytes32 indexed quoteNonce, address indexed originSender, @@ -30,12 +33,8 @@ interface SponsoredCCTPInterface { bytes signature ); - event SimpleTansferToCore( - address indexed finalToken, - address indexed finalRecipient, - uint256 finalAmount, - uint256 maxAmountToSponsor - ); + // Event when emergency receive is called + event EmergencyReceiveMessage(bytes32 nonce, address finalRecipent, address finalToken, uint256 amount); // Execution modes for the sponsored CCTP flow enum ExecutionMode { @@ -56,7 +55,6 @@ interface SponsoredCCTPInterface { // The recipient of the minted USDC on the destination chain. bytes32 mintRecipient; // The amount that the user pays on the source chain. - // TODO: rename this to indicate source amount uint256 amount; // The token that will be burned on the source chain. bytes32 burnToken; diff --git a/contracts/libraries/HyperCoreLib.sol b/contracts/libraries/HyperCoreLib.sol index a4307c122..65d97f156 100644 --- a/contracts/libraries/HyperCoreLib.sol +++ b/contracts/libraries/HyperCoreLib.sol @@ -45,7 +45,6 @@ library HyperCoreLib { uint256 public constant BASE_ASSET_BRIDGE_ADDRESS_UINT256 = uint256(uint160(BASE_ASSET_BRIDGE_ADDRESS)); // Precompile addresses - // TODO: maybe we should be using https://github.com/hyperliquid-dev/hyper-evm-lib instead? address public constant SPOT_BALANCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; address public constant SPOT_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000808; address public constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; @@ -65,7 +64,6 @@ library HyperCoreLib { error CoreUserExistsPrecompileCallFailed(); error TokenInfoPrecompileCallFailed(); error SpotPxPrecompileCallFailed(); - error TransferAmtExceedsAssetBridgeBalance(uint256 amt, uint256 maxAmt); /** * @notice Transfer `amountEVM` from HyperEVM to `to` on HyperCore. diff --git a/contracts/libraries/SponsoredCCTPQuoteLib.sol b/contracts/libraries/SponsoredCCTPQuoteLib.sol index d80275c1e..199f93761 100644 --- a/contracts/libraries/SponsoredCCTPQuoteLib.sol +++ b/contracts/libraries/SponsoredCCTPQuoteLib.sol @@ -38,7 +38,7 @@ library SponsoredCCTPQuoteLib { uint256 private constant HOOK_DATA_INDEX = 228; // Minimum length of the message body (can be longer due to variable actionData) - uint256 private constant MIN_MSG_BYTES_LENGTH = 568; + uint256 private constant MIN_MSG_BYTES_LENGTH = 664; /** * @notice Gets the data for the deposit for burn. diff --git a/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol b/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol index 16ced87b0..1ab40f3cd 100644 --- a/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol +++ b/contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol @@ -27,7 +27,14 @@ abstract contract ArbitraryEVMFlowExecutor { /// @notice MulticallHandler contract instance address public immutable multicallHandler; - /// @notice Emitted when arbitrary actions are executed successfully + /** + * @notice Emitted when arbitrary actions are executed successfully + * @param quoteNonce Unique identifier for this quote/transaction + * @param initialToken The token address received before executing actions + * @param initialAmount The amount of initial token received + * @param finalToken The token address after executing actions + * @param finalAmount The amount of final token after executing actions + */ event ArbitraryActionsExecuted( bytes32 indexed quoteNonce, address indexed initialToken, @@ -36,11 +43,7 @@ abstract contract ArbitraryEVMFlowExecutor { uint256 finalAmount ); - /// @notice Error thrown when final balance is insufficient - error InsufficientFinalBalance(address token, uint256 expected, uint256 actual); - uint256 private constant BPS_TOTAL_PRECISION = 18; - uint256 private constant BPS_DECIMALS = 4; uint256 private constant BPS_PRECISION_SCALAR = 10 ** BPS_TOTAL_PRECISION; constructor(address _multicallHandler) { @@ -139,11 +142,7 @@ abstract contract ArbitraryEVMFlowExecutor { // Add final call to drain leftover tokens back to this contract calls[callCount] = MulticallHandler.Call({ target: multicallHandler, - callData: abi.encodeWithSelector( - MulticallHandler.drainLeftoverTokens.selector, - finalToken, - fallbackRecipient - ), + callData: abi.encodeCall(MulticallHandler.drainLeftoverTokens, (finalToken, payable(fallbackRecipient))), value: 0 }); @@ -156,7 +155,7 @@ abstract contract ArbitraryEVMFlowExecutor { return abi.encode(instructions); } - /// @notice Calcualtes proportional fees to sponsor in finalToken, given the fees to sponsor in initial token and initial amount + /// @notice Calculates proportional fees to sponsor in finalToken, given the fees to sponsor in initial token and initial amount function _calcExtraFeesFinal( uint256 amount, uint256 extraFeesToSponsorTokenIn, diff --git a/contracts/periphery/mintburn/AuthorizedFundedFlow.sol b/contracts/periphery/mintburn/AuthorizedFundedFlow.sol new file mode 100644 index 000000000..1fdfb7a59 --- /dev/null +++ b/contracts/periphery/mintburn/AuthorizedFundedFlow.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +/// @notice Library shared between handler contracts and modules to communicate from handler to module what context is a +/// specific function being called in +abstract contract AuthorizedFundedFlow { + // keccak256(abi.encode(uint256(keccak256("erc7201:AuthorizedFundedFlow.bool")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant AUTHORIZED_FUNDED_FLOW_SLOT = + 0xc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100; + + error FundedFlowNotAuthorized(); + + modifier authorizeFundedFlow() { + bytes32 slot = AUTHORIZED_FUNDED_FLOW_SLOT; + assembly { + sstore(slot, 1) + } + _; + assembly { + sstore(slot, 0) + } + } + + modifier onlyAuthorizedFlow() { + bytes32 slot = AUTHORIZED_FUNDED_FLOW_SLOT; + bool authorized; + assembly { + authorized := sload(slot) + } + if (!authorized) { + revert FundedFlowNotAuthorized(); + } + _; + } +} diff --git a/contracts/periphery/mintburn/BaseModuleHandler.sol b/contracts/periphery/mintburn/BaseModuleHandler.sol new file mode 100644 index 000000000..cba037ffd --- /dev/null +++ b/contracts/periphery/mintburn/BaseModuleHandler.sol @@ -0,0 +1,48 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +import { AuthorizedFundedFlow } from "./AuthorizedFundedFlow.sol"; +import { HyperCoreFlowExecutor } from "./HyperCoreFlowExecutor.sol"; +import { HyperCoreFlowRoles } from "./HyperCoreFlowRoles.sol"; + +// Note: v5 is necessary since v4 does not use ERC-7201. +import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import { ReentrancyGuard } from "@openzeppelin/contracts-v4/security/ReentrancyGuard.sol"; + +/** + * @notice Base contract for module handlers that use delegatecall to interact with HyperCoreFlowExecutor + * @dev Uses AccessControlUpgradeable to ensure storage compatibility with HyperCoreFlowExecutor when using delegatecall + */ +abstract contract BaseModuleHandler is + AccessControlUpgradeable, + ReentrancyGuard, + AuthorizedFundedFlow, + HyperCoreFlowRoles +{ + /// @notice Address of the underlying hypercore module + address public immutable hyperCoreModule; + + constructor(address _donationBox, address _baseToken, bytes32 _roleAdmin) { + hyperCoreModule = address(new HyperCoreFlowExecutor(_donationBox, _baseToken)); + + _setRoleAdmin(PERMISSIONED_BOT_ROLE, _roleAdmin); + _setRoleAdmin(FUNDS_SWEEPER_ROLE, _roleAdmin); + } + + /// @notice Fallback function to proxy all calls to the HyperCore module via delegatecall + /// @dev Permissioning is enforced by the delegated function's own modifiers (e.g. onlyPermissionedBot) + fallback(bytes calldata data) external nonReentrant returns (bytes memory) { + return _delegateToHyperCore(data); + } + + /// @notice Internal delegatecall helper + function _delegateToHyperCore(bytes memory data) internal returns (bytes memory) { + (bool success, bytes memory ret) = hyperCoreModule.delegatecall(data); + if (!success) { + assembly { + revert(add(ret, 32), mload(ret)) + } + } + return ret; + } +} diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 1944fd8da..7372f84dd 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -1,54 +1,47 @@ //SPDX-License-Identifier: Unlicense pragma solidity ^0.8.0; -import { AccessControl } from "@openzeppelin/contracts-v4/access/AccessControl.sol"; -import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/extensions/IERC20Metadata.sol"; -import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; +import { AuthorizedFundedFlow } from "./AuthorizedFundedFlow.sol"; +import { HyperCoreFlowRoles } from "./HyperCoreFlowRoles.sol"; import { DonationBox } from "../../chain-adapters/DonationBox.sol"; import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol"; import { CoreTokenInfo } from "./Structs.sol"; import { FinalTokenInfo } from "./Structs.sol"; import { SwapHandler } from "./SwapHandler.sol"; import { BPS_SCALAR, BPS_DECIMALS } from "./Constants.sol"; -import { Lockable } from "../../Lockable.sol"; import { CommonFlowParams } from "./Structs.sol"; +// Note: v5 is necessary since v4 does not use ERC-7201. +import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/extensions/IERC20Metadata.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; + /** * @title HyperCoreFlowExecutor * @notice Contract handling HyperCore interactions for transfer-to-core or swap-with-core actions after stablecoin bridge transactions * @dev This contract is designed to work with stablecoins. baseToken and every finalToken should all be stablecoins. + * + * @dev This contract is intended to be used exclusively via delegatecall from handler contracts. + * Direct calls to this contract will produce incorrect results because functions rely on the + * caller's context, including address(this) for calculations and storage layout from the + * delegating contract. + * * @custom:security-contact bugs@across.to */ -contract HyperCoreFlowExecutor is AccessControl, Lockable { +contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow, HyperCoreFlowRoles { using SafeERC20 for IERC20; // Common decimals scalars uint256 public constant PPM_DECIMALS = 6; uint256 public constant PPM_SCALAR = 10 ** PPM_DECIMALS; - // Decimals to use for Price calculations in limit order-related calculation functions - uint8 public constant PX_D = 8; uint64 public constant ONEX1e8 = 10 ** 8; - // Roles - bytes32 public constant PERMISSIONED_BOT_ROLE = keccak256("PERMISSIONED_BOT_ROLE"); - bytes32 public constant FUNDS_SWEEPER_ROLE = keccak256("FUNDS_SWEEPER_ROLE"); - /// @notice The donation box contract. DonationBox public immutable donationBox; - /// @notice A mapping of token addresses to their core token info. - mapping(address => CoreTokenInfo) public coreTokenInfos; - - /// @notice A mapping of token address to additional relevan info for final tokens, like Hyperliquid market params - mapping(address => FinalTokenInfo) public finalTokenInfos; - /// @notice All operations performed in this contract are relative to this baseToken address public immutable baseToken; - /// @notice The block number of the last funds pull action per final token: either as a part of finalizing pending swaps, - /// or an admin funds pull - mapping(address finalToken => uint256 lastPullFundsBlock) public lastPullFundsBlock; - /// @notice A struct used for storing state of a swap flow that has been initialized, but not yet finished struct SwapFlowState { address finalRecipient; @@ -59,25 +52,61 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { bool finalized; } - /// @notice A mapping containing the pending state between initializing the swap flow and finalizing it - mapping(bytes32 quoteNonce => SwapFlowState swap) public swaps; + /// @custom:storage-location erc7201:HyperCoreFlowExecutor.main + struct MainStorage { + /// @notice A mapping of token addresses to their core token info. + mapping(address => CoreTokenInfo) coreTokenInfos; + /// @notice A mapping of token address to additional relevan info for final tokens, like Hyperliquid market params + mapping(address => FinalTokenInfo) finalTokenInfos; + /// @notice The block number of the last funds pull action per final token: either as a part of finalizing pending swaps, + /// or an admin funds pull + mapping(address finalToken => uint256 lastPullFundsBlock) lastPullFundsBlock; + /// @notice A mapping containing the pending state between initializing the swap flow and finalizing it + mapping(bytes32 quoteNonce => SwapFlowState swap) swaps; + /// @notice The cumulative amount of funds sponsored for each final token. + mapping(address => uint256) cumulativeSponsoredAmount; + /// @notice The cumulative amount of activation fees sponsored for each final token. + mapping(address => uint256) cumulativeSponsoredActivationFee; + } - /// @notice The cumulative amount of funds sponsored for each final token. - mapping(address => uint256) public cumulativeSponsoredAmount; - /// @notice The cumulative amount of activation fees sponsored for each final token. - mapping(address => uint256) public cumulativeSponsoredActivationFee; + // keccak256(abi.encode(uint256(keccak256("erc7201:HyperCoreFlowExecutor.main")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 private constant MAIN_STORAGE_LOCATION = 0x6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00; + + function _getMainStorage() private pure returns (MainStorage storage $) { + assembly { + $.slot := MAIN_STORAGE_LOCATION + } + } /************************************** * EVENTS * **************************************/ - /// @notice Emitted when the donation box is insufficient funds. + /** + * @notice Emitted when the donation box is insufficient funds. + * @param quoteNonce Unique identifier for this quote/transaction + * @param token The token address that was requested + * @param amount The amount requested from the donation box + * @param balance The actual balance available in the donation box + */ event DonationBoxInsufficientFunds(bytes32 indexed quoteNonce, address token, uint256 amount, uint256 balance); - /// @notice Emitted whenever the account is not activated in the non-sponsored flow. We fall back to HyperEVM flow in that case + /** + * @notice Emitted whenever the account is not activated in the non-sponsored flow. We fall back to HyperEVM flow in that case + * @param quoteNonce Unique identifier for this quote/transaction + * @param user The address of the user whose account is not activated + */ event AccountNotActivated(bytes32 indexed quoteNonce, address user); - /// @notice Emitted when a simple transfer to core is executed. + /** + * @notice Emitted when a simple transfer to core is executed. + * @param quoteNonce Unique identifier for this quote/transaction + * @param finalRecipient The address receiving the funds on HyperCore + * @param finalToken The token address being transferred + * @param evmAmountIn The amount received on HyperEVM (in finalToken) + * @param bridgingFeesIncurred The bridging fees incurred (in finalToken) + * @param evmAmountSponsored The amount sponsored from the donation box (in finalToken) + */ event SimpleTransferFlowCompleted( bytes32 indexed quoteNonce, address indexed finalRecipient, @@ -88,7 +117,15 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { uint256 evmAmountSponsored ); - /// @notice Emitted upon successful completion of fallback HyperEVM flow + /** + * @notice Emitted upon successful completion of fallback HyperEVM flow + * @param quoteNonce Unique identifier for this quote/transaction + * @param finalRecipient The address receiving the funds on HyperEVM + * @param finalToken The token address being transferred + * @param evmAmountIn The amount received on HyperEVM (in finalToken) + * @param bridgingFeesIncurred The bridging fees incurred (in finalToken) + * @param evmAmountSponsored The amount sponsored from the donation box (in finalToken) + */ event FallbackHyperEVMFlowCompleted( bytes32 indexed quoteNonce, address indexed finalRecipient, @@ -99,7 +136,17 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { uint256 evmAmountSponsored ); - /// @notice Emitted when a swap flow is initialized + /** + * @notice Emitted when a swap flow is initialized + * @param quoteNonce Unique identifier for this quote/transaction + * @param finalRecipient The address that will receive the swapped funds on HyperCore + * @param finalToken The token address to swap to + * @param evmAmountIn The amount received on HyperEVM (in baseToken) + * @param bridgingFeesIncurred The bridging fees incurred (in baseToken) + * @param coreAmountIn The amount sent to HyperCore (in finalToken) + * @param minAmountToSend Minimum amount to send to user after swap (in finalToken) + * @param maxAmountToSend Maximum amount to send to user after swap (in finalToken) + */ event SwapFlowInitialized( bytes32 indexed quoteNonce, address indexed finalRecipient, @@ -113,7 +160,14 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { uint64 maxAmountToSend ); - /// @notice Emitted when a swap flow is finalized + /** + * @notice Emitted when a swap flow is finalized + * @param quoteNonce Unique identifier for this quote/transaction + * @param finalRecipient The address that received the swapped funds on HyperCore + * @param finalToken The token address that was swapped to + * @param totalSent Total amount sent to the final recipient on HyperCore (in finalToken) + * @param evmAmountSponsored The amount sponsored from the donation box (in EVM finalToken) + */ event SwapFlowFinalized( bytes32 indexed quoteNonce, address indexed finalRecipient, @@ -124,13 +178,29 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { uint256 evmAmountSponsored ); - /// @notice Emitted upon cancelling a Limit order + /** + * @notice Emitted upon cancelling a Limit order + * @param token The token address for which the limit order was placed + * @param cloid Client order ID of the cancelled limit order + */ event CancelledLimitOrder(address indexed token, uint128 indexed cloid); - /// @notice Emitted upon cancelling a Limit order + /** + * @notice Emitted upon submitting a Limit order + * @param token The token address for which the limit order is placed + * @param priceX1e8 The limit order price (scaled by 1e8) + * @param sizeX1e8 The limit order size (scaled by 1e8) + * @param cloid Client order ID of the submitted limit order + */ event SubmittedLimitOrder(address indexed token, uint64 priceX1e8, uint64 sizeX1e8, uint128 indexed cloid); - /// @notice Emitted when we have to fall back from the swap flow because it's too expensive (either to sponsor or the slippage is too big) + /** + * @notice Emitted when we have to fall back from the swap flow because it's too expensive (either to sponsor or the slippage is too big) + * @param quoteNonce Unique identifier for this quote/transaction + * @param finalToken The token address that was intended to be swapped to + * @param estBpsSlippage Estimated slippage in basis points + * @param maxAllowableBpsSlippage Maximum allowable slippage in basis points + */ event SwapFlowTooExpensive( bytes32 indexed quoteNonce, address indexed finalToken, @@ -138,10 +208,21 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { uint256 maxAllowableBpsSlippage ); - /// @notice Emitted when we can't bridge some token from HyperEVM to HyperCore + /** + * @notice Emitted when we can't bridge some token from HyperEVM to HyperCore + * @param quoteNonce Unique identifier for this quote/transaction + * @param token The token address that is unsafe to bridge + * @param amount The amount that was attempted to be bridged + */ event UnsafeToBridge(bytes32 indexed quoteNonce, address indexed token, uint64 amount); - /// @notice Emitted whenever donationBox funds are used for activating a user account + /** + * @notice Emitted whenever donationBox funds are used for activating a user account + * @param quoteNonce Unique identifier for this quote/transaction + * @param finalRecipient The address of the user whose account is being activated + * @param fundingToken The token used to fund the account activation + * @param evmAmountSponsored The amount sponsored for activation (in EVM token) + */ event SponsoredAccountActivation( bytes32 indexed quoteNonce, address indexed finalRecipient, @@ -149,7 +230,14 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { uint256 evmAmountSponsored ); - /// @notice Emitted whenever a new CoreTokenInfo is configured + /** + * @notice Emitted whenever a new CoreTokenInfo is configured + * @param token The token address being configured + * @param coreIndex The index of the token on HyperCore + * @param canBeUsedForAccountActivation Whether this token can be used to pay for account activation + * @param accountActivationFeeCore The account activation fee amount (in Core token units) + * @param bridgeSafetyBufferCore The safety buffer for bridging (in Core token units) + */ event SetCoreTokenInfo( address indexed token, uint32 coreIndex, @@ -158,7 +246,21 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { uint64 bridgeSafetyBufferCore ); - /// @notice Emitted when we do an ad-hoc send of sponsorship funds to one of the Swap Handlers + /// @notice Emitted whenever a new FinalTokenInfo is configured + event SetFinalTokenInfo( + address indexed token, + uint32 spotIndex, + bool isBuy, + uint32 feePpm, + address indexed swapHandler, + uint32 suggestedFeeDiscountBps + ); + + /** + * @notice Emitted when we do an ad-hoc send of sponsorship funds to one of the Swap Handlers + * @param token The token address being sent to the swap handler + * @param evmAmountSponsored The amount sponsored from the donation box (in EVM token) + */ event SentSponsorshipFundsToSwapHandler(address indexed token, uint256 evmAmountSponsored); /************************************** @@ -174,9 +276,6 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { /// @notice Thrown when trying to finalize a quoteNonce, calling a finalizeSwapFlows with an incorrect token error WrongSwapFinalizationToken(bytes32 quoteNonce); - /// @notice Emitted when the donation box is insufficient funds and we can't proceed. - error DonationBoxInsufficientFundsError(address token, uint256 amount); - /// @notice Emitted when we're inside the sponsored flow and a user doesn't have a HyperCore account activated. The /// bot should activate user's account first by calling `activateUserAccount` error AccountNotActivatedError(address user); @@ -188,21 +287,6 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { * MODIFIERS * **************************************/ - modifier onlyDefaultAdmin() { - require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not default admin"); - _; - } - - modifier onlyPermissionedBot() { - require(hasRole(PERMISSIONED_BOT_ROLE, msg.sender), "Not limit order updater"); - _; - } - - modifier onlyFundsSweeper() { - require(hasRole(FUNDS_SWEEPER_ROLE, msg.sender), "Not funds sweeper"); - _; - } - modifier onlyExistingCoreToken(address evmTokenAddress) { _getExistingCoreTokenInfo(evmTokenAddress); _; @@ -212,7 +296,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { function _getExistingCoreTokenInfo( address evmTokenAddress ) internal view returns (CoreTokenInfo memory coreTokenInfo) { - coreTokenInfo = coreTokenInfos[evmTokenAddress]; + coreTokenInfo = _getMainStorage().coreTokenInfos[evmTokenAddress]; require( coreTokenInfo.tokenInfo.evmContract != address(0) && coreTokenInfo.tokenInfo.weiDecimals != 0, "CoreTokenInfo not set" @@ -223,7 +307,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { function _getExistingFinalTokenInfo( address evmTokenAddress ) internal view returns (FinalTokenInfo memory finalTokenInfo) { - finalTokenInfo = finalTokenInfos[evmTokenAddress]; + finalTokenInfo = _getMainStorage().finalTokenInfos[evmTokenAddress]; require(address(finalTokenInfo.swapHandler) != address(0), "FinalTokenInfo not set"); } @@ -233,13 +317,67 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { * @param _baseToken Main token used with this Forwarder */ constructor(address _donationBox, address _baseToken) { + // Set immutable variables only donationBox = DonationBox(_donationBox); baseToken = _baseToken; + } - // AccessControl setup - _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); - _setRoleAdmin(PERMISSIONED_BOT_ROLE, DEFAULT_ADMIN_ROLE); - _setRoleAdmin(FUNDS_SWEEPER_ROLE, DEFAULT_ADMIN_ROLE); + /**************************************** + * VIEW FUNCTIONS * + **************************************/ + + /** + * @notice Returns the core token info for a given token address. + * @param token The token address. + * @return The core token info for the given token address. + */ + function coreTokenInfos(address token) external view returns (CoreTokenInfo memory) { + return _getMainStorage().coreTokenInfos[token]; + } + + /** + * @notice Returns the final token info for a given token address. + * @param token The token address. + * @return The final token info for the given token address. + */ + function finalTokenInfos(address token) external view returns (FinalTokenInfo memory) { + return _getMainStorage().finalTokenInfos[token]; + } + + /** + * @notice Returns the block number of the last time funds were pulled from the donation box. + * @param token The token address. + * @return The block number of the last time funds were pulled from the donation box for the given token address. + */ + function lastPullFundsBlock(address token) external view returns (uint256) { + return _getMainStorage().lastPullFundsBlock[token]; + } + + /** + * @notice Returns the swap info for a given quote nonce. + * @param quoteNonce The quote nonce. + * @return The swap info for the given quote nonce. + */ + function swaps(bytes32 quoteNonce) external view returns (SwapFlowState memory) { + return _getMainStorage().swaps[quoteNonce]; + } + + /** + * @notice Returns the cumulative sponsored amount for a given token address. + * @param token The token address. + * @return The cumulative sponsored amount for the given token address. + */ + function cumulativeSponsoredAmount(address token) external view returns (uint256) { + return _getMainStorage().cumulativeSponsoredAmount[token]; + } + + /** + * @notice Returns the cumulative sponsored activation fee for a given token address. + * @param token The token address. + * @return The cumulative sponsored activation fee for the given token address. + */ + function cumulativeSponsoredActivationFee(address token) external view returns (uint256) { + return _getMainStorage().cumulativeSponsoredActivationFee[token]; } /************************************** @@ -251,6 +389,11 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { * @dev To be able to use the token in the swap flow, FinalTokenInfo has to be set as well * @dev Setting core token info to incorrect values can lead to loss of funds. Should NEVER be unset while the * finalTokenParams are not unset + * @param token The token address being configured + * @param coreIndex The index of the token on HyperCore + * @param canBeUsedForAccountActivation Whether this token can be used to pay for account activation + * @param accountActivationFeeCore The account activation fee amount (in Core token units) + * @param bridgeSafetyBufferCore The safety buffer for bridging (in Core token units) */ function setCoreTokenInfo( address token, @@ -258,7 +401,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { bool canBeUsedForAccountActivation, uint64 accountActivationFeeCore, uint64 bridgeSafetyBufferCore - ) external nonReentrant onlyDefaultAdmin { + ) external onlyRole(DEFAULT_ADMIN_ROLE) { _setCoreTokenInfo( token, coreIndex, @@ -273,34 +416,27 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { * @dev This function deploys a new SwapHandler contract if one is not already set. If the final token * can't be used for account activation, the handler will be left unactivated and would need to be activated by the caller. * @param finalToken The address of the final token. - * @param assetIndex The index of the asset in the Hyperliquid market. + * @param spotIndex The index of the asset in the Hyperliquid market. * @param isBuy Whether the final token is a buy or a sell. * @param feePpm The fee in parts per million. * @param suggestedDiscountBps The suggested slippage in basis points. - * @param accountActivationFeeToken A token to pay account activation fee in. Only used if adding a new final token */ function setFinalTokenInfo( address finalToken, - uint32 assetIndex, + uint32 spotIndex, bool isBuy, uint32 feePpm, - uint32 suggestedDiscountBps, - address accountActivationFeeToken - ) - external - nonReentrant - onlyExistingCoreToken(finalToken) - onlyExistingCoreToken(accountActivationFeeToken) - onlyDefaultAdmin - { - SwapHandler swapHandler = finalTokenInfos[finalToken].swapHandler; + uint32 suggestedDiscountBps + ) external onlyExistingCoreToken(finalToken) onlyRole(DEFAULT_ADMIN_ROLE) { + MainStorage storage $ = _getMainStorage(); + SwapHandler swapHandler = $.finalTokenInfos[finalToken].swapHandler; if (address(swapHandler) == address(0)) { bytes32 salt = _swapHandlerSalt(finalToken); swapHandler = new SwapHandler{ salt: salt }(); } - finalTokenInfos[finalToken] = FinalTokenInfo({ - assetIndex: assetIndex, + $.finalTokenInfos[finalToken] = FinalTokenInfo({ + spotIndex: spotIndex, isBuy: isBuy, feePpm: feePpm, swapHandler: swapHandler, @@ -310,9 +446,15 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { // We don't allow SwapHandler accounts to be uninitiated. That could lead to loss of funds. They instead should // be pre-funded using `predictSwapHandler` to predict their address require(HyperCoreLib.coreUserExists(address(swapHandler)), "SwapHandler @ core doesn't exist"); + + emit SetFinalTokenInfo(finalToken, spotIndex, isBuy, feePpm, address(swapHandler), suggestedDiscountBps); } - /// @notice Predicts the deterministic address of a SwapHandler for a given finalToken using CREATE2 + /** + * @notice Predicts the deterministic address of a SwapHandler for a given finalToken using CREATE2 + * @param finalToken The token address for which to predict the SwapHandler address + * @return The predicted address of the SwapHandler contract + */ function predictSwapHandler(address finalToken) public view returns (address) { bytes32 salt = _swapHandlerSalt(finalToken); bytes32 initCodeHash = keccak256(type(SwapHandler).creationCode); @@ -329,11 +471,10 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { **************************************/ /** - * @notice This function is to be called by an inheriting contract. It is to be called after the child contract - * checked the API signature and made sure that the params passed here have been verified by either the underlying - * bridge mechanics, or API signaure, or both. + * @notice External entrypoint to execute flow when called via delegatecall from a handler. Works with params + * checked by a handler. Params authorization by a handler is enforced via `onlyAuthorizedFlow` modifier */ - function _executeFlow(CommonFlowParams memory params, uint256 maxUserSlippageBps) internal { + function executeFlow(CommonFlowParams memory params, uint256 maxUserSlippageBps) external onlyAuthorizedFlow { if (params.finalToken == baseToken) { _executeSimpleTransferFlow(params); } else { @@ -341,11 +482,22 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { } } + /// @notice External entrypoint to execute simple transfer flow (see `executeFlow` comment for details) + function executeSimpleTransferFlow(CommonFlowParams memory params) external onlyAuthorizedFlow { + _executeSimpleTransferFlow(params); + } + + /// @notice External entrypoint to execute fallback evm flow (see `executeFlow` comment for details) + function fallbackHyperEVMFlow(CommonFlowParams memory params) external onlyAuthorizedFlow { + _fallbackHyperEVMFlow(params); + } + /// @notice Execute a simple transfer flow in which we transfer `finalToken` to the user on HyperCore after receiving /// an amount of finalToken from the user on HyperEVM - function _executeSimpleTransferFlow(CommonFlowParams memory params) internal virtual { + function _executeSimpleTransferFlow(CommonFlowParams memory params) internal { address finalToken = params.finalToken; - CoreTokenInfo storage coreTokenInfo = coreTokenInfos[finalToken]; + MainStorage storage $ = _getMainStorage(); + CoreTokenInfo memory coreTokenInfo = $.coreTokenInfos[finalToken]; // Check account activation if (!HyperCoreLib.coreUserExists(params.finalRecipient)) { @@ -370,7 +522,8 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { if (amountToSponsor > 0) { if (!_availableInDonationBox(params.quoteNonce, coreTokenInfo.tokenInfo.evmContract, amountToSponsor)) { - amountToSponsor = 0; + // If the full amount is not available in the donation box, use the balance of the token in the donation box + amountToSponsor = IERC20(coreTokenInfo.tokenInfo.evmContract).balanceOf(address(donationBox)); } } } @@ -406,7 +559,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { donationBox.withdraw(IERC20(coreTokenInfo.tokenInfo.evmContract), amountToSponsor); } - cumulativeSponsoredAmount[finalToken] += amountToSponsor; + $.cumulativeSponsoredAmount[finalToken] += amountToSponsor; // There is a very slim change that someone is sending > buffer amount in the same EVM block and the balance of // the bridge is not enough to cover our transfer, so the funds are lost. @@ -430,26 +583,29 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { /** * @notice Initiates the swap flow. Sends the funds received on EVM side over to a SwapHandler corresponding to a - * finalToken. This is the first leg of the swap flow. Next, the bot should submit a limit order through a `submitLimitOrder` + * finalToken. This is the first leg of the swap flow. Next, the bot should submit a limit order through a `submitLimitOrderFromBot` * function, and then settle the flow via a `finalizeSwapFlows` function * @dev Only works for stable -> stable swap flows (or equivalent token flows. Price between tokens is supposed to be approximately one to one) * @param maxUserSlippageBps Describes a configured user setting. Slippage here is wrt the one to one exchange */ function _initiateSwapFlow(CommonFlowParams memory params, uint256 maxUserSlippageBps) internal { + address initialToken = baseToken; + // Check account activation if (!HyperCoreLib.coreUserExists(params.finalRecipient)) { if (params.maxBpsToSponsor > 0) { revert AccountNotActivatedError(params.finalRecipient); } else { emit AccountNotActivated(params.quoteNonce, params.finalRecipient); + params.finalToken = initialToken; _fallbackHyperEVMFlow(params); return; } } - address initialToken = baseToken; - CoreTokenInfo memory initialCoreTokenInfo = coreTokenInfos[initialToken]; - CoreTokenInfo memory finalCoreTokenInfo = coreTokenInfos[params.finalToken]; + MainStorage storage $ = _getMainStorage(); + CoreTokenInfo memory initialCoreTokenInfo = $.coreTokenInfos[initialToken]; + CoreTokenInfo memory finalCoreTokenInfo = $.coreTokenInfos[params.finalToken]; FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(params.finalToken); // Calculate limit order amounts and check if feasible @@ -468,7 +624,11 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { maxUserSlippageBps ); - uint64 approxExecutionPriceX1e8 = _getApproxRealizedPrice(finalTokenInfo); + uint64 approxExecutionPriceX1e8 = _getApproxRealizedPrice( + finalTokenInfo, + finalCoreTokenInfo, + initialCoreTokenInfo + ); uint256 maxAllowableBpsDeviation = params.maxBpsToSponsor > 0 ? params.maxBpsToSponsor : maxUserSlippageBps; if (finalTokenInfo.isBuy) { if (approxExecutionPriceX1e8 < ONEX1e8) { @@ -524,7 +684,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { // Finalize swap flow setup by updating state and funding SwapHandler // State changes - swaps[params.quoteNonce] = SwapFlowState({ + $.swaps[params.quoteNonce] = SwapFlowState({ finalRecipient: params.finalRecipient, finalToken: params.finalToken, minAmountToSend: minAllowableAmountToForwardCore, @@ -544,7 +704,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { maxAllowableAmountToForwardCore ); - // Send amount received form user to a corresponding SwapHandler + // Send amount received from user to a corresponding SwapHandler SwapHandler swapHandler = finalTokenInfo.swapHandler; IERC20(initialToken).safeTransfer(address(swapHandler), tokensToSendEvm); swapHandler.transferFundsToSelfOnCore( @@ -560,17 +720,22 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { * @dev Caller is responsible for providing correct limitOrderOutput amounts per assosicated swap flow. The caller * has to estimate how much final tokens it received on core based on the input of the corresponding quote nonce * swap flow + * @param finalToken The token address for the swaps being finalized + * @param quoteNonces Array of quote nonces identifying the swap flows to finalize + * @param limitOrderOuts Array of limit order output amounts corresponding to each quote nonce + * @return finalized The number of swap flows that were successfully finalized */ function finalizeSwapFlows( address finalToken, bytes32[] calldata quoteNonces, uint64[] calldata limitOrderOuts - ) external onlyPermissionedBot returns (uint256 finalized) { + ) external onlyRole(PERMISSIONED_BOT_ROLE) returns (uint256 finalized) { + MainStorage storage $ = _getMainStorage(); require(quoteNonces.length == limitOrderOuts.length, "length"); - require(lastPullFundsBlock[finalToken] < block.number, "too soon"); + require($.lastPullFundsBlock[finalToken] < block.number, "too soon"); CoreTokenInfo memory finalCoreTokenInfo = _getExistingCoreTokenInfo(finalToken); - FinalTokenInfo memory finalTokenInfo = finalTokenInfos[finalToken]; + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); uint64 availableBalance = HyperCoreLib.spotBalance( address(finalTokenInfo.swapHandler), @@ -584,6 +749,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { quoteNonces[finalized], limitOrderOuts[finalized], finalCoreTokenInfo, + finalTokenInfo.swapHandler, availableBalance ); if (!success) { @@ -593,7 +759,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { } if (finalized > 0) { - lastPullFundsBlock[finalToken] = block.number; + $.lastPullFundsBlock[finalToken] = block.number; } else { return 0; } @@ -617,7 +783,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { revert UnsafeToBridgeError(finalCoreTokenInfo.tokenInfo.evmContract, totalAdditionalToSend); } - cumulativeSponsoredAmount[finalToken] += totalAdditionalToSendEVM; + $.cumulativeSponsoredAmount[finalToken] += totalAdditionalToSendEVM; // ! Notice: as per HyperEVM <> HyperCore rules, this amount will land on HyperCore *before* all of the core > core sends get executed // Get additional amount to send from donation box, and send it to self on core @@ -637,9 +803,10 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { bytes32 quoteNonce, uint64 limitOrderOut, CoreTokenInfo memory finalCoreTokenInfo, + SwapHandler swapHandler, uint64 availableBalance ) internal returns (bool success, uint64 additionalToSend, uint64 balanceRemaining) { - SwapFlowState storage swap = swaps[quoteNonce]; + SwapFlowState storage swap = _getMainStorage().swaps[quoteNonce]; if (swap.finalRecipient == address(0)) revert SwapDoesNotExist(); if (swap.finalized) revert SwapAlreadyFinalized(); if (swap.finalToken != finalCoreTokenInfo.tokenInfo.evmContract) revert WrongSwapFinalizationToken(quoteNonce); @@ -667,12 +834,12 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals ); - HyperCoreLib.transferERC20CoreToCore(finalCoreTokenInfo.coreIndex, swap.finalRecipient, totalToSend); + swapHandler.transferFundsToUserOnCore(finalCoreTokenInfo.coreIndex, swap.finalRecipient, totalToSend); emit SwapFlowFinalized(quoteNonce, swap.finalRecipient, swap.finalToken, totalToSend, additionalToSendEVM); } /// @notice Forwards `amount` plus potential sponsorship funds (for bridging fee) to user on HyperEVM - function _fallbackHyperEVMFlow(CommonFlowParams memory params) internal virtual { + function _fallbackHyperEVMFlow(CommonFlowParams memory params) internal { uint256 maxEvmAmountToSponsor = ((params.amountInEVM + params.extraFeesIncurred) * params.maxBpsToSponsor) / BPS_SCALAR; uint256 sponsorshipFundsToForward = params.extraFeesIncurred > maxEvmAmountToSponsor @@ -687,7 +854,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { } uint256 totalAmountToForward = params.amountInEVM + sponsorshipFundsToForward; IERC20(params.finalToken).safeTransfer(params.finalRecipient, totalAmountToForward); - cumulativeSponsoredAmount[params.finalToken] += sponsorshipFundsToForward; + _getMainStorage().cumulativeSponsoredAmount[params.finalToken] += sponsorshipFundsToForward; emit FallbackHyperEVMFlowCompleted( params.quoteNonce, params.finalRecipient, @@ -708,39 +875,53 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { bytes32 quoteNonce, address finalRecipient, address fundingToken - ) external nonReentrant onlyPermissionedBot { + ) external onlyRole(PERMISSIONED_BOT_ROLE) { CoreTokenInfo memory coreTokenInfo = _getExistingCoreTokenInfo(fundingToken); bool coreUserExists = HyperCoreLib.coreUserExists(finalRecipient); - require(coreUserExists == false, "Can't fund account activation for existing user"); + require(!coreUserExists, "Can't fund account activation for existing user"); require(coreTokenInfo.canBeUsedForAccountActivation, "Token can't be used for this"); + + // +1 wei for a spot send + uint64 totalBalanceRequiredToActivate = coreTokenInfo.accountActivationFeeCore + 1; + (uint256 evmAmountToSend, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + totalBalanceRequiredToActivate, + coreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + bool safeToBridge = HyperCoreLib.isCoreAmountSafeToBridge( coreTokenInfo.coreIndex, - coreTokenInfo.accountActivationFeeCore, + totalBalanceRequiredToActivate, coreTokenInfo.bridgeSafetyBufferCore ); require(safeToBridge, "Not safe to bridge"); - uint256 activationFeeEvm = coreTokenInfo.accountActivationFeeEVM; - cumulativeSponsoredActivationFee[fundingToken] += activationFeeEvm; + _getMainStorage().cumulativeSponsoredActivationFee[fundingToken] += evmAmountToSend; // donationBox @ evm -> Handler @ evm - donationBox.withdraw(IERC20(fundingToken), activationFeeEvm); - // Handler @ evm -> Handler @ core -> finalRecipient @ core - HyperCoreLib.transferERC20EVMToCore( + donationBox.withdraw(IERC20(fundingToken), evmAmountToSend); + // Handler @ evm -> Handler @ core + HyperCoreLib.transferERC20EVMToSelfOnCore( fundingToken, coreTokenInfo.coreIndex, - finalRecipient, - activationFeeEvm, + evmAmountToSend, coreTokenInfo.tokenInfo.evmExtraWeiDecimals ); + // The total balance withdrawn from Handler @ Core for this operation is activationFee + amountSent, so we set + // amountSent to 1 wei to only activate the account + // Handler @ core -> finalRecipient @ core + HyperCoreLib.transferERC20CoreToCore(coreTokenInfo.coreIndex, finalRecipient, 1); - emit SponsoredAccountActivation(quoteNonce, finalRecipient, fundingToken, activationFeeEvm); + emit SponsoredAccountActivation(quoteNonce, finalRecipient, fundingToken, evmAmountToSend); } - /// @notice Cancells a pending limit order by `cloid` with an intention to submit a new limit order in its place. To - /// be used for stale limit orders to speed up executing user transactions - function cancelLimitOrderByCloid(address finalToken, uint128 cloid) external nonReentrant onlyPermissionedBot { - FinalTokenInfo memory finalTokenInfo = finalTokenInfos[finalToken]; - finalTokenInfo.swapHandler.cancelOrderByCloid(finalTokenInfo.assetIndex, cloid); + /** + * @notice Cancells a pending limit order by `cloid` with an intention to submit a new limit order in its place. To + * be used for stale limit orders to speed up executing user transactions + * @param finalToken The token address for which the limit order was placed + * @param cloid Client order ID of the limit order to cancel + */ + function cancelLimitOrderByCloid(address finalToken, uint128 cloid) external onlyRole(PERMISSIONED_BOT_ROLE) { + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); + finalTokenInfo.swapHandler.cancelOrderByCloid(finalTokenInfo.spotIndex, cloid); emit CancelledLimitOrder(finalToken, cloid); } @@ -750,9 +931,9 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { uint64 priceX1e8, uint64 sizeX1e8, uint128 cloid - ) external nonReentrant onlyPermissionedBot { - FinalTokenInfo memory finalTokenInfo = finalTokenInfos[finalToken]; - finalTokenInfo.swapHandler.submitLimitOrder(finalTokenInfo, priceX1e8, sizeX1e8, cloid); + ) external onlyRole(PERMISSIONED_BOT_ROLE) { + FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(finalToken); + finalTokenInfo.swapHandler.submitSpotLimitOrder(finalTokenInfo, priceX1e8, sizeX1e8, cloid); emit SubmittedLimitOrder(finalToken, priceX1e8, sizeX1e8, cloid); } @@ -772,7 +953,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { tokenInfo.evmExtraWeiDecimals ); - coreTokenInfos[token] = CoreTokenInfo({ + _getMainStorage().coreTokenInfos[token] = CoreTokenInfo({ tokenInfo: tokenInfo, coreIndex: coreIndex, canBeUsedForAccountActivation: canBeUsedForAccountActivation, @@ -793,8 +974,9 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { /** * @notice Used for ad-hoc sends of sponsorship funds to associated SwapHandler @ HyperCore * @param token The final token for which we want to fund the SwapHandler + * @param amount The amount of tokens to send to the SwapHandler */ - function sendSponsorshipFundsToSwapHandler(address token, uint256 amount) external onlyPermissionedBot { + function sendSponsorshipFundsToSwapHandler(address token, uint256 amount) external onlyRole(PERMISSIONED_BOT_ROLE) { CoreTokenInfo memory coreTokenInfo = _getExistingCoreTokenInfo(token); FinalTokenInfo memory finalTokenInfo = _getExistingFinalTokenInfo(token); (uint256 amountEVMToSend, uint64 amountCoreToReceive) = HyperCoreLib.maximumEVMSendAmountToAmounts( @@ -811,7 +993,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { revert UnsafeToBridgeError(token, amountCoreToReceive); } - cumulativeSponsoredAmount[token] += amountEVMToSend; + _getMainStorage().cumulativeSponsoredAmount[token] += amountEVMToSend; emit SentSponsorshipFundsToSwapHandler(token, amountEVMToSend); @@ -862,10 +1044,7 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { minAllowableAmountToForwardCore = uint64( (feelessAmountCoreFinalToken * (BPS_SCALAR - maxUserSlippageBps)) / BPS_SCALAR ); - // Maximum allowable amount to forward is a one-to-one equivalent of `amount` - maxAllowableAmountToForwardCore = uint64( - (feelessAmountCoreFinalToken * amount) / (amount + extraFeesIncurred) - ); + maxAllowableAmountToForwardCore = feelessAmountCoreFinalToken; } } @@ -879,28 +1058,32 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { uint64 maxAmountToSend, bool isSponsored ) internal pure returns (uint64 totalToSend, uint64 additionalToSend) { - // What we will send from donationBox right now - // What we will forward to user on HCore - if (limitOrderOut >= maxAmountToSend || isSponsored) { - totalToSend = maxAmountToSend; + if (isSponsored) { + // `minAmountToSend` is equal to `maxAmountToSend` for the sponsored flow + totalToSend = minAmountToSend; + additionalToSend = totalToSend > limitOrderOut ? totalToSend - limitOrderOut : 0; } else { - if (limitOrderOut < minAmountToSend) { - additionalToSend = minAmountToSend - limitOrderOut; - } - - // Give user a fair deal, which is the max of: - // - limitOrderOut - // - minAmountToSend - totalToSend = limitOrderOut + additionalToSend; + additionalToSend = limitOrderOut < minAmountToSend ? minAmountToSend - limitOrderOut : 0; + uint64 proposedToSend = limitOrderOut + additionalToSend; + totalToSend = proposedToSend > maxAmountToSend ? maxAmountToSend : proposedToSend; } } /// @notice Reads the current spot price from HyperLiquid and applies a configured suggested discount for faster execution /// @dev Includes HyperLiquid fees function _getApproxRealizedPrice( - FinalTokenInfo memory finalTokenInfo + FinalTokenInfo memory finalTokenInfo, + CoreTokenInfo memory finalCoreTokenInfo, + CoreTokenInfo memory initialCoreTokenInfo ) internal view returns (uint64 limitPriceX1e8) { - uint64 spotX1e8 = HyperCoreLib.spotPx(finalTokenInfo.assetIndex); + uint256 spotPxRaw = HyperCoreLib.spotPx(finalTokenInfo.spotIndex); + // Convert to 10 ** 8 precision (https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/hyperevm/interacting-with-hypercore) + // `szDecimals` of the base aseet for spot market + uint8 additionalPowersOf10 = finalTokenInfo.isBuy + ? finalCoreTokenInfo.tokenInfo.szDecimals + : initialCoreTokenInfo.tokenInfo.szDecimals; + uint256 spotX1e8 = spotPxRaw * (10 ** additionalPowersOf10); + // Buy above spot, sell below spot uint256 adjPpm = finalTokenInfo.isBuy ? (PPM_SCALAR + finalTokenInfo.suggestedDiscountBps * 10 ** 2 + finalTokenInfo.feePpm) @@ -912,31 +1095,45 @@ contract HyperCoreFlowExecutor is AccessControl, Lockable { * SWEEP FUNCTIONS * **************************************/ - function sweepErc20(address token, uint256 amount) external nonReentrant onlyFundsSweeper { + function sweepNative(uint256 amount) external onlyRole(FUNDS_SWEEPER_ROLE) { + (bool success, ) = msg.sender.call{ value: amount }(""); + require(success, "Failed to send native funds"); + } + + function sweepErc20(address token, uint256 amount) external onlyRole(FUNDS_SWEEPER_ROLE) { IERC20(token).safeTransfer(msg.sender, amount); } - function sweepErc20FromDonationBox(address token, uint256 amount) external nonReentrant onlyFundsSweeper { + function sweepErc20FromDonationBox(address token, uint256 amount) external onlyRole(FUNDS_SWEEPER_ROLE) { donationBox.withdraw(IERC20(token), amount); IERC20(token).safeTransfer(msg.sender, amount); } - function sweepERC20FromSwapHandler(address token, uint256 amount) external nonReentrant onlyFundsSweeper { - SwapHandler swapHandler = finalTokenInfos[token].swapHandler; + function sweepERC20FromSwapHandler(address token, uint256 amount) external onlyRole(FUNDS_SWEEPER_ROLE) { + SwapHandler swapHandler = _getExistingFinalTokenInfo(token).swapHandler; swapHandler.sweepErc20(token, amount); IERC20(token).safeTransfer(msg.sender, amount); } - function sweepOnCore(address token, uint64 amount) external nonReentrant onlyFundsSweeper { - HyperCoreLib.transferERC20CoreToCore(coreTokenInfos[token].coreIndex, msg.sender, amount); + function sweepOnCore(address token, uint64 amount) external onlyRole(FUNDS_SWEEPER_ROLE) { + HyperCoreLib.transferERC20CoreToCore(_getMainStorage().coreTokenInfos[token].coreIndex, msg.sender, amount); } - function sweepOnCoreFromSwapHandler(address token, uint64 amount) external nonReentrant onlyDefaultAdmin { - // Prevent pulling fantom funds (e.g. if finalizePendingSwaps reads stale balance because of this fund pull) - require(lastPullFundsBlock[token] < block.number, "Can't pull funds twice in the same block"); - lastPullFundsBlock[token] = block.number; - - SwapHandler swapHandler = finalTokenInfos[token].swapHandler; - swapHandler.transferFundsToUserOnCore(finalTokenInfos[token].assetIndex, msg.sender, amount); + function sweepOnCoreFromSwapHandler( + address finalToken, + uint64 finalTokenAmount, + uint64 baseTokenAmount + ) external onlyRole(FUNDS_SWEEPER_ROLE) { + MainStorage storage $ = _getMainStorage(); + require($.lastPullFundsBlock[finalToken] < block.number, "Can't pull funds twice in the same block"); + $.lastPullFundsBlock[finalToken] = block.number; + + SwapHandler swapHandler = $.finalTokenInfos[finalToken].swapHandler; + if (finalTokenAmount > 0) { + swapHandler.transferFundsToUserOnCore($.coreTokenInfos[finalToken].coreIndex, msg.sender, finalTokenAmount); + } + if (baseTokenAmount > 0) { + swapHandler.transferFundsToUserOnCore($.coreTokenInfos[baseToken].coreIndex, msg.sender, baseTokenAmount); + } } } diff --git a/contracts/periphery/mintburn/HyperCoreFlowRoles.sol b/contracts/periphery/mintburn/HyperCoreFlowRoles.sol new file mode 100644 index 000000000..5f30e15df --- /dev/null +++ b/contracts/periphery/mintburn/HyperCoreFlowRoles.sol @@ -0,0 +1,7 @@ +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + +abstract contract HyperCoreFlowRoles { + bytes32 public constant PERMISSIONED_BOT_ROLE = keccak256("PERMISSIONED_BOT_ROLE"); + bytes32 public constant FUNDS_SWEEPER_ROLE = keccak256("FUNDS_SWEEPER_ROLE"); +} diff --git a/contracts/periphery/mintburn/Structs.sol b/contracts/periphery/mintburn/Structs.sol index c188483c8..6b7e6ef47 100644 --- a/contracts/periphery/mintburn/Structs.sol +++ b/contracts/periphery/mintburn/Structs.sol @@ -22,7 +22,7 @@ struct CoreTokenInfo { struct FinalTokenInfo { // The index of the market where we're going to swap baseToken -> finalToken - uint32 assetIndex; + uint32 spotIndex; // To go baseToken -> finalToken, do we have to enqueue a buy or a sell? bool isBuy; // The fee Hyperliquid charges for Limit orders in the market; in parts per million, e.g. 1.4 bps = 140 ppm diff --git a/contracts/periphery/mintburn/SwapHandler.sol b/contracts/periphery/mintburn/SwapHandler.sol index 44d2a9845..92da0e134 100644 --- a/contracts/periphery/mintburn/SwapHandler.sol +++ b/contracts/periphery/mintburn/SwapHandler.sol @@ -1,11 +1,16 @@ //SPDX-License-Identifier: Unlicense pragma solidity ^0.8.0; import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol"; import { FinalTokenInfo } from "./Structs.sol"; contract SwapHandler { + // See https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#asset + uint32 private constant SPOT_MARKET_INDEX_OFFSET = 10_000; + address public immutable parentHandler; + using SafeERC20 for IERC20; constructor() { parentHandler = msg.sender; @@ -42,14 +47,14 @@ contract SwapHandler { HyperCoreLib.transferERC20CoreToCore(erc20CoreIndex, to, amountCore); } - function submitLimitOrder( + function submitSpotLimitOrder( FinalTokenInfo memory finalTokenInfo, uint64 limitPriceX1e8, uint64 sizeX1e8, uint128 cloid ) external onlyParentHandler { HyperCoreLib.submitLimitOrder( - finalTokenInfo.assetIndex, + finalTokenInfo.spotIndex + SPOT_MARKET_INDEX_OFFSET, finalTokenInfo.isBuy, limitPriceX1e8, sizeX1e8, @@ -59,11 +64,11 @@ contract SwapHandler { ); } - function cancelOrderByCloid(uint32 assetIndex, uint128 cloid) external onlyParentHandler { - HyperCoreLib.cancelOrderByCloid(assetIndex, cloid); + function cancelOrderByCloid(uint32 spotIndex, uint128 cloid) external onlyParentHandler { + HyperCoreLib.cancelOrderByCloid(spotIndex + SPOT_MARKET_INDEX_OFFSET, cloid); } function sweepErc20(address token, uint256 amount) external onlyParentHandler { - IERC20(token).transfer(msg.sender, amount); + IERC20(token).safeTransfer(msg.sender, amount); } } diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol index 9c6d15c56..cbd982709 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol @@ -1,8 +1,7 @@ //SPDX-License-Identifier: Unlicense pragma solidity ^0.8.0; -import { IERC20Metadata } from "@openzeppelin/contracts-v4/token/ERC20/extensions/IERC20Metadata.sol"; -import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; +import { BaseModuleHandler } from "../BaseModuleHandler.sol"; import { IMessageTransmitterV2 } from "../../../external/interfaces/CCTPInterfaces.sol"; import { SponsoredCCTPQuoteLib } from "../../../libraries/SponsoredCCTPQuoteLib.sol"; import { SponsoredCCTPInterface } from "../../../interfaces/SponsoredCCTPInterface.sol"; @@ -11,26 +10,44 @@ import { HyperCoreFlowExecutor } from "../HyperCoreFlowExecutor.sol"; import { ArbitraryEVMFlowExecutor } from "../ArbitraryEVMFlowExecutor.sol"; import { CommonFlowParams, EVMFlowParams } from "../Structs.sol"; +import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + /** * @title SponsoredCCTPDstPeriphery * @notice Destination chain periphery contract that supports sponsored/non-sponsored CCTP deposits. * @dev This contract is used to receive tokens via CCTP and execute the flow accordingly. + * @dev IMPORTANT. `BaseModuleHandler` should always be the first contract in inheritance chain. Read + `BaseModuleHandler` contract code to learn more. */ -contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecutor, ArbitraryEVMFlowExecutor { +contract SponsoredCCTPDstPeriphery is BaseModuleHandler, SponsoredCCTPInterface, ArbitraryEVMFlowExecutor { using SafeERC20 for IERC20Metadata; using Bytes32ToAddress for bytes32; /// @notice The CCTP message transmitter contract. IMessageTransmitterV2 public immutable cctpMessageTransmitter; - /// @notice The public key of the signer that was used to sign the quotes. - address public signer; + /// @notice Base token associated with this handler. The one we receive from the CCTP bridge + address public immutable baseToken; + + /// @custom:storage-location erc7201:SponsoredCCTPDstPeriphery.main + struct MainStorage { + /// @notice The public key of the signer that was used to sign the quotes. + address signer; + /// @notice Allow a buffer for quote deadline validation. CCTP transfer might have taken a while to finalize + uint256 quoteDeadlineBuffer; + /// @notice A mapping of used nonces to prevent replay attacks. + mapping(bytes32 => bool) usedNonces; + } - /// @notice Allow a buffer for quote deadline validation. CCTP transfer might have taken a while to finalize - uint256 public quoteDeadlineBuffer = 30 minutes; + // keccak256(abi.encode(uint256(keccak256("erc7201:SponsoredCCTPDstPeriphery.main")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 private constant MAIN_STORAGE_LOCATION = 0xb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f00; - /// @notice A mapping of used nonces to prevent replay attacks. - mapping(bytes32 => bool) public usedNonces; + function _getMainStorage() private pure returns (MainStorage storage $) { + assembly { + $.slot := MAIN_STORAGE_LOCATION + } + } /** * @notice Constructor for the SponsoredCCTPDstPeriphery contract. @@ -46,25 +63,97 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu address _donationBox, address _baseToken, address _multicallHandler - ) HyperCoreFlowExecutor(_donationBox, _baseToken) ArbitraryEVMFlowExecutor(_multicallHandler) { + ) BaseModuleHandler(_donationBox, _baseToken, DEFAULT_ADMIN_ROLE) ArbitraryEVMFlowExecutor(_multicallHandler) { + baseToken = _baseToken; + cctpMessageTransmitter = IMessageTransmitterV2(_cctpMessageTransmitter); - signer = _signer; + + MainStorage storage $ = _getMainStorage(); + $.signer = _signer; + $.quoteDeadlineBuffer = 30 minutes; + + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + } + + /** + * @notice Returns the signer address that is used to validate the signatures of the quotes. + * @return The signer address. + */ + function signer() external view returns (address) { + return _getMainStorage().signer; + } + + /** + * @notice Returns the quote deadline buffer. + * @return The quote deadline buffer. + */ + function quoteDeadlineBuffer() external view returns (uint256) { + return _getMainStorage().quoteDeadlineBuffer; + } + + /** + * @notice Returns true if the nonce has been used, false otherwise. + * @param nonce The nonce to check. + * @return True if the nonce has been used, false otherwise. + */ + function usedNonces(bytes32 nonce) external view returns (bool) { + return _getMainStorage().usedNonces[nonce]; } /** * @notice Sets the signer address that is used to validate the signatures of the quotes. * @param _signer The new signer address. */ - function setSigner(address _signer) external nonReentrant onlyDefaultAdmin { - signer = _signer; + function setSigner(address _signer) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) { + _getMainStorage().signer = _signer; } /** * @notice Sets the quote deadline buffer. This is used to prevent the quote from being used after it has expired. * @param _quoteDeadlineBuffer The new quote deadline buffer. */ - function setQuoteDeadlineBuffer(uint256 _quoteDeadlineBuffer) external nonReentrant onlyDefaultAdmin { - quoteDeadlineBuffer = _quoteDeadlineBuffer; + function setQuoteDeadlineBuffer(uint256 _quoteDeadlineBuffer) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) { + _getMainStorage().quoteDeadlineBuffer = _quoteDeadlineBuffer; + } + + /** + * @notice Emmergency function that can be used to recover funds in cases where it is not possible to go + * through the normal flow (e.g. HyperEVM <> HyperCore USDC bridge is blacklisted). Receives the message from + * CCTP and then sends it to final recipient + * @param message The message that is received from CCTP. + * @param attestation The attestation that is received from CCTP. + */ + function emergencyReceiveMessage( + bytes memory message, + bytes memory attestation + ) external nonReentrant onlyRole(PERMISSIONED_BOT_ROLE) { + bool success = cctpMessageTransmitter.receiveMessage(message, attestation); + if (!success) { + return; + } + + // Use try-catch to handle potential abi.decode reverts gracefully + try this.validateMessage(message) returns (bool isValid) { + if (!isValid) { + return; + } + } catch { + // Malformed message that causes abi.decode to revert then early return + return; + } + (SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, uint256 feeExecuted) = SponsoredCCTPQuoteLib + .getSponsoredCCTPQuoteData(message); + + _getMainStorage().usedNonces[quote.nonce] = true; + + IERC20Metadata(baseToken).safeTransfer(quote.finalRecipient.toAddress(), quote.amount - feeExecuted); + + emit EmergencyReceiveMessage( + quote.nonce, + quote.finalRecipient.toAddress(), + baseToken, + quote.amount - feeExecuted + ); } /** @@ -78,15 +167,23 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu bytes memory message, bytes memory attestation, bytes memory signature - ) external nonReentrant { - cctpMessageTransmitter.receiveMessage(message, attestation); + ) external nonReentrant authorizeFundedFlow { + bool success = cctpMessageTransmitter.receiveMessage(message, attestation); + if (!success) { + revert CCTPMessageTransmitterFailed(); + } // If the hook data is invalid or the mint recipient is not this contract we cannot process the message // and therefore we exit. In this case the funds will be kept in this contract. - if (!SponsoredCCTPQuoteLib.validateMessage(message)) { + // Use try-catch to handle potential abi.decode reverts gracefully + try this.validateMessage(message) returns (bool isValid) { + if (!isValid) { + return; + } + } catch { + // Malformed message that causes abi.decode to revert then early return return; } - // Extract the quote and the fee that was executed from the message. (SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, uint256 feeExecuted) = SponsoredCCTPQuoteLib .getSponsoredCCTPQuoteData(message); @@ -94,7 +191,7 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu // Validate the quote and the signature. bool isQuoteValid = _isQuoteValid(quote, signature); if (isQuoteValid) { - usedNonces[quote.nonce] = true; + _getMainStorage().usedNonces[quote.nonce] = true; } uint256 amountAfterFees = quote.amount - feeExecuted; @@ -126,25 +223,41 @@ contract SponsoredCCTPDstPeriphery is SponsoredCCTPInterface, HyperCoreFlowExecu }) ); } else { - // Execute standard HyperCore flow (default) - HyperCoreFlowExecutor._executeFlow(commonParams, quote.maxUserSlippageBps); + // Execute standard HyperCore flow (default) via delegatecall + _delegateToHyperCore( + abi.encodeCall(HyperCoreFlowExecutor.executeFlow, (commonParams, quote.maxUserSlippageBps)) + ); } } + /** + * @notice External wrapper for validateMessage to enable try-catch for safe abi.decode handling + * @param message The CCTP message to validate + * @return True if the message is valid, false otherwise + */ + function validateMessage(bytes memory message) external view returns (bool) { + return SponsoredCCTPQuoteLib.validateMessage(message); + } + function _isQuoteValid( SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, bytes memory signature ) internal view returns (bool) { + MainStorage storage $ = _getMainStorage(); return - SponsoredCCTPQuoteLib.validateSignature(signer, quote, signature) && - !usedNonces[quote.nonce] && - quote.deadline + quoteDeadlineBuffer >= block.timestamp; + SponsoredCCTPQuoteLib.validateSignature($.signer, quote, signature) && + !$.usedNonces[quote.nonce] && + quote.deadline + $.quoteDeadlineBuffer >= block.timestamp; } function _executeWithEVMFlow(EVMFlowParams memory params) internal { params.commonParams = ArbitraryEVMFlowExecutor._executeFlow(params); // Route to appropriate destination based on transferToCore flag - (params.transferToCore ? _executeSimpleTransferFlow : _fallbackHyperEVMFlow)(params.commonParams); + _delegateToHyperCore( + params.transferToCore + ? abi.encodeCall(HyperCoreFlowExecutor.executeSimpleTransferFlow, (params.commonParams)) + : abi.encodeCall(HyperCoreFlowExecutor.fallbackHyperEVMFlow, (params.commonParams)) + ); } } diff --git a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol index 6fa2f1ffe..0931e0ef1 100644 --- a/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol @@ -23,11 +23,22 @@ contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { /// @notice The source domain ID for the chain that this contract is deployed on. uint32 public immutable sourceDomain; - /// @notice The signer address that is used to validate the signatures of the quotes. - address public signer; + /// @custom:storage-location erc7201:SponsoredCCTPSrcPeriphery.main + struct MainStorage { + /// @notice The signer address that is used to validate the signatures of the quotes. + address signer; + /// @notice A mapping of used nonces to prevent replay attacks. + mapping(bytes32 => bool) usedNonces; + } + + // keccak256(abi.encode(uint256(keccak256("erc7201:SponsoredCCTPSrcPeriphery.main")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 private constant MAIN_STORAGE_LOCATION = 0xf0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b200; - /// @notice A mapping of used nonces to prevent replay attacks. - mapping(bytes32 => bool) public usedNonces; + function _getMainStorage() private pure returns (MainStorage storage $) { + assembly { + $.slot := MAIN_STORAGE_LOCATION + } + } /** * @notice Constructor for the SponsoredCCTPSrcPeriphery contract. @@ -38,7 +49,24 @@ contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { constructor(address _cctpTokenMessenger, uint32 _sourceDomain, address _signer) { cctpTokenMessenger = ITokenMessengerV2(_cctpTokenMessenger); sourceDomain = _sourceDomain; - signer = _signer; + _getMainStorage().signer = _signer; + } + + /** + * @notice Returns the signer address that is used to validate the signatures of the quotes. + * @return The signer address. + */ + function signer() external view returns (address) { + return _getMainStorage().signer; + } + + /** + * @notice Returns true if the nonce has been used, false otherwise. + * @param nonce The nonce to check. + * @return True if the nonce has been used, false otherwise. + */ + function usedNonces(bytes32 nonce) external view returns (bool) { + return _getMainStorage().usedNonces[nonce]; } /** @@ -47,8 +75,9 @@ contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { * @param signature The signature of the quote. */ function depositForBurn(SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, bytes memory signature) external { - if (!SponsoredCCTPQuoteLib.validateSignature(signer, quote, signature)) revert InvalidSignature(); - if (usedNonces[quote.nonce]) revert InvalidNonce(); + MainStorage storage $ = _getMainStorage(); + if (!SponsoredCCTPQuoteLib.validateSignature($.signer, quote, signature)) revert InvalidSignature(); + if ($.usedNonces[quote.nonce]) revert InvalidNonce(); if (quote.deadline < block.timestamp) revert InvalidDeadline(); if (quote.sourceDomain != sourceDomain) revert InvalidSourceDomain(); @@ -63,7 +92,7 @@ contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { bytes memory hookData ) = SponsoredCCTPQuoteLib.getDepositForBurnData(quote); - usedNonces[quote.nonce] = true; + $.usedNonces[quote.nonce] = true; IERC20(burnToken).safeTransferFrom(msg.sender, address(this), amount); IERC20(burnToken).forceApprove(address(cctpTokenMessenger), amount); @@ -96,6 +125,6 @@ contract SponsoredCCTPSrcPeriphery is SponsoredCCTPInterface, Ownable { * @param _signer The new signer address. */ function setSigner(address _signer) external onlyOwner { - signer = _signer; + _getMainStorage().signer = _signer; } } diff --git a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol index 02cf10e68..add659848 100644 --- a/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol +++ b/contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.23; +import { BaseModuleHandler } from "../BaseModuleHandler.sol"; import { ILayerZeroComposer } from "../../../external/interfaces/ILayerZeroComposer.sol"; import { OFTComposeMsgCodec } from "../../../external/libraries/OFTComposeMsgCodec.sol"; import { ComposeMsgCodec } from "./ComposeMsgCodec.sol"; @@ -14,9 +15,13 @@ import { CommonFlowParams, EVMFlowParams } from "../Structs.sol"; import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; -/// @notice Handler that receives funds from LZ system, checks authorizations(both against LZ system and src chain -/// sender), and forwards authorized params to the `_executeFlow` function -contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEVMFlowExecutor { +/** + * @notice Handler that receives funds from LZ system, checks authorizations(both against LZ system and src chain + sender), and forwards authorized params to the `_executeFlow` function + * @dev IMPORTANT. `BaseModuleHandler` should always be the first contract in inheritance chain. Read + `BaseModuleHandler` contract code to learn more. + */ +contract DstOFTHandler is BaseModuleHandler, ILayerZeroComposer, ArbitraryEVMFlowExecutor { using ComposeMsgCodec for bytes; using Bytes32ToAddress for bytes32; using AddressToBytes32 for address; @@ -29,15 +34,33 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV address public immutable OFT_ENDPOINT_ADDRESS; address public immutable IOFT_ADDRESS; - /// @notice A mapping used to validate an incoming message against a list of authorized src periphery contracts. In - /// bytes32 to support non-EVM src chains - mapping(uint64 eid => bytes32 authorizedSrcPeriphery) public authorizedSrcPeripheryContracts; + /// @notice Base token associated with this handler. The one we receive from the OFT bridge + address public immutable baseToken; + + /// @custom:storage-location erc7201:DstOFTHandler.main + struct MainStorage { + /// @notice A mapping used to validate an incoming message against a list of authorized src periphery contracts. In + /// bytes32 to support non-EVM src chains + mapping(uint64 eid => bytes32 authorizedSrcPeriphery) authorizedSrcPeripheryContracts; + /// @notice A mapping used for nonce uniqueness checks. Our src periphery and LZ should have prevented this already, + /// but I guess better safe than sorry + mapping(bytes32 quoteNonce => bool used) usedNonces; + } + + // keccak256(abi.encode(uint256(keccak256("erc7201:DstOFTHandler.main")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 private constant MAIN_STORAGE_LOCATION = 0xe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de00; - /// @notice A mapping used for nonce uniqueness checks. Our src periphery and LZ should have prevented this already, - /// but I guess better safe than sorry - mapping(bytes32 quoteNonce => bool used) public usedNonces; + function _getMainStorage() private pure returns (MainStorage storage $) { + assembly { + $.slot := MAIN_STORAGE_LOCATION + } + } - /// @notice Emitted when a new authorized src periphery is configured + /** + * @notice Emitted when a new authorized src periphery is configured + * @param srcEid The source chain endpoint ID + * @param srcPeriphery The authorized source periphery contract address (as bytes32) + */ event SetAuthorizedPeriphery(uint32 srcEid, bytes32 srcPeriphery); /// @notice Thrown when trying to call lzCompose from a source periphery that's not been configured in `authorizedSrcPeripheryContracts` @@ -63,9 +86,10 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV address _donationBox, address _baseToken, address _multicallHandler - ) HyperCoreFlowExecutor(_donationBox, _baseToken) ArbitraryEVMFlowExecutor(_multicallHandler) { - // baseToken is assigned on `HyperCoreFlowExecutor` creation - if (baseToken != IOFT(_ioft).token()) { + ) BaseModuleHandler(_donationBox, _baseToken, DEFAULT_ADMIN_ROLE) ArbitraryEVMFlowExecutor(_multicallHandler) { + baseToken = _baseToken; + + if (_baseToken != IOFT(_ioft).token()) { revert TokenIOFTMismatch(); } @@ -74,10 +98,33 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV if (address(IOAppCore(IOFT_ADDRESS).endpoint()) != address(OFT_ENDPOINT_ADDRESS)) { revert IOFTEndpointMismatch(); } + + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + } + + /** + * @notice Returns the authorized src periphery contract for a given source chain endpoint ID. + * @param srcEid The source chain endpoint ID + * @return The authorized src periphery contract address (as bytes32) + */ + function authorizedSrcPeripheryContracts(uint64 srcEid) external view returns (bytes32) { + return _getMainStorage().authorizedSrcPeripheryContracts[srcEid]; + } + + /** + * @notice Returns true if the nonce has been used, false otherwise. + * @param nonce The nonce to check. + * @return True if the nonce has been used, false otherwise. + */ + function usedNonces(bytes32 nonce) external view returns (bool) { + return _getMainStorage().usedNonces[nonce]; } - function setAuthorizedPeriphery(uint32 srcEid, bytes32 srcPeriphery) external nonReentrant onlyDefaultAdmin { - authorizedSrcPeripheryContracts[srcEid] = srcPeriphery; + function setAuthorizedPeriphery( + uint32 srcEid, + bytes32 srcPeriphery + ) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) { + _getMainStorage().authorizedSrcPeripheryContracts[srcEid] = srcPeriphery; emit SetAuthorizedPeriphery(srcEid, srcPeriphery); } @@ -86,6 +133,7 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV * @dev Ensures the message comes from the correct OApp and is sent through the authorized endpoint. * * @param _oApp The address of the OApp that is sending the composed message. + * @param _message The composed message payload containing transfer and execution details */ function lzCompose( address _oApp, @@ -93,7 +141,7 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV bytes calldata _message, address /* _executor */, bytes calldata /* _extraData */ - ) external payable override nonReentrant { + ) external payable override nonReentrant authorizeFundedFlow { _requireAuthorizedMessage(_oApp, _message); // Decode the actual `composeMsg` payload to extract the recipient address @@ -101,16 +149,17 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV // This check is a safety mechanism against blackholing funds. The funds were sent by the authorized periphery // contract, but if the length is unexpected, we require funds be rescued, this is not a situation we aim to - // revover from in `lzCompose` call - if (composeMsg._isValidComposeMsgBytelength() == false) { + // recover from in `lzCompose` call + if (!composeMsg._isValidComposeMsgBytelength()) { revert InvalidComposeMsgFormat(); } bytes32 quoteNonce = composeMsg._getNonce(); - if (usedNonces[quoteNonce]) { + MainStorage storage $ = _getMainStorage(); + if ($.usedNonces[quoteNonce]) { revert NonceAlreadyUsed(); } - usedNonces[quoteNonce] = true; + $.usedNonces[quoteNonce] = true; uint256 amountLD = OFTComposeMsgCodec.amountLD(_message); uint256 maxBpsToSponsor = composeMsg._getMaxBpsToSponsor(); @@ -144,8 +193,8 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV }) ); } else { - // Execute standard HyperCore flow (default) - HyperCoreFlowExecutor._executeFlow(commonParams, maxUserSlippageBps); + // Execute standard HyperCore flow (default) via delegatecall + _delegateToHyperCore(abi.encodeCall(HyperCoreFlowExecutor.executeFlow, (commonParams, maxUserSlippageBps))); } } @@ -153,7 +202,11 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV params.commonParams = ArbitraryEVMFlowExecutor._executeFlow(params); // Route to appropriate destination based on transferToCore flag - (params.transferToCore ? _executeSimpleTransferFlow : _fallbackHyperEVMFlow)(params.commonParams); + _delegateToHyperCore( + params.transferToCore + ? abi.encodeCall(HyperCoreFlowExecutor.executeSimpleTransferFlow, (params.commonParams)) + : abi.encodeCall(HyperCoreFlowExecutor.fallbackHyperEVMFlow, (params.commonParams)) + ); } /// @notice Checks that message was authorized by LayerZero's identity system and that it came from authorized src periphery @@ -170,7 +223,7 @@ contract DstOFTHandler is ILayerZeroComposer, HyperCoreFlowExecutor, ArbitraryEV /// @dev Checks that _message came from the authorized src periphery contract stored in `authorizedSrcPeripheryContracts` function _requireAuthorizedPeriphery(bytes calldata _message) internal view { uint32 _srcEid = OFTComposeMsgCodec.srcEid(_message); - bytes32 authorizedPeriphery = authorizedSrcPeripheryContracts[_srcEid]; + bytes32 authorizedPeriphery = _getMainStorage().authorizedSrcPeripheryContracts[_srcEid]; if (authorizedPeriphery == bytes32(0)) { revert AuthorizedPeripheryNotSet(_srcEid); } diff --git a/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol b/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol index 8d58dbf84..4177bdb6f 100644 --- a/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol +++ b/contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol @@ -30,13 +30,35 @@ contract SponsoredOFTSrcPeriphery is Ownable { /// @notice Source endpoint id uint32 public immutable SRC_EID; - /// @notice Signer public key to check the signed quote against - address public signer; + /// @custom:storage-location erc7201:SponsoredOFTSrcPeriphery.main + struct MainStorage { + /// @notice Signer public key to check the signed quote against + address signer; + /// @notice A mapping to enforce only a single usage per quote + mapping(bytes32 => bool) quoteNonces; + } + + // keccak256(abi.encode(uint256(keccak256("erc7201:SponsoredOFTSrcPeriphery.main")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 private constant MAIN_STORAGE_LOCATION = 0xbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500; - /// @notice A mapping to enforce only a single usage per quote - mapping(bytes32 => bool) public quoteNonces; + function _getMainStorage() private pure returns (MainStorage storage $) { + assembly { + $.slot := MAIN_STORAGE_LOCATION + } + } - /// @notice Event with auxiliary information. To be used in concert with OftSent event to get relevant quote details + /** + * @notice Event with auxiliary information. To be used in concert with OftSent event to get relevant quote details + * @param quoteNonce Unique identifier for this quote/transaction + * @param originSender The address initiating the transfer on the source chain + * @param finalRecipient The final recipient address on the destination chain (as bytes32) + * @param destinationHandler The handler contract address on the destination chain (as bytes32) + * @param quoteDeadline The timestamp by which the quote expires + * @param maxBpsToSponsor Maximum basis points that can be sponsored + * @param maxUserSlippageBps Maximum user slippage in basis points + * @param finalToken The final token address on the destination chain (as bytes32) + * @param sig The signature authorizing this transfer + */ event SponsoredOFTSend( bytes32 indexed quoteNonce, address indexed originSender, @@ -59,6 +81,8 @@ contract SponsoredOFTSrcPeriphery is Ownable { error QuoteExpired(); /// @notice Thrown if Quote nonce was already used error NonceAlreadyUsed(); + /// @notice Thrown when provided msg.value is not sufficient to cover OFT bridging fee + error InsufficientNativeFee(); constructor(address _token, address _oftMessenger, uint32 _srcEid, address _signer) { TOKEN = _token; @@ -70,24 +94,57 @@ contract SponsoredOFTSrcPeriphery is Ownable { if (IOFT(_oftMessenger).token() != _token) { revert TokenIOFTMismatch(); } - signer = _signer; + _getMainStorage().signer = _signer; + } + + /** + * @notice Returns the signer address that is used to validate the signatures of the quotes. + * @return The signer address. + */ + function signer() external view returns (address) { + return _getMainStorage().signer; } - /// @notice Main entrypoint function to start the user flow + /** + * @notice Returns true if the nonce has been used, false otherwise. + * @param nonce The nonce to check. + * @return True if the nonce has been used, false otherwise. + */ + function usedNonces(bytes32 nonce) external view returns (bool) { + return _getMainStorage().quoteNonces[nonce]; + } + + /** + * @notice Main entrypoint function to start the user flow + * @param quote The quote struct containing all transfer parameters + * @param signature The signature authorizing the quote + */ function deposit(Quote calldata quote, bytes calldata signature) external payable { // Step 1: validate quote and mark quote nonce used _validateQuote(quote, signature); - quoteNonces[quote.signedParams.nonce] = true; + _getMainStorage().quoteNonces[quote.signedParams.nonce] = true; // Step 2: build oft send params from quote (SendParam memory sendParam, MessagingFee memory fee, address refundAddress) = _buildOftTransfer(quote); + if (fee.nativeFee > msg.value) { + revert InsufficientNativeFee(); + } + // OFT doesn't refund the unused native fee portion. Instead, it expects precise fee.nativeFee to be transferred + // as msg.value, so we refund the user ourselves + uint256 nativeFeeRefund = msg.value - fee.nativeFee; + if (nativeFeeRefund > 0) { + // Adapted from "@openzeppelin/contracts/utils/Address.sol"; + (bool success, ) = payable(refundAddress).call{ value: nativeFeeRefund }(""); + require(success, "Unable to send value, recipient may have reverted"); + } + // Step 3: pull tokens from user and apporove OFT messenger IERC20(TOKEN).safeTransferFrom(msg.sender, address(this), quote.signedParams.amountLD); IERC20(TOKEN).forceApprove(address(OFT_MESSENGER), quote.signedParams.amountLD); // Step 4: send oft transfer and emit event with auxiliary data - IOFT(OFT_MESSENGER).send{ value: msg.value }(sendParam, fee, refundAddress); + IOFT(OFT_MESSENGER).send{ value: fee.nativeFee }(sendParam, fee, refundAddress); emit SponsoredOFTSend( quote.signedParams.nonce, msg.sender, @@ -138,7 +195,8 @@ contract SponsoredOFTSrcPeriphery is Ownable { } function _validateQuote(Quote calldata quote, bytes calldata signature) internal view { - if (!QuoteSignLib.isSignatureValid(signer, quote.signedParams, signature)) { + MainStorage storage $ = _getMainStorage(); + if (!QuoteSignLib.isSignatureValid($.signer, quote.signedParams, signature)) { revert IncorrectSignature(); } if (quote.signedParams.deadline < block.timestamp) { @@ -147,12 +205,12 @@ contract SponsoredOFTSrcPeriphery is Ownable { if (quote.signedParams.srcEid != SRC_EID) { revert IncorrectSrcEid(); } - if (quoteNonces[quote.signedParams.nonce]) { + if ($.quoteNonces[quote.signedParams.nonce]) { revert NonceAlreadyUsed(); } } function setSigner(address _newSigner) external onlyOwner { - signer = _newSigner; + _getMainStorage().signer = _newSigner; } } diff --git a/contracts/periphery/mintburn/sponsored-oft/Structs.sol b/contracts/periphery/mintburn/sponsored-oft/Structs.sol index b947e6555..6085ec4c8 100644 --- a/contracts/periphery/mintburn/sponsored-oft/Structs.sol +++ b/contracts/periphery/mintburn/sponsored-oft/Structs.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.23; -import { SendParam, MessagingFee } from "../../../interfaces/IOFT.sol"; - /// @notice Execution modes for the sponsored OFT flow enum ExecutionMode { // Send to core and perform swap (if needed) there. diff --git a/hardhat.config.ts b/hardhat.config.ts index 5e059987b..ce7b1f878 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -108,6 +108,7 @@ const config: HardhatUserConfig = { "contracts/Cher_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS, "contracts/Blast_SpokePool.sol": LARGEST_CONTRACT_COMPILER_SETTINGS, "contracts/Tatara_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS, + "contracts/periphery/mintburn/HyperCoreFlowExecutor.sol": LARGE_CONTRACT_COMPILER_SETTINGS, }, }, zksolc: { diff --git a/package.json b/package.json index 0538841e2..559cfd4ec 100644 --- a/package.json +++ b/package.json @@ -118,6 +118,7 @@ "hardhat": "^2.14.0", "hardhat-deploy": "^0.11.12", "hardhat-gas-reporter": "^1.0.8", + "hardhat-preprocessor": "^0.1.5", "husky": "^4.2.3", "mocha": "^9.0.3", "multiformats": "9.9.0", diff --git a/yarn.lock b/yarn.lock index f08f387af..53993f28d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10358,6 +10358,13 @@ hardhat-gas-reporter@^1.0.4, hardhat-gas-reporter@^1.0.8: eth-gas-reporter "^0.2.24" sha1 "^1.1.1" +hardhat-preprocessor@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/hardhat-preprocessor/-/hardhat-preprocessor-0.1.5.tgz#75b22641fd6a680739c995d03bd5f7868eb72144" + integrity sha512-j8m44mmPxpxAAd0G8fPHRHOas/INZdzptSur0TNJvMEGcFdLDhbHHxBcqZVQ/bmiW42q4gC60AP4CXn9EF018g== + dependencies: + murmur-128 "^0.2.1" + hardhat-typechain@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/hardhat-typechain/-/hardhat-typechain-0.3.5.tgz#8e50616a9da348b33bd001168c8fda9c66b7b4af" From b8ec0d7e5bc5886bfcd816184b2f85187eaa9220 Mon Sep 17 00:00:00 2001 From: Ihor Farion <65650773+grasphoper@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:04:22 -0800 Subject: [PATCH 32/47] chore: common branch (#21) Signed-off-by: Ihor Farion Signed-off-by: Matt Rice Signed-off-by: Faisal Usmani Signed-off-by: nicholaspai npai.nyc@gmail.com Signed-off-by: nicholaspai Signed-off-by: Taylor Webb Co-authored-by: Matt Rice Co-authored-by: Faisal Usmani Co-authored-by: nicholaspai <9457025+nicholaspai@users.noreply.github.com> Co-authored-by: Taylor Webb <84364476+tbwebb22@users.noreply.github.com> Co-authored-by: Taylor Webb --- .github/workflows/publish.yml | 29 +- .solhint.json | 3 +- .../42161/run-latest.json | 66 ++ .../8453/run-latest.json | 71 ++ .../999/run-latest.json | 214 ++++++ .../1/run-latest.json | 220 ++++++ .../42161/run-latest.json | 257 +++++++ .../999/run-latest.json | 418 +++++++++++ .../999/run-latest.json | 173 +++++ .../1/run-latest.json | 68 ++ .../42161/run-latest.json | 71 ++ .../999/run-latest.json | 60 ++ broadcast/deployed-addresses.json | 45 ++ broadcast/deployed-addresses.md | 54 ++ .../handlers/HyperliquidDepositHandler.sol | 251 +++++++ contracts/test/MockEndpoint.sol | 16 + contracts/test/MockOFTMessenger.sol | 34 +- .../interfaces/IHyperCoreFlowExecutor.sol | 189 +++++ foundry.toml | 25 +- package.json | 7 +- remappings.txt | 18 + script/113DeploySponsoredCCTPSrcPeriphery.sol | 41 -- script/114DeploySponsoredCCTPDstPeriphery.sol | 55 -- script/115DeploySrcOFTPeriphery.sol | 35 - script/116DeployDstOFTHandler.sol | 50 -- script/DeployHyperliquidDepositHandler.s.sol | 50 ++ script/mintburn/CreateSponsoredDeposit.sol | 142 ---- script/mintburn/LimitOrderCalcCli.s.sol | 69 ++ script/mintburn/LimitOrderCalcUtils.sol | 154 +++++ script/mintburn/ReadHCoreTokenInfoUtil.s.sol | 50 ++ script/mintburn/SetAuthorizedPeriphery.s.sol | 30 - .../113DeploySponsoredCCTPSrcPeriphery.sol | 35 + .../114DeploySponsoredCCTPDstPeriphery.sol | 50 ++ script/mintburn/cctp/config.toml | 70 ++ .../mintburn/cctp/createSponsoredDeposit.sol | 161 +++++ script/mintburn/cctp/setUpTokens.sol | 53 ++ script/mintburn/hypercore-tokens.json | 38 + .../mintburn/oft/CreateSponsoredDeposit.s.sol | 289 ++++++++ script/mintburn/oft/DeployDstHandler.s.sol | 83 +++ script/mintburn/oft/DeploySrcPeriphery.s.sol | 157 +++++ script/mintburn/oft/DstHandlerConfigLib.s.sol | 98 +++ script/mintburn/oft/README.md | 118 ++++ .../oft/UpdateAuthorizedPeripheries.s.sol | 42 ++ script/mintburn/oft/usdt0.toml | 20 + script/utils/DeploymentUtils.sol | 3 +- scripts/hyperliquidDepositHandler.ts | 176 +++++ .../foundry/local/HyperCoreFlowExecutor.t.sol | 170 +++++ .../evm/foundry/local/HyperCoreMockHelper.sol | 160 +++++ .../local/SponsoredOFTSrcPeriphery.t.sol | 250 +++++++ .../local/SponsorredCCTPDstPeriphery.t.sol | 650 ++++++++++++++++++ .../hyper-evm-lib/src/CoreWriterLib.sol | 202 ++++++ .../hyper-evm-lib/src/PrecompileLib.sol | 400 +++++++++++ .../hyper-evm-lib/src/common/HLConstants.sol | 71 ++ .../src/common/HLConversions.sol | 74 ++ .../src/interfaces/ICoreWriter.sol | 6 + .../src/interfaces/ITokenRegistry.sol | 6 + .../hyper-evm-lib/test/BaseSimulatorTest.sol | 44 ++ .../test/simulation/CoreSimulatorLib.sol | 198 ++++++ .../test/simulation/CoreWriterSim.sol | 97 +++ .../test/simulation/HyperCore.sol | 70 ++ .../test/simulation/PrecompileSim.sol | 100 +++ .../simulation/hyper-core/CoreExecution.sol | 575 ++++++++++++++++ .../test/simulation/hyper-core/CoreState.sol | 373 ++++++++++ .../test/simulation/hyper-core/CoreView.sol | 128 ++++ .../hyper-evm-lib/test/utils/RealL1Read.sol | 294 ++++++++ 65 files changed, 7829 insertions(+), 397 deletions(-) create mode 100644 broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/42161/run-latest.json create mode 100644 broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/8453/run-latest.json create mode 100644 broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json create mode 100644 broadcast/CreateSponsoredDeposit.s.sol/1/run-latest.json create mode 100644 broadcast/CreateSponsoredDeposit.s.sol/42161/run-latest.json create mode 100644 broadcast/DeployDstHandler.s.sol/999/run-latest.json create mode 100644 broadcast/DeployHyperliquidDepositHandler.s.sol/999/run-latest.json create mode 100644 broadcast/DeploySrcPeriphery.s.sol/1/run-latest.json create mode 100644 broadcast/DeploySrcPeriphery.s.sol/42161/run-latest.json create mode 100644 broadcast/UpdateAuthorizedPeripheries.s.sol/999/run-latest.json create mode 100644 contracts/handlers/HyperliquidDepositHandler.sol create mode 100644 contracts/test/MockEndpoint.sol create mode 100644 contracts/test/interfaces/IHyperCoreFlowExecutor.sol create mode 100644 remappings.txt delete mode 100644 script/113DeploySponsoredCCTPSrcPeriphery.sol delete mode 100644 script/114DeploySponsoredCCTPDstPeriphery.sol delete mode 100644 script/115DeploySrcOFTPeriphery.sol delete mode 100644 script/116DeployDstOFTHandler.sol create mode 100644 script/DeployHyperliquidDepositHandler.s.sol delete mode 100644 script/mintburn/CreateSponsoredDeposit.sol create mode 100644 script/mintburn/LimitOrderCalcCli.s.sol create mode 100644 script/mintburn/LimitOrderCalcUtils.sol create mode 100644 script/mintburn/ReadHCoreTokenInfoUtil.s.sol delete mode 100644 script/mintburn/SetAuthorizedPeriphery.s.sol create mode 100644 script/mintburn/cctp/113DeploySponsoredCCTPSrcPeriphery.sol create mode 100644 script/mintburn/cctp/114DeploySponsoredCCTPDstPeriphery.sol create mode 100644 script/mintburn/cctp/config.toml create mode 100644 script/mintburn/cctp/createSponsoredDeposit.sol create mode 100644 script/mintburn/cctp/setUpTokens.sol create mode 100644 script/mintburn/hypercore-tokens.json create mode 100644 script/mintburn/oft/CreateSponsoredDeposit.s.sol create mode 100644 script/mintburn/oft/DeployDstHandler.s.sol create mode 100644 script/mintburn/oft/DeploySrcPeriphery.s.sol create mode 100644 script/mintburn/oft/DstHandlerConfigLib.s.sol create mode 100644 script/mintburn/oft/README.md create mode 100644 script/mintburn/oft/UpdateAuthorizedPeripheries.s.sol create mode 100644 script/mintburn/oft/usdt0.toml create mode 100644 scripts/hyperliquidDepositHandler.ts create mode 100644 test/evm/foundry/local/HyperCoreFlowExecutor.t.sol create mode 100644 test/evm/foundry/local/HyperCoreMockHelper.sol create mode 100644 test/evm/foundry/local/SponsoredOFTSrcPeriphery.t.sol create mode 100644 test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/src/CoreWriterLib.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/src/PrecompileLib.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConstants.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConversions.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ICoreWriter.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ITokenRegistry.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/test/BaseSimulatorTest.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreSimulatorLib.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreWriterSim.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/test/simulation/HyperCore.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/test/simulation/PrecompileSim.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreExecution.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreState.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreView.sol create mode 100644 test/evm/foundry/local/external/hyper-evm-lib/test/utils/RealL1Read.sol diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index dac6d5e8d..f0d991876 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,6 +14,7 @@ on: types: [created] permissions: contents: write + id-token: write env: NODE_VERSION: 22.18 EVM_ARTIFACTS_PATHS: | @@ -29,21 +30,23 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 # Note we set an `id` called `release`. We'll use that later... - name: Validate and extract release information id: release uses: manovotny/github-releases-for-automated-package-publishing-action@v2.0.1 - # Setup .npmrc file to publish to npm - - uses: actions/setup-node@v3 + # Enable npm trusted publishing (OIDC) for releases + - uses: actions/setup-node@v4 with: node-version: "${{ env.NODE_VERSION }}" - always-auth: true registry-url: "https://registry.npmjs.org" cache: "yarn" + - name: Update npm + run: npm install -g npm@latest + - name: Install packages run: yarn install --frozen-lockfile @@ -88,25 +91,23 @@ jobs: # If there *is not* a tag (ie. `beta`, `canary`, etc.), we publish a # version of a package (ie. 1.2.3). # - # This example is using npm to publish, but you could just as easily - # use yarn, if you prefer. It's also publishing to the NPM registry, - # thus, it's using `NPM_TOKEN`, but you could just as easily use - # `GITHUB_TOKEN` if you were publishing to the GitHub Package registry. + # This workflow publishes via npm using GitHub's OIDC integration for + # trusted publishing, so no long-lived tokens are required. # This will publish a "pre-release" or "tagged" version of a package. # This will publish a version of a package. - name: Publish version if: steps.release.outputs.tag == '' - run: yarn publish + run: npm publish env: - NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_CONFIG_PROVENANCE: "false" - name: Publish tagged version if: steps.release.outputs.tag != '' - run: yarn publish --tag ${{ steps.release.outputs.tag }} + run: npm publish --tag ${{ steps.release.outputs.tag }} env: - NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_CONFIG_PROVENANCE: "false" # Due to GitHub cache scoping, we cannot easily share the cache between releases even if the cache key is the same. # In order to not slow down the NPM package publishing, we move the building of SVM binaries for GitHub release to a @@ -115,7 +116,7 @@ jobs: name: Release SVM production binaries on GitHub runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Anchor & Solana uses: ./.github/actions/setup-solana-anchor @@ -138,7 +139,7 @@ jobs: name: Release SVM test binaries on GitHub runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Anchor & Solana uses: ./.github/actions/setup-solana-anchor diff --git a/.solhint.json b/.solhint.json index ad76487ec..5f61aa9a7 100644 --- a/.solhint.json +++ b/.solhint.json @@ -3,6 +3,7 @@ "rules": { "compiler-version": ["error", "^0.8.0"], "func-visibility": ["warn", { "ignoreConstructors": true }], - "const-name-snakecase": "off" + "const-name-snakecase": "off", + "no-console": "off" } } diff --git a/broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/42161/run-latest.json b/broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/42161/run-latest.json new file mode 100644 index 000000000..77b94a44c --- /dev/null +++ b/broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/42161/run-latest.json @@ -0,0 +1,66 @@ +{ + "transactions": [ + { + "hash": "0x6c4f6d7537530911757ecc317e0b5a39b5caf7089ab5549cb4299c830a9d854c", + "transactionType": "CREATE", + "contractName": "SponsoredCCTPSrcPeriphery", + "contractAddress": "0xce1ffe01ebb4f8521c12e74363a396ee3d337e1b", + "function": null, + "arguments": ["0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", "3", "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x1271f2", + "value": "0x0", + "input": "0x60c03461012157601f61100738819003918201601f19168301916001600160401b03831184841017610125578084926060946040528339810103126101215761004781610139565b60208201519163ffffffff831683036101215760406100669101610139565b905f549260018060a01b03908160018060a01b03199333858816175f5560405196823391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a31660805260a0527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b200921690825416179055610eb9908161014e8239608051818181610170015281816107b701528181610826015281816108b001526109b3015260a0518181816101b1015261061c0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036101215756fe6080806040526004361015610012575f80fd5b5f905f3560e01c908163238ac93314610b645750806363cd663b146102d45780636c19e78314610261578063715018a6146101fb5780638da5cb5b146101d55780638ddb4a89146101945780639748cf7c14610150578063f2fde38b146100d15763feb6172414610081575f80fd5b346100ce5760203660031901126100ce5760ff604060209260043581527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20184522054166040519015158152f35b80fd5b50346100ce5760203660031901126100ce576004356001600160a01b0380821680920361014c57610100610cb2565b811561014c575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b8280fd5b50346100ce57806003193601126100ce5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100ce57806003193601126100ce57602060405163ffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100ce57806003193601126100ce576001600160a01b036020915416604051908152f35b50346100ce57806003193601126100ce57610214610cb2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346100ce5760203660031901126100ce576004356001600160a01b0381168091036102d05761028f610cb2565b7ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b2009073ffffffffffffffffffffffffffffffffffffffff1982541617905580f35b5080fd5b5034610995576003196040368201126109955767ffffffffffffffff60043511610995576102008091600435360301126109955760405190810181811067ffffffffffffffff82111761097657604052610332600435600401610c01565b8152610342602460043501610c01565b602082015260043560448101356040830152606481013560608301526084810135608083015260a481013560a083015260c481013560c08301526103889060e401610c01565b60e08201526004356101048101356101008301526101248101356101208301526101448101356101408301526101648101356101608301526101848101356101808301526101a48101356101a08301526101c4013560ff81169003610995576004356101c48101356101c08301526101e4013567ffffffffffffffff81116109955761041b906004369181350101610c2e565b6101e082015267ffffffffffffffff602435116109955761044136602435600401610c2e565b6001600160a01b037ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b200541663ffffffff83511663ffffffff60208501511660408501516060860151608087015160a08801519060c08901519263ffffffff60e08b01511694604051966020880198895260408801526060870152608086015260a085015260c084015260e083015261010082015261010081526104e381610ba6565b5190206101008401516101208501516101408601516101608701516101808801516101a08901519060ff6101c08b015116926101e08b01516020815191012094604051966020880198895260408801526060870152608086015260a085015260c084015260e0830152610100820152610100815261056081610ba6565b51902060405191602083015260408201526040815280606081011067ffffffffffffffff6060830111176109765760608101604052602081519101206105a68382610daa565b6005819492941015610b5057159283610b3d575b508215610aac575b505015610a9a576101008201515f527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20160205260ff60405f205416610a88576101208201514211610a765763ffffffff82511663ffffffff7f00000000000000000000000000000000000000000000000000000000000000001603610a6457606082015163ffffffff602084015116906040840151916080850151918260a01c610a525760a08601519260c087015163ffffffff60e089015116610100890151916107036101208b0151936106f56101408d01518d6101608101516101808201516101a0830151916101e060ff6101c086015116940151946040519b8c9860208a015260408901526060880152608087015260a086015260c085015260e084015261010080840152610120830190610c74565b03601f198101855284610bdf565b6101008a01515f527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20160205260405f20600160ff198254161790556040516323b872dd60e01b6020820152336024820152306044820152856064820152606481528060a081011067ffffffffffffffff60a083011117610976578060a061079792016040526001600160a01b038616610cdd565b60405160208101905f8063095ea7b360e01b938481526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166024850152896044850152604484526107ef84610bc3565b835190826001600160a01b038b165af1610807610d7b565b81610a23575b5080610a10575b15610999575b50506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163b15610995575f966001600160a01b036108a3956040519a8b998a9963779b432d60e01b8b5260048b015260248a01526044890152166064870152608486015260a485015260c484015261010060e4840152610104830190610c74565b0381836001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af1801561098a57610959575b506101008201517f42d1b5f3692944aee65b659fda3e120f817f17d8f2ac9a256f6fc5d642a591fe6101808401519361012081015190610140810151906101a06101608201519101519160405193845260208401526040830152606082015260a0608082015280610953339560a0830190610c74565b0390a480f35b90925067ffffffffffffffff8111610976576040525f915f6108dd565b634e487b7160e01b5f52604160045260245ffd5b6040513d5f823e3d90fd5b5f80fd5b6109fa610a09926040519060208201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660248201525f6044820152604481526109eb81610bc3565b6001600160a01b038816610cdd565b6001600160a01b038616610cdd565b5f8061081a565b506001600160a01b0386163b1515610814565b8051801592508215610a38575b50505f61080d565b610a4b9250602080918301019101610cc5565b5f80610a30565b6040516379ec0ed760e11b8152600490fd5b604051631b91204960e01b8152600490fd5b604051631da7447960e21b8152600490fd5b604051633ab3447f60e11b8152600490fd5b604051638baa579f60e01b8152600490fd5b5f91925081906040516020810190630b135d3f60e11b95868352602482015260406044820152610af181610ae3606482018a610c74565b03601f198101835282610bdf565b51915afa90610afe610d7b565b82610b2f575b82610b13575b50505f806105c2565b9091506020818051810103126109955760200151145f80610b0a565b915060208251101591610b04565b6001600160a01b0316811492505f6105ba565b634e487b7160e01b5f52602160045260245ffd5b34610995575f366003190112610995576020906001600160a01b037ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20054168152f35b610120810190811067ffffffffffffffff82111761097657604052565b6080810190811067ffffffffffffffff82111761097657604052565b90601f8019910116810190811067ffffffffffffffff82111761097657604052565b359063ffffffff8216820361099557565b67ffffffffffffffff811161097657601f01601f191660200190565b81601f8201121561099557803590610c4582610c12565b92610c536040519485610bdf565b8284526020838301011161099557815f926020809301838601378301015290565b91908251928382525f5b848110610c9e575050825f602080949584010152601f8019910116010190565b602081830181015184830182015201610c7e565b6001600160a01b035f5416330361099557565b90816020910312610995575180151581036109955790565b6001600160a01b03166040516040810181811067ffffffffffffffff82111761097657610d4b937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460205f948594604052818152015260208151910182855af1610d45610d7b565b91610e5a565b8051908115918215610d61575b50501561099557565b610d749250602080918301019101610cc5565b5f80610d58565b3d15610da5573d90610d8c82610c12565b91610d9a6040519384610bdf565b82523d5f602084013e565b606090565b9060418151145f14610dd657610dd291602082015190606060408401519301515f1a90610ddf565b9091565b50505f90600290565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411610e4f576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa1561098a575f516001600160a01b03811615610e4757905f90565b505f90600190565b505050505f90600390565b9015610e7457815115610e6b575090565b3b156109955790565b50805190811561099557602001fdfea26469706673582212208c663c4d9608c58c567c60bd0a3b59cd34b106eb0822cc185cd73394904c00ef64736f6c6343000818003300000000000000000000000028b5a0e9c621a5badaa536219b3a228c8168cf5d00000000000000000000000000000000000000000000000000000000000000030000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "nonce": "0x1491", + "chainId": "0xa4b1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x1566bc", + "logs": [ + { + "address": "0xce1ffe01ebb4f8521c12e74363a396ee3d337e1b", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xb96390ff4cb98b461e0035779e43c62874dd281b874612d90ceb474c7af460e7", + "blockNumber": "0x183aa1d0", + "blockTimestamp": "0x692f41c1", + "transactionHash": "0x6c4f6d7537530911757ecc317e0b5a39b5caf7089ab5549cb4299c830a9d854c", + "transactionIndex": "0x5", + "logIndex": "0xc", + "removed": false + } + ], + "logsBloom": "0x00000000000000020000000000000000000000000000000000800000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000040000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000010000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x6c4f6d7537530911757ecc317e0b5a39b5caf7089ab5549cb4299c830a9d854c", + "transactionIndex": "0x5", + "blockHash": "0xb96390ff4cb98b461e0035779e43c62874dd281b874612d90ceb474c7af460e7", + "blockNumber": "0x183aa1d0", + "gasUsed": "0xe0c5d", + "effectiveGasPrice": "0xa67930", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0xce1ffe01ebb4f8521c12e74363a396ee3d337e1b", + "gasUsedForL1": "0xc55", + "l1BlockNumber": "0x16d1c86", + "timeboosted": false + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764704723344, + "chain": 42161, + "commit": "4b103fe" +} diff --git a/broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/8453/run-latest.json b/broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/8453/run-latest.json new file mode 100644 index 000000000..2543d0af5 --- /dev/null +++ b/broadcast/113DeploySponsoredCCTPSrcPeriphery.sol/8453/run-latest.json @@ -0,0 +1,71 @@ +{ + "transactions": [ + { + "hash": "0xa7a480cad9b735b44890b80945b33b22d171820c6343349343813bb25e5be017", + "transactionType": "CREATE", + "contractName": "SponsoredCCTPSrcPeriphery", + "contractAddress": "0xa7a8d1efc1ee3e69999d370380949092251a5c20", + "function": null, + "arguments": ["0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", "6", "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x12333d", + "value": "0x0", + "input": "0x60c03461012157601f61100738819003918201601f19168301916001600160401b03831184841017610125578084926060946040528339810103126101215761004781610139565b60208201519163ffffffff831683036101215760406100669101610139565b905f549260018060a01b03908160018060a01b03199333858816175f5560405196823391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a31660805260a0527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b200921690825416179055610eb9908161014e8239608051818181610170015281816107b701528181610826015281816108b001526109b3015260a0518181816101b1015261061c0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036101215756fe6080806040526004361015610012575f80fd5b5f905f3560e01c908163238ac93314610b645750806363cd663b146102d45780636c19e78314610261578063715018a6146101fb5780638da5cb5b146101d55780638ddb4a89146101945780639748cf7c14610150578063f2fde38b146100d15763feb6172414610081575f80fd5b346100ce5760203660031901126100ce5760ff604060209260043581527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20184522054166040519015158152f35b80fd5b50346100ce5760203660031901126100ce576004356001600160a01b0380821680920361014c57610100610cb2565b811561014c575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b8280fd5b50346100ce57806003193601126100ce5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100ce57806003193601126100ce57602060405163ffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100ce57806003193601126100ce576001600160a01b036020915416604051908152f35b50346100ce57806003193601126100ce57610214610cb2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346100ce5760203660031901126100ce576004356001600160a01b0381168091036102d05761028f610cb2565b7ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b2009073ffffffffffffffffffffffffffffffffffffffff1982541617905580f35b5080fd5b5034610995576003196040368201126109955767ffffffffffffffff60043511610995576102008091600435360301126109955760405190810181811067ffffffffffffffff82111761097657604052610332600435600401610c01565b8152610342602460043501610c01565b602082015260043560448101356040830152606481013560608301526084810135608083015260a481013560a083015260c481013560c08301526103889060e401610c01565b60e08201526004356101048101356101008301526101248101356101208301526101448101356101408301526101648101356101608301526101848101356101808301526101a48101356101a08301526101c4013560ff81169003610995576004356101c48101356101c08301526101e4013567ffffffffffffffff81116109955761041b906004369181350101610c2e565b6101e082015267ffffffffffffffff602435116109955761044136602435600401610c2e565b6001600160a01b037ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b200541663ffffffff83511663ffffffff60208501511660408501516060860151608087015160a08801519060c08901519263ffffffff60e08b01511694604051966020880198895260408801526060870152608086015260a085015260c084015260e083015261010082015261010081526104e381610ba6565b5190206101008401516101208501516101408601516101608701516101808801516101a08901519060ff6101c08b015116926101e08b01516020815191012094604051966020880198895260408801526060870152608086015260a085015260c084015260e0830152610100820152610100815261056081610ba6565b51902060405191602083015260408201526040815280606081011067ffffffffffffffff6060830111176109765760608101604052602081519101206105a68382610daa565b6005819492941015610b5057159283610b3d575b508215610aac575b505015610a9a576101008201515f527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20160205260ff60405f205416610a88576101208201514211610a765763ffffffff82511663ffffffff7f00000000000000000000000000000000000000000000000000000000000000001603610a6457606082015163ffffffff602084015116906040840151916080850151918260a01c610a525760a08601519260c087015163ffffffff60e089015116610100890151916107036101208b0151936106f56101408d01518d6101608101516101808201516101a0830151916101e060ff6101c086015116940151946040519b8c9860208a015260408901526060880152608087015260a086015260c085015260e084015261010080840152610120830190610c74565b03601f198101855284610bdf565b6101008a01515f527ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20160205260405f20600160ff198254161790556040516323b872dd60e01b6020820152336024820152306044820152856064820152606481528060a081011067ffffffffffffffff60a083011117610976578060a061079792016040526001600160a01b038616610cdd565b60405160208101905f8063095ea7b360e01b938481526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166024850152896044850152604484526107ef84610bc3565b835190826001600160a01b038b165af1610807610d7b565b81610a23575b5080610a10575b15610999575b50506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163b15610995575f966001600160a01b036108a3956040519a8b998a9963779b432d60e01b8b5260048b015260248a01526044890152166064870152608486015260a485015260c484015261010060e4840152610104830190610c74565b0381836001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af1801561098a57610959575b506101008201517f42d1b5f3692944aee65b659fda3e120f817f17d8f2ac9a256f6fc5d642a591fe6101808401519361012081015190610140810151906101a06101608201519101519160405193845260208401526040830152606082015260a0608082015280610953339560a0830190610c74565b0390a480f35b90925067ffffffffffffffff8111610976576040525f915f6108dd565b634e487b7160e01b5f52604160045260245ffd5b6040513d5f823e3d90fd5b5f80fd5b6109fa610a09926040519060208201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660248201525f6044820152604481526109eb81610bc3565b6001600160a01b038816610cdd565b6001600160a01b038616610cdd565b5f8061081a565b506001600160a01b0386163b1515610814565b8051801592508215610a38575b50505f61080d565b610a4b9250602080918301019101610cc5565b5f80610a30565b6040516379ec0ed760e11b8152600490fd5b604051631b91204960e01b8152600490fd5b604051631da7447960e21b8152600490fd5b604051633ab3447f60e11b8152600490fd5b604051638baa579f60e01b8152600490fd5b5f91925081906040516020810190630b135d3f60e11b95868352602482015260406044820152610af181610ae3606482018a610c74565b03601f198101835282610bdf565b51915afa90610afe610d7b565b82610b2f575b82610b13575b50505f806105c2565b9091506020818051810103126109955760200151145f80610b0a565b915060208251101591610b04565b6001600160a01b0316811492505f6105ba565b634e487b7160e01b5f52602160045260245ffd5b34610995575f366003190112610995576020906001600160a01b037ff0a1b42b86a218bb35dbc2254545839ce4b1bf1d3780b5099e3e0abfc7a5b20054168152f35b610120810190811067ffffffffffffffff82111761097657604052565b6080810190811067ffffffffffffffff82111761097657604052565b90601f8019910116810190811067ffffffffffffffff82111761097657604052565b359063ffffffff8216820361099557565b67ffffffffffffffff811161097657601f01601f191660200190565b81601f8201121561099557803590610c4582610c12565b92610c536040519485610bdf565b8284526020838301011161099557815f926020809301838601378301015290565b91908251928382525f5b848110610c9e575050825f602080949584010152601f8019910116010190565b602081830181015184830182015201610c7e565b6001600160a01b035f5416330361099557565b90816020910312610995575180151581036109955790565b6001600160a01b03166040516040810181811067ffffffffffffffff82111761097657610d4b937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460205f948594604052818152015260208151910182855af1610d45610d7b565b91610e5a565b8051908115918215610d61575b50501561099557565b610d749250602080918301019101610cc5565b5f80610d58565b3d15610da5573d90610d8c82610c12565b91610d9a6040519384610bdf565b82523d5f602084013e565b606090565b9060418151145f14610dd657610dd291602082015190606060408401519301515f1a90610ddf565b9091565b50505f90600290565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411610e4f576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa1561098a575f516001600160a01b03811615610e4757905f90565b505f90600190565b505050505f90600390565b9015610e7457815115610e6b575090565b3b156109955790565b50805190811561099557602001fdfea26469706673582212208c663c4d9608c58c567c60bd0a3b59cd34b106eb0822cc185cd73394904c00ef64736f6c6343000818003300000000000000000000000028b5a0e9c621a5badaa536219b3a228c8168cf5d00000000000000000000000000000000000000000000000000000000000000060000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "nonce": "0x9d0", + "chainId": "0x2105" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x14c0b42", + "logs": [ + { + "address": "0xa7a8d1efc1ee3e69999d370380949092251a5c20", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0x939880c390ff6989e37ee7b43b26af09c2dbbbfbb80dc841225af782e87fa21b", + "blockNumber": "0x2527309", + "blockTimestamp": "0x692f42f5", + "transactionHash": "0xa7a480cad9b735b44890b80945b33b22d171820c6343349343813bb25e5be017", + "transactionIndex": "0x66", + "logIndex": "0x103", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000040000400000000000000000000000000000000008000000000000000000200000000000000000000000000000000000000800000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xa7a480cad9b735b44890b80945b33b22d171820c6343349343813bb25e5be017", + "transactionIndex": "0x66", + "blockHash": "0x939880c390ff6989e37ee7b43b26af09c2dbbbfbb80dc841225af782e87fa21b", + "blockNumber": "0x2527309", + "gasUsed": "0xe0008", + "effectiveGasPrice": "0x73af", + "blobGasUsed": "0xe6aa0", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0xa7a8d1efc1ee3e69999d370380949092251a5c20", + "daFootprintGasScalar": "0x190", + "l1BaseFeeScalar": "0x8dd", + "l1BlobBaseFee": "0x147055", + "l1BlobBaseFeeScalar": "0x101c12", + "l1Fee": "0x1cce5a375", + "l1GasPrice": "0x30d620f", + "l1GasUsed": "0x93a5" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764705036690, + "chain": 8453, + "commit": "4b103fe" +} diff --git a/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json b/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json new file mode 100644 index 000000000..24f6cc66c --- /dev/null +++ b/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json @@ -0,0 +1,214 @@ +{ + "transactions": [ + { + "hash": "0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d", + "transactionType": "CREATE", + "contractName": "DonationBox", + "contractAddress": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "function": null, + "arguments": null, + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x522e7", + "value": "0x0", + "input": "0x6080806040523461005a575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610344908161005f8239f35b5f80fdfe608060409080825260049081361015610016575f80fd5b5f3560e01c908163715018a61461026e5781638da5cb5b1461024c57508063f2fde38b146101d45763f3fef3a31461004c575f80fd5b346101545781600319360112610154578035916001600160a01b0383168093036101545760249261007b6102d2565b8151916020830163a9059cbb60e01b815233868501528535604485015260448452608084019367ffffffffffffffff94818110868211176101c25760c08201818110878211176101b0578452602090527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152515f9182919082865af1923d1561019f573d9181831161018d57805195601f8401601f19908116603f011687019283118784101761017b575052835261013e93503d5f602085013e6102e5565b8051908115918215610158575b50501561015457005b5f80fd5b819250906020918101031261015457602001518015158103610154575f8061014b565b60418891634e487b7160e01b5f52525ffd5b86604187634e487b7160e01b5f52525ffd5b5050915061013e92506060916102e5565b88604189634e487b7160e01b5f52525ffd5b87604188634e487b7160e01b5f52525ffd5b503461015457602036600319011261015457356001600160a01b03808216809203610154576102016102d2565b8115610154575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b34610154575f366003190112610154576020906001600160a01b035f54168152f35b34610154575f366003190112610154576102866102d2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361015457565b90156102ff578151156102f6575090565b3b156101545790565b50805190811561015457602001fdfea2646970667358221220c55691de465342d56d68c31160f5d7d661422ef39ebccaa5223872c823ad6d6964736f6c63430008180033", + "nonce": "0x243", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "transactionType": "CREATE", + "contractName": "SponsoredCCTPDstPeriphery", + "contractAddress": "0x83e245941befbde29682df068bcda006a804eb0c", + "function": null, + "arguments": [ + "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", + "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", + "0x002E76DC036A1efF1488ee5435eE66C6aBF32674", + "0xb88339CB7199b77E23DB6E890353E22632Ba630f", + "0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x8ec9d8", + "value": "0x0", + "input": "0x610100346200027257601f6200833338819003918201601f19168301926001600160401b03929091838511838610176200025e578160a092849260409788528339810103126200027257620000548162000276565b91620000636020830162000276565b926200007185840162000276565b926200008e6080620000866060840162000276565b920162000276565b60015f5586519093615d3e808301918211838310176200025e5788918391620025f583396001600160a01b03978816815284881660208201520301905ff09283156200025457848094166080525f7f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68082527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020528160018a82200181815491557fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff92838380a47f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f80835260018a8420019183835493558380a460a05260e0521660c0527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0080546001600160a01b031916919092161790556107087fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0155620001f7336200028b565b50516122b890816200033d82396080518181816101fe01526112fa015260a051818181610711015281816114650152612030015260c0518181816107db0152610aef015260e0518181816108aa01528181610c3f0152610cf50152f35b86513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b51906001600160a01b03821682036200027257565b6001600160a01b03165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260409020547f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009060ff1662000336575f805260205260405f20815f5260205260405f20600160ff1982541617905533905f7f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b50505f9056fe60806040526004361015610030575b361561002e573461002a5761002236610dbb565b602081519101f35b5f80fd5b005b5f3560e01c806301ffc9a71461017f57806309cfd6751461017a5780631b1062b81461017557806321081d3c14610170578063238ac9331461016b578063248a9ca3146101665780632561efb214610161578063277e661d1461015c5780632f2ff15d1461015757806336568abe14610152578063490e662f1461014d5780634b3b029b146101485780634f7d9d2e14610143578063657cad8a1461013e5780636c19e783146101395780638c73eb041461013457806391d148541461012f578063a217fddf1461012a578063c55dae6314610125578063d547741f146101205763feb617240361000e5761092d565b6108ce565b61088b565b610871565b6107ff565b6107bc565b610735565b6106f2565b6106b6565b61067c565b610642565b6105f7565b610598565b610542565b6104f6565b6104aa565b610465565b61042b565b610388565b6101df565b3461002a57602036600319011261002a5760043563ffffffff60e01b811680910361002a57602090637965db0b60e01b81149081156101c4575b506040519015158152f35b6301ffc9a760e01b1490505f6101b9565b5f91031261002a57565b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff811161024a57604052565b610222565b6040810190811067ffffffffffffffff82111761024a57604052565b6080810190811067ffffffffffffffff82111761024a57604052565b90601f8019910116810190811067ffffffffffffffff82111761024a57604052565b6040519060c0820182811067ffffffffffffffff82111761024a57604052565b604051906102d68261026b565b565b60405190610200820182811067ffffffffffffffff82111761024a57604052565b604051906060820182811067ffffffffffffffff82111761024a57604052565b604051906102d68261024f565b67ffffffffffffffff811161024a57601f01601f191660200190565b81601f8201121561002a5780359061035982610326565b926103676040519485610287565b8284526020838301011161002a57815f926020809301838601378301015290565b3461002a57606036600319011261002a5767ffffffffffffffff60043581811161002a576103ba903690600401610342565b60243582811161002a576103d2903690600401610342565b9160443590811161002a575f926103f0610424923690600401610342565b906103f9610e00565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1009360018555610abc565b5560015f55005b3461002a575f36600319011261002a5760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b3461002a575f36600319011261002a5760206001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f005416604051908152f35b3461002a57602036600319011261002a576004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526020600160405f200154604051908152f35b3461002a57602036600319011261002a5761050f610e00565b61051761175e565b6004357fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f015560015f55005b3461002a57602036600319011261002a5760043567ffffffffffffffff811161002a5761057d6105786020923690600401610342565b611823565b6040519015158152f35b6001600160a01b0381160361002a57565b3461002a57604036600319011261002a5761002e6024356004356105bb82610587565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526105f2600160405f2001546117b4565b6118cf565b3461002a57604036600319011261002a5760243561061481610587565b336001600160a01b038216036106305761002e90600435611988565b60405163334bd91960e11b8152600490fd5b3461002a575f36600319011261002a5760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b3461002a575f36600319011261002a5760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b3461002a575f36600319011261002a5760207fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0154604051908152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57602036600319011261002a5760043561075281610587565b61075a610e00565b61076261175e565b6001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0091167fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905560015f555f80f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57604036600319011261002a57602060ff61086560243561082381610587565b6004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800845260405f20906001600160a01b03165f5260205260405f2090565b54166040519015158152f35b3461002a575f36600319011261002a5760206040515f8152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57604036600319011261002a5761002e6024356004356108f182610587565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052610928600160405f2001546117b4565b611988565b3461002a57602036600319011261002a576004355f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f02602052602060ff60405f2054166040519015158152f35b9081602091031261002a5751801515810361002a5790565b5f5b8381106109a45750505f910152565b8181015183820152602001610995565b906020916109cd81518092818552858086019101610993565b601f01601f1916010190565b90916109f06109fe936040845260408401906109b4565b9160208184039101526109b4565b90565b6040513d5f823e3d90fd5b9060206109fe9281815201906109b4565b634e487b7160e01b5f52601160045260245ffd5b5f19810191908211610a3f57565b610a1d565b91908203918211610a3f57565b634e487b7160e01b5f52602160045260245ffd5b60c09093929193610ab88160e081019660a08091805184526020810151602085015260408101516001600160a01b0380911660408601526060820151166060850152608081015160808501520151910152565b0152565b604051630afd9fa560e31b81526020939192849082908190610ae29087600484016109d9565b03815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af18015610db657610d99575b5060405163277e661d60e01b8152838180610b3a8660048301610a0c565b0381305afa5f9181610d6a575b50610b525750505050565b15610d6557610b63610b6c92610f1f565b92909182611049565b80610d1a575b610b80836060840151610a44565b92610100830151610bee610b9861018086015161129a565b8415610cf057610bde610baf6101a088015161129a565b915b8615610ce957610140880151945b610bc76102a9565b998a528a8a01526001600160a01b03166040890152565b6001600160a01b03166060870152565b608085015260a084015280610cb9575b15610c78576102d69281610c65600160ff610c276101c06101e0610c7398015195015160ff1690565b161492610c326102c9565b9586526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690860152565b604084015215156060830152565b611383565b610cb692610ca3610160610cb193015160405194859363073ffe1360e31b9085015260248401610a65565b03601f198101835282610287565b6112ef565b50565b5060ff610ccb6101c083015160ff1690565b1660018114908115610cde575b50610bfe565b60029150145f610cd8565b5f94610bbf565b610bde7f000000000000000000000000000000000000000000000000000000000000000091610bb1565b610d60610d536101008401515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b805460ff19166001179055565b610b72565b505050565b610d8b919250853d8711610d92575b610d838183610287565b81019061097b565b905f610b47565b503d610d79565b610daf90843d8611610d9257610d838183610287565b505f610b1c565b610a01565b610dc3610e00565b610dcc81610326565b610dd96040519182610287565b81815236821161002a575f602083610df9948383860137830101526112ef565b9060015f55565b60025f541461002a5760025f55565b610e176102d8565b905f82525f60208301525f60408301525f60608301525f60808301525f60a08301525f60c08301525f60e08301525f6101008301525f6101208301525f6101408301525f6101608301525f6101808301525f6101a08301525f6101c083015260606101e0830152565b81601f8201121561002a578051610e9681610326565b92610ea46040519485610287565b8184526020828401011161002a576109fe9160208085019101610993565b9190916101008184031261002a5780519260208201519260408301519260608101519260808201519260a08301519260c081015160ff8116810361002a579260e082015167ffffffffffffffff811161002a576109fe9201610e80565b90610f8a610f2b610e0f565b92610f42610f3882611a38565b63ffffffff168552565b610f5b610f4e82611a5a565b63ffffffff166020860152565b610f6481611a7a565b60a0850152610f82610f7582611a6a565b63ffffffff1660e0860152565b805190611c56565b90610f9482611a8a565b6040840152610fa282611a9a565b6060840152610fb082611aaa565b6080840152610fbe82611aba565b60c0840152611008610fed610fde610fd585611aca565b94805190611cb0565b60208082518301019101610ec2565b6101e08c999394959697989901526101c08b019060ff169052565b6101a0890152610180880152610160870152610140860152610120850152610100840152565b9060018201809211610a3f57565b91908201809211610a3f57565b6112086001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f00541692611086835163ffffffff1690565b93611098602085015163ffffffff1690565b946040850151956111186060870151608088015160a089015160c08a0151916110c860e08c015163ffffffff1690565b936040519c8d9760208901998a96929360e09692959199989461010089019a63ffffffff97888092168b521660208a015260408901526060880152608087015260a086015260c085015216910152565b039561112c601f1997888101835282610287565b5190206111f36111ff61010087019788516101208901986111d48a516111c861014084015193610160810151906101808101516101a0820151906101e06111786101c085015160ff1690565b9301516020815191012093604051988997602089019b8c9490989796929360ff9460e0979361010088019b8852602088015260408701526060860152608085015260a08401521660c08201520152565b03848101835282610287565b5190206040805160208101968752908101919091529283906060820190565b03908101835282610287565b51902090611cdd565b918261124d575b5081611219575090565b6112479150517fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f01549061103c565b42111590565b61129391925061128861128f91515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b5460ff1690565b1590565b905f61120f565b8060a01c6112ae576001600160a01b031690565b6040516379ec0ed760e11b8152600490fd5b3d156112ea573d906112d182610326565b916112df6040519384610287565b82523d5f602084013e565b606090565b5f80916020815191017f00000000000000000000000000000000000000000000000000000000000000005af46113236112c0565b901561132c5790565b602081519101fd5b6102d69092919260c081019360a08091805184526020810151602085015260408101516001600160a01b0380911660408601526060820151166060850152608081015160808501520151910152565b61138b611aea565b5060409081810151916113a983519360208080968301019101611b43565b92808301936113d16113c56113c587516001600160a01b031690565b6001600160a01b031690565b83516370a0823160e01b80825230600483015296909290918490849060249082905afa928315610db6575f9361173f575b506060926114216113c56113c5868a51016001600160a01b0390511690565b865189815230600482015291908690839060249082905afa918215610db6575f92611720575b506114a661145f6113c586516001600160a01b031690565b9361148f7f000000000000000000000000000000000000000000000000000000000000000095868c515191611da9565b89518701513091906001600160a01b031690611fab565b6001600160a01b038094166114c286516001600160a01b031690565b918a515191803b1561002a576114f3935f80948d5196879586948593633a5be8cb60e01b8552309160048601611c28565b03925af18015610db657611707575b5061151a6113c56113c586516001600160a01b031690565b87518a815230600482015291908790839060249082905afa918215610db6575f926116e8575b50036116575750610cb6965061157261156083516001600160a01b031690565b84885101906001600160a01b03169052565b855151915b61158a83885160a0815191015190612126565b60a088510152828751527fb88fc27be67e678ffb77faf8f8bb00d39b66b4845e4f7ec1e623b0f15abd52138751926115cd8785015193516001600160a01b031690565b946116046115e68887519701516001600160a01b031690565b91838b51948594169816968360209093929193604081019481520152565b0390a4835190840151156116365750610ca3610cb19293519351938492632132ff4360e11b9084015260248301611334565b9250610ca3610cb19251938492627f9f5760e91b9084015260248301611334565b846116736113c56113c5878b51016001600160a01b0390511690565b8751998a523060048b0152899060249082905afa908115610db657610cb6985f926116b9575b508082106116b0576116aa91610a44565b91611577565b50505f91611577565b6116da919250863d88116116e1575b6116d28183610287565b810190611c19565b905f611699565b503d6116c8565b611700919250873d89116116e1576116d28183610287565b905f611540565b8061171461171a92610236565b806101d5565b5f611502565b611738919250863d88116116e1576116d28183610287565b905f611447565b611757919350843d86116116e1576116d28183610287565b915f611402565b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161561179657565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff6117fb3360405f20906001600160a01b03165f5260205260405f2090565b5416156118055750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b6102988151106118ca5761183e61183982611ada565b61129a565b6001600160a01b03309116036118ca578051908160941082609418028218808303928311610a3f5782610fde9260206118796118ab96610326565b936118876040519586610287565b83855261189384610326565b8583019390601f19013685370101905e805190611cb0565b505094509250505060a01c1590816118c1575090565b905060a01c1590565b505f90565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205260ff6119178460405f20906001600160a01b03165f5260205260405f2090565b541661198157815f526020526119418260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916600117905533916001600160a01b0316907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205260ff6119d08460405f20906001600160a01b03165f5260205260405f2090565b54161561198157815f526020526119fb8260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916905533916001600160a01b0316907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b6008815110611a48576008015190565b604051632d0483c560e21b8152600490fd5b600c815110611a4857600c015190565b6090815110611a48576090015190565b608c815110611a4857608c015190565b6044815110611a48576044015190565b6064815110611a48576064015190565b6024815110611a48576024015190565b60a4815110611a485760a4015190565b60c4815110611a485760c4015190565b60d8815110611a485760d8015190565b6040519060c0820182811067ffffffffffffffff82111761024a576040525f60a0838281528260208201528260408201528260608201528260808201520152565b67ffffffffffffffff811161024a5760051b60200190565b90602090818382031261002a57825167ffffffffffffffff9384821161002a57019080601f8301121561002a578151611b7b81611b2b565b94604090611b8c6040519788610287565b828752858088019360051b8601019484861161002a57868101935b868510611bb957505050505050505090565b845183811161002a5782019084601f19838903011261002a57845190611bde8261024f565b89830151611beb81610587565b8252858301519185831161002a57611c0a898c80969581960101610e80565b83820152815201940193611ba7565b9081602091031261002a575190565b906109fe94936080936001600160a01b038093168452602084015216604082015281606082015201906109b4565b908151908180821091180218806094108160941802811891828203918211610a3f576020611c8383610326565b93611c916040519586610287565b838552611c9d84610326565b8583019390601f19013685370101905e90565b9081519081808210911802188060e4108160e41802811891828203918211610a3f576020611c8383610326565b611ce783836121a9565b6005819592951015611da457159384611d8e575b508315611d09575b50505090565b5f929350908291604051611d4181610ca36020820194630b135d3f60e11b998a875260248401526040604484015260648301906109b4565b51915afa90611d4e6112c0565b82611d80575b82611d64575b50505f8080611d03565b90915060208180518101031261002a5760200151145f80611d5a565b915060208251101591611d54565b6001600160a01b0383811691161493505f611cfb565b610a51565b905f611e3093819260405194602086019263a9059cbb60e01b84526001600160a01b038093166024880152604487015260448652611de68661026b565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051611e178161024f565b8181520152519082855af1611e2a6112c0565b91612259565b8051908115918215611e46575b50501561002a57565b611e59925060208091830101910161097b565b5f80611e3d565b90611e6a82611b2b565b6040611e796040519283610287565b8382528193611e8a601f1991611b2b565b01905f5b828110611e9b5750505050565b8151906060918281019281841067ffffffffffffffff85111761024a5760209385525f82528390818301525f85830152828701015201611e8e565b8051821015611eea5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b9190916020908181526060916060820192855193604080848601528551809252608085018460808460051b8801019701935f925b848410611f5a575050505050506040906109fe93949501519101906001600160a01b03169052565b9091929394978680600192607f198b82030187528b51906001600160a01b0382511681528580611f95858501518a878601528a8501906109b4565b9301519101529a01940194019294939190611f32565b92918351611fc0611fbb8261102e565b611e60565b925f5b82811061209a575060405163ef8738d360e01b60208201526001600160a01b03918216602482015290831660448083019190915281529394506111f3936109fe93601f1993909261208b9261206f919061201e606482610287565b6120266102f9565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681529060208201525f60408201526120688287611ed6565b5284611ed6565b50612078610319565b9283526001600160a01b03166020830152565b60405193849160208301611efe565b806120b86120aa6001938a611ed6565b51516001600160a01b031690565b6020806120c5848c611ed6565b510151906120e36120d46102f9565b6001600160a01b039094168452565b8201525f60408201526120f68288611ed6565b526121018187611ed6565b5001611fc3565b8115612112570490565b634e487b7160e01b5f52601260045260245ffd5b9190808301809311610a3f57670de0b6b3a76400009283820291808304851490151715610a3f57808201809211610a3f575f198201918211610a3f5761216b91612108565b820391808311610a3f57808202908282041482151715610a3f57828101809111610a3f576109fe9261219f6121a492610a31565b612108565b610a44565b9060418151145f146121d5576121d191602082015190606060408401519301515f1a906121de565b9091565b50505f90600290565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161224e576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15610db6575f516001600160a01b0381161561224657905f90565b505f90600190565b505050505f90600390565b90156122735781511561226a575090565b3b1561002a5790565b50805190811561002a57602001fdfea26469706673582212205aa57543ac0ca275e5f6cf9aea12226da06ca1a789701807a553fd671792689c64736f6c6343000818003360c0346200010457601f62005d3e38819003918201601f19168301916001600160401b038311848410176200010857808492604094855283398101031262000104576200005a602062000052836200011c565b92016200011c565b6001600160a01b0390911660805260a052604051615c0c90816200013282396080518181816108b7015281816109ff0152818161158b0152818161182f01528181612a1701528181614a0501528181614b7c01528181614f68015261504e015260a051818181610670015281816107de01528181611a7b01528181613a6e01528181614188015281816141e401528181614288015281816142b40152818161435301526146990152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001045756fe6080604052600436101562000012575f80fd5b5f803560e01c806246912e146200257057806301ffc9a71462002515578063037a06a414620022c957806304c73f6014620022a9578063057f037014620021f45780631f74a0b5146200217257806321081d3c1462002135578063248a9ca314620020e75780632e748b211462001f7f5780632f2ff15d1462001f1a578063319adf9f1462001b6657806336568abe1462001b1757806337710e201462001abd57806339fff0981462001a225780633b1c6a0114620017275780633cf3a02514620016eb5780634265fe861462001699578063490e662f146200165c5780634b3b029b146200161f578063502a82e21462001554578063521c98ba1462000d9157806369b97ac71462000d7157806379c7b60b1462000d1757806379c7f2891462000c1557806390a0827b1462000bd457806391d148541462000b6657806396cc2cfb14620008f9578063a217fddf14620008db578063a4b672b61462000895578063af5de6f91462000802578063c55dae6314620007bc578063ccbedaec1462000542578063d06e28ed1462000443578063d547741f14620003da578063e38b73a914620003bc578063ea0aaf241462000359578063eb84e7f2146200024b5763ff3eae0014620001e2575f80fd5b34620002485760c0366003190112620002485762000200366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10054156200023657620002339062004e31565b80f35b60405163cd6d8f7d60e01b8152600490fd5b80fd5b503462000248576020366003190112620002485760408160c09260a0835162000274816200270b565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051620002c8816200270b565b6001600160a01b039060028284541693848352600181015490602084019185811683526001600160401b0393849283604088019360a01c1683520154956060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b50346200024857602036600319011262000248576020620003b36200037d620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b50346200024857806003193601126200024857602060405160068152f35b50346200024857604036600319011262000248576200043f600435620003ff620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262000439600160408620015462002e64565b620039b1565b5080f35b503462000248576040366003190112620002485762000461620025de565b602435906fffffffffffffffffffffffffffffffff82168092036200053e576200048a62002e08565b6200049581620032e8565b90836001600160a01b039263ffffffff84608083015116915116813b156200053a57829160448392604051948593849263435354d360e01b845260048401528a60248401525af180156200052f5762000513575b5050167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b6200051e90620026f7565b6200052b57835f620004e9565b8380fd5b6040513d84823e3d90fd5b8280fd5b5f80fd5b50346200024857606036600319011262000248578062000561620025de565b6200056b6200260c565b906200057662002623565b906200058162002d37565b620005bc816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200071a574362000601826001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0380600162000648846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b01541693816001600160401b03938483166200071f575b5050508183166200066e578480f35b7f00000000000000000000000000000000000000000000000000000000000000001684525f8051602062005bb783398151915260205260046040852001541690823b156200071a5760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156200052f5762000702575b8080808480f35b6200070d90620026f7565b6200024857805f620006fb565b505050fd5b1686525f8051602062005bb783398151915260205282600460408820015416853b15620007b85760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152858160648183895af1908115620007ad57869162000791575b82906200065f565b6200079c90620026f7565b620007a957845f62000789565b8480fd5b6040513d88823e3d90fd5b8680fd5b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503462000248576020366003190112620002485760206001600160a01b03620008346200082e620025de565b620031ff565b6109ab60405162000848858301826200277c565b818152848101916200520c8339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152620008898162002744565b51902016604051908152f35b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346200024857806003193601126200024857602090604051908152f35b503462000248576040366003190112620002485762000917620025de565b906200092262002e08565b6200092d8262002f58565b6200093883620032e8565b926200094e60e083510151840b60243562004da2565b9460208401906001600160401b036200097381845116898360a08a01511691620038d2565b1562000b3657869750620009b7856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b620009c48582546200287c565b90556001600160a01b03808616807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051898152a2817f000000000000000000000000000000000000000000000000000000000000000016803b1562000b325760405163f3fef3a360e01b81526001600160a01b038316600482015260248101889052908a908290604490829084905af1801562000b275787918b9162000b08575b505062000a8290608060e0960192848451169062003353565b511692511694510151850b93813b1562000b0457604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156200052f5762000af15750f35b62000afc90620026f7565b620002485780f35b8580fd5b62000b15919250620026f7565b62000b235785895f62000a69565b8880fd5b6040513d8c823e3d90fd5b8980fd5b6040516377e88bc960e11b81526001600160a01b03861660048201526001600160401b0389166024820152604490fd5b50346200024857604036600319011262000248576001600160a01b03604062000b8e620025f5565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020522091165f52602052602060ff60405f2054166040519015158152f35b50346200024857604036600319011262000248576200023362000bf6620025de565b62000c0062002d37565b602435906001600160a01b0333911662003353565b503462000248576020366003190112620002485760a062000c7a62000c39620025de565b62000c4362002d0b565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b0360016040519262000c9384620026db565b805463ffffffff90818116865260ff8160201c1615156020870152818160281c16604087015260481c166060850152015416608082015262000d1560405180926001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565bf35b50346200024857602036600319011262000248576020620003b362000d3b620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b503462000248578060031936011262000248576020604051620f42408152f35b5034620002485760a0366003190112620002485762000daf620025de565b62000db96200263a565b9062000dc46200264e565b6001600160401b0360643516606435036200053e57608435906001600160401b03821682036200053e5762000df862002db1565b62000e0262002c05565b508480604051602081019063ffffffff881682526020815262000e258162002760565b519061080c5afa62000e3662002cd7565b901562001542578051810190602081830312620007b85760208101516001600160401b038111620015055761010081830184031262001505576040519262000e7e8462002727565b602082840101516001600160401b03811162000b325760208201603f828587010101121562000b32576020818486010101519062000ebc8262002cbb565b9162000ecc60405193846200277c565b8083526020840160408284888a01010101116200152a5762000efa9160406020850191878901010162002691565b8452604082840101516001600160401b03811162000b325760208201603f828587010101121562000b3257602081848601010151916001600160401b0383116200152e578260051b6040519362000f5560208301866200277c565b84526020808501920160408285888a01010101116200152a576040838688010101915b60408285888a01010101831062001509575050505060208401526101009062000fa683820160600162003428565b604085015262000fbb6080828501016200512c565b606085015262000fd060a0828501016200512c565b9283608086015262000fe760c08383010162005141565b60a086015262000ffc60e08383010162005141565b60c08601520101519081880b820362001505578160e08401526001600160a01b03808716911603620007b8576200103890870b60643562003829565b506040519162001048836200270b565b825263ffffffff86166020830152821515604083015260608201526001600160401b036064351660808201526001600160401b03831660a08201526001600160a01b03841686525f8051602062005bb783398151915260205260408620815180518051906001600160401b03821162001438578190620010c9855462002c80565b601f8111620014c5575b50602090601f831160011462001458578b926200144c575b50508160011b915f199060031b1c19161782555b60208101518051906001600160401b038211620014385768010000000000000000821162001438576020906001850154836001870155808410620013e5575b500190600184018a5260208a20908a5b8160021c81106200139d5750600319811680820362001334575b50505050936001600160a01b03937f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd82244353969360068460809860026001600160401b039701876040830151168154907bffffffffffffffffffffffffffffffffffffffff0000000000000000606085015160401b169163ffffffff60e01b16171790556003820190898b820151169082549174ff000000000000000000000000000000000000000060a083015160a01b16907fffffffffffffffffff000000000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff00000000000000000000000000000000000000000000169416171717179055620012c5600482018760208601511688198254161781556040850151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b6060830151600582015501908488820151166fffffffffffffffff000000000000000060a0845493015160401b16916fffffffffffffffffffffffffffffffff19161717905563ffffffff6040519816885215156020880152816064351660408801521660608601521692a280f35b928b938c5b81840381106200135d5750505060021c0155838360066001600160401b0362001168565b9091946020620013926001926001600160401b03895116908560031b60031b916001600160401b03809116831b921b19161790565b960192910162001339565b8b8c5b60048110620013b75750838201556001016200114e565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501620013a0565b6200141890600187018d52838d20600380870160021c820192601888831b16806200141f575b500160021c019062004d8a565b5f6200113e565b5f1990818601918254918a03851b1c1690555f6200140b565b634e487b7160e01b8a52604160045260248afd5b015190505f80620010eb565b9250848b5260208b20908b935b601f1984168510620014a9576001945083601f1981161062001490575b505050811b018255620010ff565b01515f1960f88460031b161c191690555f808062001482565b8181015183556020948501946001909301929091019062001465565b620014f390868d5260208d20601f850160051c81019160208610620014fa575b601f0160051c019062004d8a565b5f620010d3565b9091508190620014e5565b8780fd5b906020806040936200151b8662003428565b81520193019290915062000f78565b8b80fd5b634e487b7160e01b8b52604160045260248bfd5b604051639b0c335d60e01b8152600490fd5b503462000248576040366003190112620002485762001572620025de565b6024356200157f62002d37565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156200161b5760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156200052f5762001603575b50506200023391339062003353565b6200160e90620026f7565b6200053a57825f620015f4565b5080fd5b5034620002485780600319360112620002485760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b5034620002485780600319360112620002485760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b5034620002485760c03660031901126200024857620016b8366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576200023390620046c4565b50346200024857602036600319011262000248576200170962002d37565b80808080600435335af16200171d62002cd7565b5015620002485780f35b503462000248576060366003190112620002485762001745620025f5565b6001600160a01b0360443581811692918382036200053e576200176762002e08565b620017728262002f58565b906200177e8462003257565b62000b045760408201511562000b04576001600160401b0391600183608083015116019083821162001a0e57620017bd60e082510151890b8362003829565b5094620017db602083019386855116908760a08601511691620038d2565b1562000b23576200181c906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b620018298682546200287c565b905587837f000000000000000000000000000000000000000000000000000000000000000016803b156200161b5760405163f3fef3a360e01b81526001600160a01b038a166004820152602481018890529082908290604490829084905af180156200052f57620019f0575b50509183826200192494620018bc60e062001913986024975116925101518c0b8962004da2565b508481620019d0575b505050511695604051916020830191169687825260408301526001606083015260608252620018f48262002744565b6040519485926280000360e11b60208501525180928585019062002691565b81010360048101845201826200277c565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062001970906024830190620026b4565b03925af18015620019c557620019b3575b506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b620019be90620026f7565b5f62001981565b6040513d5f823e3d90fd5b620019df620019e79362002847565b168b62003353565b5f8084620018c5565b620019ff9094929394620026f7565b62001505579190875f62001895565b634e487b7160e01b5f52601160045260245ffd5b5034620002485760e0366003190112620002485762001a41366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576001600160a01b0380606083015116907f000000000000000000000000000000000000000000000000000000000000000016145f1462001aae576200023390620046c4565b620002339060c4359062003a45565b50346200024857602036600319011262000248576020620003b362001ae1620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b503462000248576040366003190112620002485762001b35620025f5565b336001600160a01b0382160362001b54576200043f90600435620039b1565b60405163334bd91960e11b8152600490fd5b50346200024857602036600319011262000248576001600160a01b0362001b8c620025de565b62001b9662002c45565b501681525f8051602062005bb783398151915260205260408120906040519162001bc0836200270b565b60405162001bce8162002727565b6040518254818562001be08362002c80565b808352926001811690811562001ef9575060011462001eb4575b62001c08925003826200277c565b815260405160018301805480835290855260208086209083019186915b81600384011062001e72579360069593819362001c73936001600160401b039997549181811062001e5e575b81811062001e47575b81811062001e30575b1062001e21575b5003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c850b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a0830152604051906020825282519060c0602084015262001d31825161010060e08601526101e0850190620026b4565b60208084015185830360df190161010087015280518084529282019492910190835b81811062001e015750505083946001600160401b039260e08385604060a0960151166101208901526001600160a01b036060820151166101408901526001600160a01b0360808201511661016089015260ff858201511661018089015260ff60c0820151166101a08901520151900b6101c086015282602082015116604086015260408101511515606086015260608101516080860152826080820151168286015201511660c08301520390f35b82516001600160401b031686526020958601959092019160010162001d53565b60c01c81526020015f62001c6a565b9260206001918c8560801c16815201930162001c63565b9260206001918c8560401c16815201930162001c5a565b9260206001918c8516815201930162001c51565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162001c25565b5090848652602086209086915b81831062001edc57505090602062001c089282010162001bfa565b602091935080600191548385880101520191019091839262001ec1565b6020925062001c0894915060ff191682840152151560051b82010162001bfa565b50346200024857604036600319011262000248576200043f60043562001f3f620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262001f79600160408620015462002e64565b62003912565b503462000248576080366003190112620002485762001f9d620025de565b62001fa76200260c565b9062001fb262002623565b90606435926fffffffffffffffffffffffffffffffff84168094036200053e5762001fdc62002e08565b62001fe782620032e8565b856001600160a01b03928360808401511692833b156200053a57620020646101048492836040519586948593636f0d192560e11b855260048501906001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565b6001600160401b038091169b8c60a485015216978860c48401528c60e48401525af180156200052f57620020cb575b5050907f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c92604092835195865260208601521692a380f35b620020d990939293620026f7565b62000b045790855f62002093565b50346200024857602036600319011262000248576001604060209260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008452200154604051908152f35b5034620002485780600319360112620002485760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503462000248576060366003190112620002485762002190620025de565b6001600160401b036024358181116200052b57620021b39036906004016200265e565b91909360443591821162000248576020620021ec868686620021d936600489016200265e565b939092620021e662002e08565b6200288a565b604051908152f35b50346200053e5760403660031901126200053e5762002212620025de565b906024356200222062002d37565b6001600160a01b03928360806200223783620032e8565b015116803b156200053e576040516390a0827b60e01b81526001600160a01b038316600482015260248101849052905f908290604490829084905af18015620019c55762002290575b5062000233929333911662003353565b620002339350620022a190620026f7565b5f9262002280565b346200053e575f3660031901126200053e5760206040516305f5e1008152f35b346200053e5760a03660031901126200053e57620022e6620025de565b620022f06200263a565b620022fa6200264e565b6064359063ffffffff938483168093036200053e57608435908582168092036200053e57620023298162002f58565b506200233462002db1565b6001600160a01b03918260016200237b846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b0154168015620024bc575b8360018960405199620023998b620026db565b16988981526020810197151597888152604082018a81526060830190878252856080850197169d8e8852620023fe8a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b9451166cffffffff00000000000000000068ffffffff000000000064ff0000000087549651151560201b16935160281b16935160481b16936cffffffffffffffffffffffffff19161717171781550191511673ffffffffffffffffffffffffffffffffffffffff19825416179055620024778762003257565b156200053e577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9946080946040519788526020880152604087015260608601521692a3005b50620024c882620031ff565b6040516109ab8082018281106001600160401b03821117620025015782916200520c833903905ff58062002386576040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b346200053e5760203660031901126200053e5760043563ffffffff60e01b81168091036200053e57602090637965db0b60e01b81149081156200255e575b506040519015158152f35b6301ffc9a760e01b1490508262002553565b346200053e5760403660031901126200053e57620025dc62002591620025de565b6001600160a01b03620025a36200260c565b91620025ae62002d37565b165f525f8051602062005bb78339815191526020526001600160401b03600460405f20015416339062002ea6565b005b600435906001600160a01b03821682036200053e57565b602435906001600160a01b03821682036200053e57565b602435906001600160401b03821682036200053e57565b604435906001600160401b03821682036200053e57565b6024359063ffffffff821682036200053e57565b6044359081151582036200053e57565b9181601f840112156200053e578235916001600160401b0383116200053e576020808501948460051b0101116200053e57565b5f5b838110620026a35750505f910152565b818101518382015260200162002693565b90602091620026cf8151809281855285808601910162002691565b601f01601f1916010190565b60a081019081106001600160401b038211176200250157604052565b6001600160401b0381116200250157604052565b60c081019081106001600160401b038211176200250157604052565b61010081019081106001600160401b038211176200250157604052565b608081019081106001600160401b038211176200250157604052565b604081019081106001600160401b038211176200250157604052565b90601f801991011681019081106001600160401b038211176200250157604052565b60c09060031901126200053e5760405190620027ba826200270b565b60043582526024356020830152816001600160a01b0360443581811681036200053e57604083015260643590811681036200053e576060820152608435608082015260a060a435910152565b9190811015620028175760051b0190565b634e487b7160e01b5f52603260045260245ffd5b9190916001600160401b038080941691160191821162001a0e57565b9073200000000000000000000000000000000000000091820180921162001a0e57565b906305f5e0ff820180921162001a0e57565b9190820180921162001a0e57565b919392935f945f948083036200053e57620028d5856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200053e57869796939291620028ef8662002f58565b926080620028fd88620032e8565b01906001600160a01b0392838351169460208701976200292a6001600160401b0397888b5116906200343d565b845f9b5b1062002b94575b50508c15925062002b8791505057436200297f886001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5582861662002993575b5050505050505050565b620029c195620029ab60e0865101515f0b8262003829565b85889992995116908660a08901511691620038d2565b1562002b4d575062002a03876001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62002a108782546200287c565b90558682167f00000000000000000000000000000000000000000000000000000000000000008316803b156200053e5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101899052905f908290604490829084905af18015620019c55762002b29575b509062002a9587849360e096958451169062003353565b511693511691510151850b93823b1562000b0457604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156200052f5762002b11575b80808080808062002989565b62002b1d8291620026f7565b62000248578062002b05565b60e094939291995062002b3c90620026f7565b5f98929350909162002a9562002a7e565b8451608001516040516377e88bc960e11b81529084166001600160a01b031660048201526001600160401b03919091166024820152604490fd5b505f985050505050505050565b62002ba6859e9f809c95968462002806565b359062002bb58c858862002806565b359189831683036200053e578a62002bd2938a8a51169262003520565b9490911562002bfb5762002beb8f93926001926200282b565b9b019e9d8f95949192956200292e565b9a9e9d5062002935565b6040519062002c148262002727565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b6040519062002c54826200270b565b5f60a08362002c6262002c05565b81528260208201528260408201528260608201528260808201520152565b90600182811c9216801562002cb0575b602083101462002c9c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162002c90565b6001600160401b0381116200250157601f01601f191660200190565b3d1562002d06573d9062002ceb8262002cbb565b9162002cfb60405193846200277c565b82523d5f602084013e565b606090565b6040519062002d1a82620026db565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a60205260409020547f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f9060ff161562002d935750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161562002dea57565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd60205260409020547f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef69060ff161562002d935750565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f2054161562002d935750565b620019139262002eec92602492604051926001600160a01b0360208501931683526001600160401b03809216604085015216606083015260608252620018f48262002744565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062002f38906024830190620026b4565b03925af18015620019c55762002f4b5750565b62002f5690620026f7565b565b9062002f6362002c45565b506001600160a01b038092165f5260205f8051602062005bb78339815191528152604092835f2084519062002f98826200270b565b85519562002fa68762002727565b80518254905f8162002fb88462002c80565b918282526001948a86821691825f14620031e05750506001146200319f575b62002fe5925003826200277c565b88528083019082519087829384928282549586815201915f52825f20945f5b8160038201106200315a57620030469654928583831062003140575b83831062003124575b83831062003107575b505010620030f8575b50905003826200277c565b858801526006600283015492856001600160401b0394858116858c0152841c1660608a015260ff60038201549760808b019a888a168c52828a60a01c1660a082015260c0810199838160a81c168b5260b01c5f0b60e0820152875260048301549086821690880152841c161515838601526005810154606086015201549082821660808501521c1660a082015293511615159081620030e9575b50156200053e57565b60ff9150511615155f620030e0565b60c01c81520188905f6200303b565b9091946001600160401b038560801c1681520193015f8562003032565b8192956001600160401b03868d1c168152019401908562003029565b8192956001600160401b0386168152019401908562003020565b86546001600160401b038082168652818b1c811696860196909652608081811c9096168a86015260c01c6060850152958201958795508c949093019260040162003004565b5050845f528188805f20855f915b858310620031c657505062002fe5935082010162002fd7565b8091929450548385880101520191018990858593620031ad565b60ff19168482015262002fe594151560051b840101915062002fd79050565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152606081018181106001600160401b03821117620025015760405251902090565b519081151582036200053e57565b5f80916040516001600160a01b036020820192168252602081526200327c8162002760565b51906108105afa6200328d62002cd7565b9015620032d6576020818051810103126200053e5760405190602082018281106001600160401b038211176200250157604052620032ce9060200162003249565b809152151590565b6040516313dd7ccd60e31b8152600490fd5b620032f79062000c4362002d0b565b906001600160a01b036001604051936200331185620026db565b805463ffffffff90818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156200053e57565b60405163a9059cbb60e01b602082019081526001600160a01b039384166024830152604480830195909552938152620033ea9390925f92839291906200339b6064876200277c565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051620033ce8162002760565b8181520152519082855af1620033e362002cd7565b91620051de565b805190811591821562003402575b5050156200053e57565b81925090602091810103126200053e57602062003420910162003249565b5f80620033f8565b51906001600160401b03821682036200053e57565b604080516001600160a01b0392909216602083019081526001600160401b039390931682820152808252915f918291906200347a6060826200277c565b51906108015afa6200348b62002cd7565b9015620034f5576060818051810103126200053e5781516001600160401b0392606082018481118382101762002501578152620034ee6060620034d16020860162003428565b94858552620034e284820162003428565b60208601520162003428565b9101521690565b8151639d2c8fcb60e01b8152600490fd5b6001600160401b03918216908216039190821162001a0e57565b949192909394805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20906001600160a01b039485835416958615620037ab57600284019788549860ff8a60481c166200379957600186015483608086510151168482160362003780576001600160401b039060a01c166001600160401b038b1660ff8c60401c165f146200371f575080926001600160401b03811682115f146200371657620035d79162003506565b995b620035e58b8d6200282b565b9b6001600160401b038d166001600160401b0385161162003704575069010000000000000000009069ff00000000000000000019161790556200362b8160019b62003506565b976001600160401b036020846200364b8d60e0895101515f0b9062003829565b50991695015116843b156200053e5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152925f908490606490829084905af1918215620019c5577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4393604093620036f2575b5060018187541696015416956001600160401b038351921682526020820152a4565b620036fd90620026f7565b5f620036d0565b5f9c508c9b5099505050505050505050565b50505f620035d7565b9092809b93806001600160401b038316105f1462003773576200374f91620037479162003506565b809c6200282b565b816001600160401b038216115f146200376b57505b91620035d9565b905062003764565b50506200374f5f62003747565b60405163358d72d160e01b815260048101879052602490fd5b60405163f7348a7960e01b8152600490fd5b604051631a40316d60e01b8152600490fd5b60ff16604d811162001a0e57600a0a90565b9190820391821162001a0e57565b8115620037e8570490565b634e487b7160e01b5f52601260045260245ffd5b90620f42409182810292818404149015171562001a0e57565b8181029291811591840414171562001a0e57565b9190805f0b80155f14620038475750506001600160401b0382169190565b5f81131562003877575062003863620038739160ff16620037bd565b6001600160401b03841662003815565b9190565b9050607f19811462001a0e5762003893905f0360ff16620037bd565b91620038aa836001600160401b038093166200287c565b5f19810190811162001a0e57620038c684620038ce92620037dd565b938462003815565b1690565b6200390a6200390392936001600160401b0393836001600160a01b03620038fc8780971662002847565b166200343d565b946200282b565b169116101590565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f205416155f14620039aa57825f5260205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f2054165f14620039aa57825f5260205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b9062003a5e6001600160a01b0360408401511662003257565b1562004618576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165f525f8051602062005bb783398151915260205260405f20906040519162003ab6836200270b565b60405162003ac48162002727565b6040518254815f62003ad68362002c80565b8083529260018116908115620045f75750600114620045b2575b62003afe925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b81600384011062004570579360069593819362003b67936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a08301526001600160a01b036060840151165f525f8051602062005bb783398151915260205260405f206040519062003c2e826200270b565b60405162003c3c8162002727565b6040518254815f62003c4e8362002c80565b80835292600181169081156200454f57506001146200450a575b62003c76925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b816003840110620044bd579360069593819362003cdf936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015262003d896001600160a01b03606086015116620032e8565b9184519062003dd662003dbb62003dac60a089015160808a01511515956200287c565b60e0885101515f0b9062004da2565b905060ff60c0885101511660ff60c087510151169162005165565b91156200448c5781925b5f8063ffffffff875116604051602081019182526020815262003e038162002760565b51906108085afa9062003e1562002cd7565b91156200447a576020828051810103126200053e576001600160401b0362003e44602062003e6d940162003428565b60208801519116919015620044665760ff60a062003e669251015116620037bd565b9062003815565b602085015115620044175763ffffffff62003e8e8160608801511662005026565b16620f4240818101811162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c01511691016200287c565b0416906080870151908115155f146200440e5750905b602085015115620043c5576305f5e1008110156200437e57505f5b62003f2462003f0b60a0890151620037fc565b62003f1d895160a08b0151906200287c565b906200287c565b5f1981019190821162001a0e5762003f1d62003f539262003f4c8a5160a08c0151906200287c565b90620037dd565b606482028281046064148315171562001a0e578111620042df57505062003f84855160e0865101515f0b9062004da2565b92909562003faf6001600160401b03602088015116856001600160401b0360a08a01511691620038d2565b156200423b5791608093917f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91860a0856001600160401b036001600160a01b036040819b990151168a606084015116908a8401511515916040519162004014836200270b565b82526020820152828816604082015282871660608201528a81019182528481015f815262004118602086019384515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b03602052856060600260405f206001600160a01b0388511673ffffffffffffffffffffffffffffffffffffffff19825416178155600181016001600160a01b0360208a0151168154907bffffffffffffffff000000000000000000000000000000000000000060408c015160a01b169163ffffffff60e01b161717905501950151168619855416178455511515839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151569ff00000000000000000082549160481b169069ff00000000000000000019161790555194818b60408501511697818d6060870151169a87875197015160405197885260208801521660408601521660608401521688820152a4015116620041ae83826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001662003353565b60e06001600160401b03602084015116925101515f0b90803b156200053e57604051639c45c34b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160401b03909316602484015260448301939093525f90810b6064830152909182908183816084810162002f38565b94505050905062002f56925060208201516001600160401b03604051921682527f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c60206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b9350949350505060208201516001600160a01b0360608401511691606481019081811162001a0e5760630190811162001a0e5760407fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e9162002f569660648351920482526020820152a36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166060820152620046c4565b6001600160401b036305f5e0ff1982011162001a0e57620043be620043b86001600160401b036305f5e100936305f5e0ff190116620037fc565b6200286a565b0462003ef8565b6305f5e100811115620043da57505f62003ef8565b6001600160401b03816305f5e100031162001a0e57620043be620043b86001600160401b036305f5e10093840316620037fc565b90509062003edd565b63ffffffff6200442d8160608801511662005026565b16620f4240808281031162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c0151169103620037cf565b5062003e6660ff60a08951015116620037bd565b604051635cffc5fb60e11b8152600490fd5b6127108181031162001a0e576001600160401b03612710620044b383820383861662003815565b0416919262003de0565b926001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019162003c95565b5090845f5260205f20905f915b8183106200453257505090602062003c769282010162003c68565b602091935080600191548385880101520191019091839262004517565b6020925062003c7694915060ff191682840152151560051b82010162003c68565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162003b1d565b5090845f5260205f20905f915b818310620045da57505090602062003afe9282010162003af0565b6020919350806001915483858801015201910190918392620045bf565b6020925062003afe94915060ff191682840152151560051b82010162003af0565b506080810151909190156200464c5760246001600160a01b03604084015116604051906320a2097d60e11b82526004820152fd5b9080602062002f569201517f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b03604085015116604051908152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b6060906060810151905f6001600160a01b0380931692835f526020945f8051602062005bb78339815191528652604091825f2096835162004705816200270b565b845193620047138562002727565b85518a5491905f81620047268562002c80565b918282526001958887821691825f1462004d6b57505060011462004d2a575b62004756925094939403826200277c565b8652808b019184885193898586948484549283815201935f52845f20965f915b83600384011062004cde575085620047b998549484841062004cc4575b84841062004ca7575b5083831062004c8a575b50501062004c7b5750905003826200277c565b82850152600289015495836001600160401b039788811688880152871c16606086015260038a0154946080958581168783015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528252600660048b01549a60ff8585019c8d8b82169052891c1615158885015260058101546060850152015490878216868401528760a0840192881c168252868901976200485e868a511662003257565b1562004c0d57899a9b5f9a98999a508c612710620048958b519b8b6200488b60a083019e8f51906200287c565b9101519062003815565b04988a5199808b1162004c04575b508d8a8062004b2e575b620048c2620048d191620048e493516200287c565b60e08a5101515f0b9062004da2565b91909782878088511692511691620038d2565b1562004ae65750908c9695949392918a620049f9575b509260e0896200498f94620049c59b9997947fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b99976200496e906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6200497b8d82546200287c565b9055511696511694510151900b9062004da2565b9190928d84620049ca575b5050505050880151965116965192519351938493846040919493926060820195825260208201520152565b0390a4565b620049ee94620049e89188620049e08562002847565b169062003353565b62002ea6565b5f8080808d6200499a565b919293949596505087807f00000000000000000000000000000000000000000000000000000000000000001691875101511690803b156200053e578b5163f3fef3a360e01b81526001600160a01b03929092166004830152602482018a90525f908290604490829084905af1801562004adc578c948f91998098956200498f957fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b9995620049c59d62004ac0575b509497995094509950939597999a9b620048fa565b60e0945062004acf90620026f7565b6200496e5f945062004aab565b8b513d5f823e3d90fd5b9a50505050955050505093957f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c955084915062004b238162004e31565b0151945191168152a3565b62004b45908a8301518c858c51015116906200503f565b1562004b53575b8a620048ad565b509192939495968881999a50602492508389510151168c51928380926370a0823160e01b82528d7f00000000000000000000000000000000000000000000000000000000000000001660048301525afa90811562004adc57918e918e9a999897969594935f9162004bc7575b509962004b4c565b92505098508681813d831162004bfc575b62004be481836200277c565b810103126200053e57518c988e91620048d162004bbf565b503d62004bd8565b99505f620048a3565b505050509296509450839293015115155f1462004c405750505191516320a2097d60e11b81529116600482015260249150fd5b62002f56948294827f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d9401519451169051908152a262004e31565b60c01c81520185905f6200303b565b9091946001600160401b038560801c1681520193015f85620047a6565b956001600160401b03868495981c16815201940190858e6200479c565b6001600160401b0386168752958101959282019262004793565b95509360049296509284919388546001600160401b039283821683528d8483831c169084015260809382851c169083015260c01c8782015201960191019386948c9296938a9562004776565b50508c5f528186805f20865f915b85831062004d5157505062004756935082010162004745565b809192945054838588010152019101879086859362004d38565b60ff1916848201526200475694151560051b8401019150620047459050565b81811062004d96575050565b5f815560010162004d8a565b9190805f0b9081155f1462004dc05750506001600160401b03821690565b5f82131562004e035762004dd8915060ff16620037bd565b918215620037e857620038ce62004dfb82856001600160401b03940690620037cf565b9384620037dd565b505f0380805f0b0362001a0e57620038ce62004e2a6001600160401b039260ff16620037bd565b8462003815565b805161271062004e5862004e4c60a08501938451906200287c565b60808501519062003815565b0481518181115f146200501d5750905b8190602084019182519162004e8f60608701956001600160a01b039485885116906200503f565b1562005014575b8162004f61575b620049c5837f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9462004ed1858a516200287c565b9662004eeb828a51169860408c0199848b51169062003353565b62004f29828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62004f368782546200287c565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b84518316927f00000000000000000000000000000000000000000000000000000000000000008116803b156200053e5760405163f3fef3a360e01b81526001600160a01b03959095166004860152602485018490525f908590604490829084905af1908115620019c5577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f94620049c59262005002575b5093505062004e9d565b6200500d90620026f7565b5f62004ff8565b5f915062004e96565b90509062004e68565b90606463ffffffff8093160291821691820362001a0e57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa918215620019c5575f92620050f2575b5080821094851595620050b2575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080620050ac565b9091506020813d60201162005123575b8162005111602093836200277c565b810103126200053e5751905f6200509e565b3d915062005102565b51906001600160a01b03821682036200053e57565b519060ff821682036200053e57565b9060ff8091169116039060ff821162001a0e57565b919060ff821660ff82168181145f1462005180575050505090565b929391921015620051b857620038ce9192620051b0620051aa6001600160401b0395869362005150565b620037bd565b911662003815565b620038ce91620051d6620051aa6001600160401b0395869362005150565b9116620037dd565b9015620051fb57815115620051f1575090565b3b156200053e5790565b5080519081156200053e57602001fdfe60a080604052346100455733608052610961908161004a8239608051818181608b015281816101ac0152818161038e015281816104a20152818161051b015261069c0152f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f915f3560e01c908163435354d3146104d757816390a0827b1461047b5781639c45c34b14610469578163a703334f1461033c578163de1a324a146100d457508063e94b77c1146100b35763eba61c0e1461006d575f80fd5b346100af57816003193601126100af57602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b82346100d1576100ce6100c5366105b5565b9291909161068d565b80f35b80fd5b91905034610338576101009136600319018381126103345760a01361033057815167ffffffffffffffff939060a081018581118282101761031d5784526101196105a2565b91828252602435918215158303610319576020810192835263ffffffff9360443585811681036102ce578783015260643585811681036102ce5760608301526084356001600160a01b03928382168203610315576080015260a4358881168091036102ce5760c435918983168093036102ce5760e435936fffffffffffffffffffffffffffffffff85168095036102ce577f000000000000000000000000000000000000000000000000000000000000000016330361031557866101dd9116610663565b9451151581156103055782156102f5578a6102e557885196602088019616865288870152606086015260808501525f60a0850152600260c085015260e084015260e08352820194828610908611176102d25761026891602491868652630100000160e01b6101208301526102598251809261012485019061079c565b81010383810186520184610641565b73333333333333333333333333333333333333333392833b156102ce576102a5935f928385518097819582946317938e1360e01b845283016107bd565b03925af19081156102c557506102b9575080f35b6102c391506105fd565b005b513d5f823e3d90fd5b5f80fd5b604183634e487b7160e01b5f525260245ffd5b88516376d4929560e11b81528890fd5b88516313c0a8df60e01b81528890fd5b885163017461b760e71b81528890fd5b8a80fd5b8780fd5b604184634e487b7160e01b5f525260245ffd5b8380fd5b8480fd5b8280fd5b919050346103385760603660031901126103385781359067ffffffffffffffff928383168093036102ce576024356001600160a01b03948582168092036102ce576044359081168091036102ce5786957f000000000000000000000000000000000000000000000000000000000000000016330361046557610259946103fc9260249286519260208401928352878401526060830152606082526103df82610625565b85519687926280000360e11b60208501525180928585019061079c565b73333333333333333333333333333333333333333392833b15610334576104399385928385518097819582946317938e1360e01b845283016107bd565b03925af190811561045c575061044c5750f35b610455906105fd565b6100d15780f35b513d84823e3d90fd5b8580fd5b83346100d1576100ce6100c5366105b5565b91905034610338573660031901126100af57356001600160a01b038082168092036102ce577f00000000000000000000000000000000000000000000000000000000000000001633036100af576100ce906024359033906107e9565b919050346102ce57806003193601126102ce576104f26105a2565b91602435926fffffffffffffffffffffffffffffffff84168094036102ce576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102ce5761054a90610663565b9282519063ffffffff602083019516855283820152828152606081019381851067ffffffffffffffff8611176102d25761026891602491868652630100000b60e01b608083015261025982518092608485019061079c565b6004359063ffffffff821682036102ce57565b60809060031901126102ce576004356001600160a01b03811681036102ce579060243567ffffffffffffffff811681036102ce579060443590606435805f0b81036102ce5790565b67ffffffffffffffff811161061157604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761061157604052565b90601f8019910116810190811067ffffffffffffffff82111761061157604052565b9061271063ffffffff8093160191821161067957565b634e487b7160e01b5f52601160045260245ffd5b9092916001600160a01b0392837f00000000000000000000000000000000000000000000000000000000000000001633036102ce57805f0b80155f1461071c575050915b826106dd575b50505050565b67ffffffffffffffff732000000000000000000000000000000000000000941684018094116106795780610713941691166107e9565b5f8080806106d7565b5f81131561075c57506107319060ff166108f1565b8015610748578106810390811161067957916106d1565b634e487b7160e01b5f52601260045260245ffd5b90505f9391930380805f0b03610679576107789060ff166108f1565b9280938181810204149015176106d157634e487b7160e01b5f52601160045260245ffd5b5f5b8381106107ad5750505f910152565b818101518382015260200161079e565b604091602082526107dd815180928160208601526020868601910161079c565b601f01601f1916010190565b9190604051906020938483019363a9059cbb60e01b85526001600160a01b03809316602485015260448401526044835261082283610625565b169160405190604082019067ffffffffffffffff9383831085841117610611577f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564875f9586956040528181520152519082865af13d156108e4573d918211610611576108ac926040519261089f86601f19601f8401160185610641565b83523d5f8685013e610902565b80519182159182156108c4575b50509050156102ce57565b8092508193810103126102ce57015180151581036102ce57805f806108b9565b6108ac9260609250610902565b60ff16604d811161067957600a0a90565b901561091c57815115610913575090565b3b156102ce5790565b5080519081156102ce57602001fdfea2646970667358221220c909e7dbe656d7c973709e34eb673837a89b1ae88012e9262063134290e6f21f64736f6c634300081800336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00a2646970667358221220d7009235facd2afacf7dbd784aa03c97dd6444be46f4a33aa486147317b81a7764736f6c6343000818003300000000000000000000000081d40f21f12a8f0e3252bccb954d722d4c464b640000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000002e76dc036a1eff1488ee5435ee66c6abf32674000000000000000000000000b88339cb7199b77e23db6e890353e22632ba630f0000000000000000000000005e7840e06faccb6d1c3b5f5e0d1d3d07f2829bba", + "nonce": "0x244", + "chainId": "0x3e7" + }, + "additionalContracts": [ + { + "transactionType": "CREATE", + "contractName": "HyperCoreFlowExecutor", + "address": "0xc5b7162e6a0fde52cc180dc8ab273feb8b32f57d", + "initCode": "0x60c0346200010457601f62005d3e38819003918201601f19168301916001600160401b038311848410176200010857808492604094855283398101031262000104576200005a602062000052836200011c565b92016200011c565b6001600160a01b0390911660805260a052604051615c0c90816200013282396080518181816108b7015281816109ff0152818161158b0152818161182f01528181612a1701528181614a0501528181614b7c01528181614f68015261504e015260a051818181610670015281816107de01528181611a7b01528181613a6e01528181614188015281816141e401528181614288015281816142b40152818161435301526146990152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001045756fe6080604052600436101562000012575f80fd5b5f803560e01c806246912e146200257057806301ffc9a71462002515578063037a06a414620022c957806304c73f6014620022a9578063057f037014620021f45780631f74a0b5146200217257806321081d3c1462002135578063248a9ca314620020e75780632e748b211462001f7f5780632f2ff15d1462001f1a578063319adf9f1462001b6657806336568abe1462001b1757806337710e201462001abd57806339fff0981462001a225780633b1c6a0114620017275780633cf3a02514620016eb5780634265fe861462001699578063490e662f146200165c5780634b3b029b146200161f578063502a82e21462001554578063521c98ba1462000d9157806369b97ac71462000d7157806379c7b60b1462000d1757806379c7f2891462000c1557806390a0827b1462000bd457806391d148541462000b6657806396cc2cfb14620008f9578063a217fddf14620008db578063a4b672b61462000895578063af5de6f91462000802578063c55dae6314620007bc578063ccbedaec1462000542578063d06e28ed1462000443578063d547741f14620003da578063e38b73a914620003bc578063ea0aaf241462000359578063eb84e7f2146200024b5763ff3eae0014620001e2575f80fd5b34620002485760c0366003190112620002485762000200366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10054156200023657620002339062004e31565b80f35b60405163cd6d8f7d60e01b8152600490fd5b80fd5b503462000248576020366003190112620002485760408160c09260a0835162000274816200270b565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051620002c8816200270b565b6001600160a01b039060028284541693848352600181015490602084019185811683526001600160401b0393849283604088019360a01c1683520154956060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b50346200024857602036600319011262000248576020620003b36200037d620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b50346200024857806003193601126200024857602060405160068152f35b50346200024857604036600319011262000248576200043f600435620003ff620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262000439600160408620015462002e64565b620039b1565b5080f35b503462000248576040366003190112620002485762000461620025de565b602435906fffffffffffffffffffffffffffffffff82168092036200053e576200048a62002e08565b6200049581620032e8565b90836001600160a01b039263ffffffff84608083015116915116813b156200053a57829160448392604051948593849263435354d360e01b845260048401528a60248401525af180156200052f5762000513575b5050167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b6200051e90620026f7565b6200052b57835f620004e9565b8380fd5b6040513d84823e3d90fd5b8280fd5b5f80fd5b50346200024857606036600319011262000248578062000561620025de565b6200056b6200260c565b906200057662002623565b906200058162002d37565b620005bc816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200071a574362000601826001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0380600162000648846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b01541693816001600160401b03938483166200071f575b5050508183166200066e578480f35b7f00000000000000000000000000000000000000000000000000000000000000001684525f8051602062005bb783398151915260205260046040852001541690823b156200071a5760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156200052f5762000702575b8080808480f35b6200070d90620026f7565b6200024857805f620006fb565b505050fd5b1686525f8051602062005bb783398151915260205282600460408820015416853b15620007b85760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152858160648183895af1908115620007ad57869162000791575b82906200065f565b6200079c90620026f7565b620007a957845f62000789565b8480fd5b6040513d88823e3d90fd5b8680fd5b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503462000248576020366003190112620002485760206001600160a01b03620008346200082e620025de565b620031ff565b6109ab60405162000848858301826200277c565b818152848101916200520c8339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152620008898162002744565b51902016604051908152f35b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346200024857806003193601126200024857602090604051908152f35b503462000248576040366003190112620002485762000917620025de565b906200092262002e08565b6200092d8262002f58565b6200093883620032e8565b926200094e60e083510151840b60243562004da2565b9460208401906001600160401b036200097381845116898360a08a01511691620038d2565b1562000b3657869750620009b7856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b620009c48582546200287c565b90556001600160a01b03808616807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051898152a2817f000000000000000000000000000000000000000000000000000000000000000016803b1562000b325760405163f3fef3a360e01b81526001600160a01b038316600482015260248101889052908a908290604490829084905af1801562000b275787918b9162000b08575b505062000a8290608060e0960192848451169062003353565b511692511694510151850b93813b1562000b0457604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156200052f5762000af15750f35b62000afc90620026f7565b620002485780f35b8580fd5b62000b15919250620026f7565b62000b235785895f62000a69565b8880fd5b6040513d8c823e3d90fd5b8980fd5b6040516377e88bc960e11b81526001600160a01b03861660048201526001600160401b0389166024820152604490fd5b50346200024857604036600319011262000248576001600160a01b03604062000b8e620025f5565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020522091165f52602052602060ff60405f2054166040519015158152f35b50346200024857604036600319011262000248576200023362000bf6620025de565b62000c0062002d37565b602435906001600160a01b0333911662003353565b503462000248576020366003190112620002485760a062000c7a62000c39620025de565b62000c4362002d0b565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b0360016040519262000c9384620026db565b805463ffffffff90818116865260ff8160201c1615156020870152818160281c16604087015260481c166060850152015416608082015262000d1560405180926001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565bf35b50346200024857602036600319011262000248576020620003b362000d3b620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b503462000248578060031936011262000248576020604051620f42408152f35b5034620002485760a0366003190112620002485762000daf620025de565b62000db96200263a565b9062000dc46200264e565b6001600160401b0360643516606435036200053e57608435906001600160401b03821682036200053e5762000df862002db1565b62000e0262002c05565b508480604051602081019063ffffffff881682526020815262000e258162002760565b519061080c5afa62000e3662002cd7565b901562001542578051810190602081830312620007b85760208101516001600160401b038111620015055761010081830184031262001505576040519262000e7e8462002727565b602082840101516001600160401b03811162000b325760208201603f828587010101121562000b32576020818486010101519062000ebc8262002cbb565b9162000ecc60405193846200277c565b8083526020840160408284888a01010101116200152a5762000efa9160406020850191878901010162002691565b8452604082840101516001600160401b03811162000b325760208201603f828587010101121562000b3257602081848601010151916001600160401b0383116200152e578260051b6040519362000f5560208301866200277c565b84526020808501920160408285888a01010101116200152a576040838688010101915b60408285888a01010101831062001509575050505060208401526101009062000fa683820160600162003428565b604085015262000fbb6080828501016200512c565b606085015262000fd060a0828501016200512c565b9283608086015262000fe760c08383010162005141565b60a086015262000ffc60e08383010162005141565b60c08601520101519081880b820362001505578160e08401526001600160a01b03808716911603620007b8576200103890870b60643562003829565b506040519162001048836200270b565b825263ffffffff86166020830152821515604083015260608201526001600160401b036064351660808201526001600160401b03831660a08201526001600160a01b03841686525f8051602062005bb783398151915260205260408620815180518051906001600160401b03821162001438578190620010c9855462002c80565b601f8111620014c5575b50602090601f831160011462001458578b926200144c575b50508160011b915f199060031b1c19161782555b60208101518051906001600160401b038211620014385768010000000000000000821162001438576020906001850154836001870155808410620013e5575b500190600184018a5260208a20908a5b8160021c81106200139d5750600319811680820362001334575b50505050936001600160a01b03937f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd82244353969360068460809860026001600160401b039701876040830151168154907bffffffffffffffffffffffffffffffffffffffff0000000000000000606085015160401b169163ffffffff60e01b16171790556003820190898b820151169082549174ff000000000000000000000000000000000000000060a083015160a01b16907fffffffffffffffffff000000000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff00000000000000000000000000000000000000000000169416171717179055620012c5600482018760208601511688198254161781556040850151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b6060830151600582015501908488820151166fffffffffffffffff000000000000000060a0845493015160401b16916fffffffffffffffffffffffffffffffff19161717905563ffffffff6040519816885215156020880152816064351660408801521660608601521692a280f35b928b938c5b81840381106200135d5750505060021c0155838360066001600160401b0362001168565b9091946020620013926001926001600160401b03895116908560031b60031b916001600160401b03809116831b921b19161790565b960192910162001339565b8b8c5b60048110620013b75750838201556001016200114e565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501620013a0565b6200141890600187018d52838d20600380870160021c820192601888831b16806200141f575b500160021c019062004d8a565b5f6200113e565b5f1990818601918254918a03851b1c1690555f6200140b565b634e487b7160e01b8a52604160045260248afd5b015190505f80620010eb565b9250848b5260208b20908b935b601f1984168510620014a9576001945083601f1981161062001490575b505050811b018255620010ff565b01515f1960f88460031b161c191690555f808062001482565b8181015183556020948501946001909301929091019062001465565b620014f390868d5260208d20601f850160051c81019160208610620014fa575b601f0160051c019062004d8a565b5f620010d3565b9091508190620014e5565b8780fd5b906020806040936200151b8662003428565b81520193019290915062000f78565b8b80fd5b634e487b7160e01b8b52604160045260248bfd5b604051639b0c335d60e01b8152600490fd5b503462000248576040366003190112620002485762001572620025de565b6024356200157f62002d37565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156200161b5760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156200052f5762001603575b50506200023391339062003353565b6200160e90620026f7565b6200053a57825f620015f4565b5080fd5b5034620002485780600319360112620002485760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b5034620002485780600319360112620002485760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b5034620002485760c03660031901126200024857620016b8366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576200023390620046c4565b50346200024857602036600319011262000248576200170962002d37565b80808080600435335af16200171d62002cd7565b5015620002485780f35b503462000248576060366003190112620002485762001745620025f5565b6001600160a01b0360443581811692918382036200053e576200176762002e08565b620017728262002f58565b906200177e8462003257565b62000b045760408201511562000b04576001600160401b0391600183608083015116019083821162001a0e57620017bd60e082510151890b8362003829565b5094620017db602083019386855116908760a08601511691620038d2565b1562000b23576200181c906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b620018298682546200287c565b905587837f000000000000000000000000000000000000000000000000000000000000000016803b156200161b5760405163f3fef3a360e01b81526001600160a01b038a166004820152602481018890529082908290604490829084905af180156200052f57620019f0575b50509183826200192494620018bc60e062001913986024975116925101518c0b8962004da2565b508481620019d0575b505050511695604051916020830191169687825260408301526001606083015260608252620018f48262002744565b6040519485926280000360e11b60208501525180928585019062002691565b81010360048101845201826200277c565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062001970906024830190620026b4565b03925af18015620019c557620019b3575b506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b620019be90620026f7565b5f62001981565b6040513d5f823e3d90fd5b620019df620019e79362002847565b168b62003353565b5f8084620018c5565b620019ff9094929394620026f7565b62001505579190875f62001895565b634e487b7160e01b5f52601160045260245ffd5b5034620002485760e0366003190112620002485762001a41366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576001600160a01b0380606083015116907f000000000000000000000000000000000000000000000000000000000000000016145f1462001aae576200023390620046c4565b620002339060c4359062003a45565b50346200024857602036600319011262000248576020620003b362001ae1620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b503462000248576040366003190112620002485762001b35620025f5565b336001600160a01b0382160362001b54576200043f90600435620039b1565b60405163334bd91960e11b8152600490fd5b50346200024857602036600319011262000248576001600160a01b0362001b8c620025de565b62001b9662002c45565b501681525f8051602062005bb783398151915260205260408120906040519162001bc0836200270b565b60405162001bce8162002727565b6040518254818562001be08362002c80565b808352926001811690811562001ef9575060011462001eb4575b62001c08925003826200277c565b815260405160018301805480835290855260208086209083019186915b81600384011062001e72579360069593819362001c73936001600160401b039997549181811062001e5e575b81811062001e47575b81811062001e30575b1062001e21575b5003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c850b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a0830152604051906020825282519060c0602084015262001d31825161010060e08601526101e0850190620026b4565b60208084015185830360df190161010087015280518084529282019492910190835b81811062001e015750505083946001600160401b039260e08385604060a0960151166101208901526001600160a01b036060820151166101408901526001600160a01b0360808201511661016089015260ff858201511661018089015260ff60c0820151166101a08901520151900b6101c086015282602082015116604086015260408101511515606086015260608101516080860152826080820151168286015201511660c08301520390f35b82516001600160401b031686526020958601959092019160010162001d53565b60c01c81526020015f62001c6a565b9260206001918c8560801c16815201930162001c63565b9260206001918c8560401c16815201930162001c5a565b9260206001918c8516815201930162001c51565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162001c25565b5090848652602086209086915b81831062001edc57505090602062001c089282010162001bfa565b602091935080600191548385880101520191019091839262001ec1565b6020925062001c0894915060ff191682840152151560051b82010162001bfa565b50346200024857604036600319011262000248576200043f60043562001f3f620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262001f79600160408620015462002e64565b62003912565b503462000248576080366003190112620002485762001f9d620025de565b62001fa76200260c565b9062001fb262002623565b90606435926fffffffffffffffffffffffffffffffff84168094036200053e5762001fdc62002e08565b62001fe782620032e8565b856001600160a01b03928360808401511692833b156200053a57620020646101048492836040519586948593636f0d192560e11b855260048501906001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565b6001600160401b038091169b8c60a485015216978860c48401528c60e48401525af180156200052f57620020cb575b5050907f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c92604092835195865260208601521692a380f35b620020d990939293620026f7565b62000b045790855f62002093565b50346200024857602036600319011262000248576001604060209260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008452200154604051908152f35b5034620002485780600319360112620002485760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503462000248576060366003190112620002485762002190620025de565b6001600160401b036024358181116200052b57620021b39036906004016200265e565b91909360443591821162000248576020620021ec868686620021d936600489016200265e565b939092620021e662002e08565b6200288a565b604051908152f35b50346200053e5760403660031901126200053e5762002212620025de565b906024356200222062002d37565b6001600160a01b03928360806200223783620032e8565b015116803b156200053e576040516390a0827b60e01b81526001600160a01b038316600482015260248101849052905f908290604490829084905af18015620019c55762002290575b5062000233929333911662003353565b620002339350620022a190620026f7565b5f9262002280565b346200053e575f3660031901126200053e5760206040516305f5e1008152f35b346200053e5760a03660031901126200053e57620022e6620025de565b620022f06200263a565b620022fa6200264e565b6064359063ffffffff938483168093036200053e57608435908582168092036200053e57620023298162002f58565b506200233462002db1565b6001600160a01b03918260016200237b846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b0154168015620024bc575b8360018960405199620023998b620026db565b16988981526020810197151597888152604082018a81526060830190878252856080850197169d8e8852620023fe8a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b9451166cffffffff00000000000000000068ffffffff000000000064ff0000000087549651151560201b16935160281b16935160481b16936cffffffffffffffffffffffffff19161717171781550191511673ffffffffffffffffffffffffffffffffffffffff19825416179055620024778762003257565b156200053e577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9946080946040519788526020880152604087015260608601521692a3005b50620024c882620031ff565b6040516109ab8082018281106001600160401b03821117620025015782916200520c833903905ff58062002386576040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b346200053e5760203660031901126200053e5760043563ffffffff60e01b81168091036200053e57602090637965db0b60e01b81149081156200255e575b506040519015158152f35b6301ffc9a760e01b1490508262002553565b346200053e5760403660031901126200053e57620025dc62002591620025de565b6001600160a01b03620025a36200260c565b91620025ae62002d37565b165f525f8051602062005bb78339815191526020526001600160401b03600460405f20015416339062002ea6565b005b600435906001600160a01b03821682036200053e57565b602435906001600160a01b03821682036200053e57565b602435906001600160401b03821682036200053e57565b604435906001600160401b03821682036200053e57565b6024359063ffffffff821682036200053e57565b6044359081151582036200053e57565b9181601f840112156200053e578235916001600160401b0383116200053e576020808501948460051b0101116200053e57565b5f5b838110620026a35750505f910152565b818101518382015260200162002693565b90602091620026cf8151809281855285808601910162002691565b601f01601f1916010190565b60a081019081106001600160401b038211176200250157604052565b6001600160401b0381116200250157604052565b60c081019081106001600160401b038211176200250157604052565b61010081019081106001600160401b038211176200250157604052565b608081019081106001600160401b038211176200250157604052565b604081019081106001600160401b038211176200250157604052565b90601f801991011681019081106001600160401b038211176200250157604052565b60c09060031901126200053e5760405190620027ba826200270b565b60043582526024356020830152816001600160a01b0360443581811681036200053e57604083015260643590811681036200053e576060820152608435608082015260a060a435910152565b9190811015620028175760051b0190565b634e487b7160e01b5f52603260045260245ffd5b9190916001600160401b038080941691160191821162001a0e57565b9073200000000000000000000000000000000000000091820180921162001a0e57565b906305f5e0ff820180921162001a0e57565b9190820180921162001a0e57565b919392935f945f948083036200053e57620028d5856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200053e57869796939291620028ef8662002f58565b926080620028fd88620032e8565b01906001600160a01b0392838351169460208701976200292a6001600160401b0397888b5116906200343d565b845f9b5b1062002b94575b50508c15925062002b8791505057436200297f886001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5582861662002993575b5050505050505050565b620029c195620029ab60e0865101515f0b8262003829565b85889992995116908660a08901511691620038d2565b1562002b4d575062002a03876001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62002a108782546200287c565b90558682167f00000000000000000000000000000000000000000000000000000000000000008316803b156200053e5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101899052905f908290604490829084905af18015620019c55762002b29575b509062002a9587849360e096958451169062003353565b511693511691510151850b93823b1562000b0457604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156200052f5762002b11575b80808080808062002989565b62002b1d8291620026f7565b62000248578062002b05565b60e094939291995062002b3c90620026f7565b5f98929350909162002a9562002a7e565b8451608001516040516377e88bc960e11b81529084166001600160a01b031660048201526001600160401b03919091166024820152604490fd5b505f985050505050505050565b62002ba6859e9f809c95968462002806565b359062002bb58c858862002806565b359189831683036200053e578a62002bd2938a8a51169262003520565b9490911562002bfb5762002beb8f93926001926200282b565b9b019e9d8f95949192956200292e565b9a9e9d5062002935565b6040519062002c148262002727565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b6040519062002c54826200270b565b5f60a08362002c6262002c05565b81528260208201528260408201528260608201528260808201520152565b90600182811c9216801562002cb0575b602083101462002c9c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162002c90565b6001600160401b0381116200250157601f01601f191660200190565b3d1562002d06573d9062002ceb8262002cbb565b9162002cfb60405193846200277c565b82523d5f602084013e565b606090565b6040519062002d1a82620026db565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a60205260409020547f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f9060ff161562002d935750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161562002dea57565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd60205260409020547f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef69060ff161562002d935750565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f2054161562002d935750565b620019139262002eec92602492604051926001600160a01b0360208501931683526001600160401b03809216604085015216606083015260608252620018f48262002744565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062002f38906024830190620026b4565b03925af18015620019c55762002f4b5750565b62002f5690620026f7565b565b9062002f6362002c45565b506001600160a01b038092165f5260205f8051602062005bb78339815191528152604092835f2084519062002f98826200270b565b85519562002fa68762002727565b80518254905f8162002fb88462002c80565b918282526001948a86821691825f14620031e05750506001146200319f575b62002fe5925003826200277c565b88528083019082519087829384928282549586815201915f52825f20945f5b8160038201106200315a57620030469654928583831062003140575b83831062003124575b83831062003107575b505010620030f8575b50905003826200277c565b858801526006600283015492856001600160401b0394858116858c0152841c1660608a015260ff60038201549760808b019a888a168c52828a60a01c1660a082015260c0810199838160a81c168b5260b01c5f0b60e0820152875260048301549086821690880152841c161515838601526005810154606086015201549082821660808501521c1660a082015293511615159081620030e9575b50156200053e57565b60ff9150511615155f620030e0565b60c01c81520188905f6200303b565b9091946001600160401b038560801c1681520193015f8562003032565b8192956001600160401b03868d1c168152019401908562003029565b8192956001600160401b0386168152019401908562003020565b86546001600160401b038082168652818b1c811696860196909652608081811c9096168a86015260c01c6060850152958201958795508c949093019260040162003004565b5050845f528188805f20855f915b858310620031c657505062002fe5935082010162002fd7565b8091929450548385880101520191018990858593620031ad565b60ff19168482015262002fe594151560051b840101915062002fd79050565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152606081018181106001600160401b03821117620025015760405251902090565b519081151582036200053e57565b5f80916040516001600160a01b036020820192168252602081526200327c8162002760565b51906108105afa6200328d62002cd7565b9015620032d6576020818051810103126200053e5760405190602082018281106001600160401b038211176200250157604052620032ce9060200162003249565b809152151590565b6040516313dd7ccd60e31b8152600490fd5b620032f79062000c4362002d0b565b906001600160a01b036001604051936200331185620026db565b805463ffffffff90818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156200053e57565b60405163a9059cbb60e01b602082019081526001600160a01b039384166024830152604480830195909552938152620033ea9390925f92839291906200339b6064876200277c565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051620033ce8162002760565b8181520152519082855af1620033e362002cd7565b91620051de565b805190811591821562003402575b5050156200053e57565b81925090602091810103126200053e57602062003420910162003249565b5f80620033f8565b51906001600160401b03821682036200053e57565b604080516001600160a01b0392909216602083019081526001600160401b039390931682820152808252915f918291906200347a6060826200277c565b51906108015afa6200348b62002cd7565b9015620034f5576060818051810103126200053e5781516001600160401b0392606082018481118382101762002501578152620034ee6060620034d16020860162003428565b94858552620034e284820162003428565b60208601520162003428565b9101521690565b8151639d2c8fcb60e01b8152600490fd5b6001600160401b03918216908216039190821162001a0e57565b949192909394805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20906001600160a01b039485835416958615620037ab57600284019788549860ff8a60481c166200379957600186015483608086510151168482160362003780576001600160401b039060a01c166001600160401b038b1660ff8c60401c165f146200371f575080926001600160401b03811682115f146200371657620035d79162003506565b995b620035e58b8d6200282b565b9b6001600160401b038d166001600160401b0385161162003704575069010000000000000000009069ff00000000000000000019161790556200362b8160019b62003506565b976001600160401b036020846200364b8d60e0895101515f0b9062003829565b50991695015116843b156200053e5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152925f908490606490829084905af1918215620019c5577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4393604093620036f2575b5060018187541696015416956001600160401b038351921682526020820152a4565b620036fd90620026f7565b5f620036d0565b5f9c508c9b5099505050505050505050565b50505f620035d7565b9092809b93806001600160401b038316105f1462003773576200374f91620037479162003506565b809c6200282b565b816001600160401b038216115f146200376b57505b91620035d9565b905062003764565b50506200374f5f62003747565b60405163358d72d160e01b815260048101879052602490fd5b60405163f7348a7960e01b8152600490fd5b604051631a40316d60e01b8152600490fd5b60ff16604d811162001a0e57600a0a90565b9190820391821162001a0e57565b8115620037e8570490565b634e487b7160e01b5f52601260045260245ffd5b90620f42409182810292818404149015171562001a0e57565b8181029291811591840414171562001a0e57565b9190805f0b80155f14620038475750506001600160401b0382169190565b5f81131562003877575062003863620038739160ff16620037bd565b6001600160401b03841662003815565b9190565b9050607f19811462001a0e5762003893905f0360ff16620037bd565b91620038aa836001600160401b038093166200287c565b5f19810190811162001a0e57620038c684620038ce92620037dd565b938462003815565b1690565b6200390a6200390392936001600160401b0393836001600160a01b03620038fc8780971662002847565b166200343d565b946200282b565b169116101590565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f205416155f14620039aa57825f5260205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f2054165f14620039aa57825f5260205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b9062003a5e6001600160a01b0360408401511662003257565b1562004618576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165f525f8051602062005bb783398151915260205260405f20906040519162003ab6836200270b565b60405162003ac48162002727565b6040518254815f62003ad68362002c80565b8083529260018116908115620045f75750600114620045b2575b62003afe925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b81600384011062004570579360069593819362003b67936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a08301526001600160a01b036060840151165f525f8051602062005bb783398151915260205260405f206040519062003c2e826200270b565b60405162003c3c8162002727565b6040518254815f62003c4e8362002c80565b80835292600181169081156200454f57506001146200450a575b62003c76925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b816003840110620044bd579360069593819362003cdf936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015262003d896001600160a01b03606086015116620032e8565b9184519062003dd662003dbb62003dac60a089015160808a01511515956200287c565b60e0885101515f0b9062004da2565b905060ff60c0885101511660ff60c087510151169162005165565b91156200448c5781925b5f8063ffffffff875116604051602081019182526020815262003e038162002760565b51906108085afa9062003e1562002cd7565b91156200447a576020828051810103126200053e576001600160401b0362003e44602062003e6d940162003428565b60208801519116919015620044665760ff60a062003e669251015116620037bd565b9062003815565b602085015115620044175763ffffffff62003e8e8160608801511662005026565b16620f4240818101811162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c01511691016200287c565b0416906080870151908115155f146200440e5750905b602085015115620043c5576305f5e1008110156200437e57505f5b62003f2462003f0b60a0890151620037fc565b62003f1d895160a08b0151906200287c565b906200287c565b5f1981019190821162001a0e5762003f1d62003f539262003f4c8a5160a08c0151906200287c565b90620037dd565b606482028281046064148315171562001a0e578111620042df57505062003f84855160e0865101515f0b9062004da2565b92909562003faf6001600160401b03602088015116856001600160401b0360a08a01511691620038d2565b156200423b5791608093917f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91860a0856001600160401b036001600160a01b036040819b990151168a606084015116908a8401511515916040519162004014836200270b565b82526020820152828816604082015282871660608201528a81019182528481015f815262004118602086019384515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b03602052856060600260405f206001600160a01b0388511673ffffffffffffffffffffffffffffffffffffffff19825416178155600181016001600160a01b0360208a0151168154907bffffffffffffffff000000000000000000000000000000000000000060408c015160a01b169163ffffffff60e01b161717905501950151168619855416178455511515839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151569ff00000000000000000082549160481b169069ff00000000000000000019161790555194818b60408501511697818d6060870151169a87875197015160405197885260208801521660408601521660608401521688820152a4015116620041ae83826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001662003353565b60e06001600160401b03602084015116925101515f0b90803b156200053e57604051639c45c34b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160401b03909316602484015260448301939093525f90810b6064830152909182908183816084810162002f38565b94505050905062002f56925060208201516001600160401b03604051921682527f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c60206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b9350949350505060208201516001600160a01b0360608401511691606481019081811162001a0e5760630190811162001a0e5760407fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e9162002f569660648351920482526020820152a36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166060820152620046c4565b6001600160401b036305f5e0ff1982011162001a0e57620043be620043b86001600160401b036305f5e100936305f5e0ff190116620037fc565b6200286a565b0462003ef8565b6305f5e100811115620043da57505f62003ef8565b6001600160401b03816305f5e100031162001a0e57620043be620043b86001600160401b036305f5e10093840316620037fc565b90509062003edd565b63ffffffff6200442d8160608801511662005026565b16620f4240808281031162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c0151169103620037cf565b5062003e6660ff60a08951015116620037bd565b604051635cffc5fb60e11b8152600490fd5b6127108181031162001a0e576001600160401b03612710620044b383820383861662003815565b0416919262003de0565b926001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019162003c95565b5090845f5260205f20905f915b8183106200453257505090602062003c769282010162003c68565b602091935080600191548385880101520191019091839262004517565b6020925062003c7694915060ff191682840152151560051b82010162003c68565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162003b1d565b5090845f5260205f20905f915b818310620045da57505090602062003afe9282010162003af0565b6020919350806001915483858801015201910190918392620045bf565b6020925062003afe94915060ff191682840152151560051b82010162003af0565b506080810151909190156200464c5760246001600160a01b03604084015116604051906320a2097d60e11b82526004820152fd5b9080602062002f569201517f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b03604085015116604051908152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b6060906060810151905f6001600160a01b0380931692835f526020945f8051602062005bb78339815191528652604091825f2096835162004705816200270b565b845193620047138562002727565b85518a5491905f81620047268562002c80565b918282526001958887821691825f1462004d6b57505060011462004d2a575b62004756925094939403826200277c565b8652808b019184885193898586948484549283815201935f52845f20965f915b83600384011062004cde575085620047b998549484841062004cc4575b84841062004ca7575b5083831062004c8a575b50501062004c7b5750905003826200277c565b82850152600289015495836001600160401b039788811688880152871c16606086015260038a0154946080958581168783015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528252600660048b01549a60ff8585019c8d8b82169052891c1615158885015260058101546060850152015490878216868401528760a0840192881c168252868901976200485e868a511662003257565b1562004c0d57899a9b5f9a98999a508c612710620048958b519b8b6200488b60a083019e8f51906200287c565b9101519062003815565b04988a5199808b1162004c04575b508d8a8062004b2e575b620048c2620048d191620048e493516200287c565b60e08a5101515f0b9062004da2565b91909782878088511692511691620038d2565b1562004ae65750908c9695949392918a620049f9575b509260e0896200498f94620049c59b9997947fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b99976200496e906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6200497b8d82546200287c565b9055511696511694510151900b9062004da2565b9190928d84620049ca575b5050505050880151965116965192519351938493846040919493926060820195825260208201520152565b0390a4565b620049ee94620049e89188620049e08562002847565b169062003353565b62002ea6565b5f8080808d6200499a565b919293949596505087807f00000000000000000000000000000000000000000000000000000000000000001691875101511690803b156200053e578b5163f3fef3a360e01b81526001600160a01b03929092166004830152602482018a90525f908290604490829084905af1801562004adc578c948f91998098956200498f957fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b9995620049c59d62004ac0575b509497995094509950939597999a9b620048fa565b60e0945062004acf90620026f7565b6200496e5f945062004aab565b8b513d5f823e3d90fd5b9a50505050955050505093957f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c955084915062004b238162004e31565b0151945191168152a3565b62004b45908a8301518c858c51015116906200503f565b1562004b53575b8a620048ad565b509192939495968881999a50602492508389510151168c51928380926370a0823160e01b82528d7f00000000000000000000000000000000000000000000000000000000000000001660048301525afa90811562004adc57918e918e9a999897969594935f9162004bc7575b509962004b4c565b92505098508681813d831162004bfc575b62004be481836200277c565b810103126200053e57518c988e91620048d162004bbf565b503d62004bd8565b99505f620048a3565b505050509296509450839293015115155f1462004c405750505191516320a2097d60e11b81529116600482015260249150fd5b62002f56948294827f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d9401519451169051908152a262004e31565b60c01c81520185905f6200303b565b9091946001600160401b038560801c1681520193015f85620047a6565b956001600160401b03868495981c16815201940190858e6200479c565b6001600160401b0386168752958101959282019262004793565b95509360049296509284919388546001600160401b039283821683528d8483831c169084015260809382851c169083015260c01c8782015201960191019386948c9296938a9562004776565b50508c5f528186805f20865f915b85831062004d5157505062004756935082010162004745565b809192945054838588010152019101879086859362004d38565b60ff1916848201526200475694151560051b8401019150620047459050565b81811062004d96575050565b5f815560010162004d8a565b9190805f0b9081155f1462004dc05750506001600160401b03821690565b5f82131562004e035762004dd8915060ff16620037bd565b918215620037e857620038ce62004dfb82856001600160401b03940690620037cf565b9384620037dd565b505f0380805f0b0362001a0e57620038ce62004e2a6001600160401b039260ff16620037bd565b8462003815565b805161271062004e5862004e4c60a08501938451906200287c565b60808501519062003815565b0481518181115f146200501d5750905b8190602084019182519162004e8f60608701956001600160a01b039485885116906200503f565b1562005014575b8162004f61575b620049c5837f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9462004ed1858a516200287c565b9662004eeb828a51169860408c0199848b51169062003353565b62004f29828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62004f368782546200287c565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b84518316927f00000000000000000000000000000000000000000000000000000000000000008116803b156200053e5760405163f3fef3a360e01b81526001600160a01b03959095166004860152602485018490525f908590604490829084905af1908115620019c5577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f94620049c59262005002575b5093505062004e9d565b6200500d90620026f7565b5f62004ff8565b5f915062004e96565b90509062004e68565b90606463ffffffff8093160291821691820362001a0e57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa918215620019c5575f92620050f2575b5080821094851595620050b2575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080620050ac565b9091506020813d60201162005123575b8162005111602093836200277c565b810103126200053e5751905f6200509e565b3d915062005102565b51906001600160a01b03821682036200053e57565b519060ff821682036200053e57565b9060ff8091169116039060ff821162001a0e57565b919060ff821660ff82168181145f1462005180575050505090565b929391921015620051b857620038ce9192620051b0620051aa6001600160401b0395869362005150565b620037bd565b911662003815565b620038ce91620051d6620051aa6001600160401b0395869362005150565b9116620037dd565b9015620051fb57815115620051f1575090565b3b156200053e5790565b5080519081156200053e57602001fdfe60a080604052346100455733608052610961908161004a8239608051818181608b015281816101ac0152818161038e015281816104a20152818161051b015261069c0152f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f915f3560e01c908163435354d3146104d757816390a0827b1461047b5781639c45c34b14610469578163a703334f1461033c578163de1a324a146100d457508063e94b77c1146100b35763eba61c0e1461006d575f80fd5b346100af57816003193601126100af57602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b82346100d1576100ce6100c5366105b5565b9291909161068d565b80f35b80fd5b91905034610338576101009136600319018381126103345760a01361033057815167ffffffffffffffff939060a081018581118282101761031d5784526101196105a2565b91828252602435918215158303610319576020810192835263ffffffff9360443585811681036102ce578783015260643585811681036102ce5760608301526084356001600160a01b03928382168203610315576080015260a4358881168091036102ce5760c435918983168093036102ce5760e435936fffffffffffffffffffffffffffffffff85168095036102ce577f000000000000000000000000000000000000000000000000000000000000000016330361031557866101dd9116610663565b9451151581156103055782156102f5578a6102e557885196602088019616865288870152606086015260808501525f60a0850152600260c085015260e084015260e08352820194828610908611176102d25761026891602491868652630100000160e01b6101208301526102598251809261012485019061079c565b81010383810186520184610641565b73333333333333333333333333333333333333333392833b156102ce576102a5935f928385518097819582946317938e1360e01b845283016107bd565b03925af19081156102c557506102b9575080f35b6102c391506105fd565b005b513d5f823e3d90fd5b5f80fd5b604183634e487b7160e01b5f525260245ffd5b88516376d4929560e11b81528890fd5b88516313c0a8df60e01b81528890fd5b885163017461b760e71b81528890fd5b8a80fd5b8780fd5b604184634e487b7160e01b5f525260245ffd5b8380fd5b8480fd5b8280fd5b919050346103385760603660031901126103385781359067ffffffffffffffff928383168093036102ce576024356001600160a01b03948582168092036102ce576044359081168091036102ce5786957f000000000000000000000000000000000000000000000000000000000000000016330361046557610259946103fc9260249286519260208401928352878401526060830152606082526103df82610625565b85519687926280000360e11b60208501525180928585019061079c565b73333333333333333333333333333333333333333392833b15610334576104399385928385518097819582946317938e1360e01b845283016107bd565b03925af190811561045c575061044c5750f35b610455906105fd565b6100d15780f35b513d84823e3d90fd5b8580fd5b83346100d1576100ce6100c5366105b5565b91905034610338573660031901126100af57356001600160a01b038082168092036102ce577f00000000000000000000000000000000000000000000000000000000000000001633036100af576100ce906024359033906107e9565b919050346102ce57806003193601126102ce576104f26105a2565b91602435926fffffffffffffffffffffffffffffffff84168094036102ce576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102ce5761054a90610663565b9282519063ffffffff602083019516855283820152828152606081019381851067ffffffffffffffff8611176102d25761026891602491868652630100000b60e01b608083015261025982518092608485019061079c565b6004359063ffffffff821682036102ce57565b60809060031901126102ce576004356001600160a01b03811681036102ce579060243567ffffffffffffffff811681036102ce579060443590606435805f0b81036102ce5790565b67ffffffffffffffff811161061157604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761061157604052565b90601f8019910116810190811067ffffffffffffffff82111761061157604052565b9061271063ffffffff8093160191821161067957565b634e487b7160e01b5f52601160045260245ffd5b9092916001600160a01b0392837f00000000000000000000000000000000000000000000000000000000000000001633036102ce57805f0b80155f1461071c575050915b826106dd575b50505050565b67ffffffffffffffff732000000000000000000000000000000000000000941684018094116106795780610713941691166107e9565b5f8080806106d7565b5f81131561075c57506107319060ff166108f1565b8015610748578106810390811161067957916106d1565b634e487b7160e01b5f52601260045260245ffd5b90505f9391930380805f0b03610679576107789060ff166108f1565b9280938181810204149015176106d157634e487b7160e01b5f52601160045260245ffd5b5f5b8381106107ad5750505f910152565b818101518382015260200161079e565b604091602082526107dd815180928160208601526020868601910161079c565b601f01601f1916010190565b9190604051906020938483019363a9059cbb60e01b85526001600160a01b03809316602485015260448401526044835261082283610625565b169160405190604082019067ffffffffffffffff9383831085841117610611577f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564875f9586956040528181520152519082865af13d156108e4573d918211610611576108ac926040519261089f86601f19601f8401160185610641565b83523d5f8685013e610902565b80519182159182156108c4575b50509050156102ce57565b8092508193810103126102ce57015180151581036102ce57805f806108b9565b6108ac9260609250610902565b60ff16604d811161067957600a0a90565b901561091c57815115610913575090565b3b156102ce5790565b5080519081156102ce57602001fdfea2646970667358221220c909e7dbe656d7c973709e34eb673837a89b1ae88012e9262063134290e6f21f64736f6c634300081800336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00a2646970667358221220d7009235facd2afacf7dbd784aa03c97dd6444be46f4a33aa486147317b81a7764736f6c63430008180033000000000000000000000000002e76dc036a1eff1488ee5435ee66c6abf32674000000000000000000000000b88339cb7199b77e23db6e890353e22632ba630f" + } + ], + "isFixedGasLimit": false + }, + { + "hash": "0x2d31d35b8f8ed4e1f7f84b15c97c9c0e95798883948b4bcf349229e1ab62d82d", + "transactionType": "CALL", + "contractName": "DonationBox", + "contractAddress": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "function": "transferOwnership(address)", + "arguments": ["0x83e245941BefbDe29682dF068Bcda006A804eb0C"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "gas": "0x9922", + "value": "0x0", + "input": "0xf2fde38b00000000000000000000000083e245941befbde29682df068bcda006a804eb0c", + "nonce": "0x245", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0xc11a2", + "logs": [ + { + "address": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d", + "transactionIndex": "0x3", + "logIndex": "0x22", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000040000400000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000880000000000000000000000000000000000000000008020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d", + "transactionIndex": "0x3", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "gasUsed": "0x3f377", + "effectiveGasPrice": "0x6052340", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x002e76dc036a1eff1488ee5435ee66c6abf32674" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x79e7fa", + "logs": [ + { + "address": "0x83e245941befbde29682df068bcda006a804eb0c", + "topics": [ + "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", + "0x5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef6", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "transactionIndex": "0x4", + "logIndex": "0x23", + "removed": false + }, + { + "address": "0x83e245941befbde29682df068bcda006a804eb0c", + "topics": [ + "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", + "0x880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "transactionIndex": "0x4", + "logIndex": "0x24", + "removed": false + }, + { + "address": "0x83e245941befbde29682df068bcda006a804eb0c", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "transactionIndex": "0x4", + "logIndex": "0x25", + "removed": false + } + ], + "logsBloom": "0x00000004000000000800000000000000080000000000000000000080000000000100000000000000000000000000000000000000001000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000020000400000000000000800000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000800000000000000000000000000000000100000000000020100001000000000000000000000000000100000004000000000000000001000000", + "type": "0x2", + "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "transactionIndex": "0x4", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "gasUsed": "0x6dd658", + "effectiveGasPrice": "0x6052340", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x83e245941befbde29682df068bcda006a804eb0c" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x7a56d9", + "logs": [ + { + "address": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x00000000000000000000000083e245941befbde29682df068bcda006a804eb0c" + ], + "data": "0x", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0x2d31d35b8f8ed4e1f7f84b15c97c9c0e95798883948b4bcf349229e1ab62d82d", + "transactionIndex": "0x5", + "logIndex": "0x26", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000001000000000000100000000000000000000000000000000000000000000000000000000000000000000000040000400000000000000000000000200000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000880000000000000000000000000000000000000000008000000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x2d31d35b8f8ed4e1f7f84b15c97c9c0e95798883948b4bcf349229e1ab62d82d", + "transactionIndex": "0x5", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "gasUsed": "0x6edf", + "effectiveGasPrice": "0x6052340", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764705991008, + "chain": 999, + "commit": "4b103fe" +} diff --git a/broadcast/CreateSponsoredDeposit.s.sol/1/run-latest.json b/broadcast/CreateSponsoredDeposit.s.sol/1/run-latest.json new file mode 100644 index 000000000..6ad9a3995 --- /dev/null +++ b/broadcast/CreateSponsoredDeposit.s.sol/1/run-latest.json @@ -0,0 +1,220 @@ +{ + "transactions": [ + { + "hash": "0xad1f22fd9e3cec893ebf186688666aeeda5e7eaf0196a589ca95d22de61e8680", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "function": "approve(address,uint256)", + "arguments": ["0xE35d1205a523B699785967FFfe99b72059B46707", "1000000"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "gas": "0x105f1", + "value": "0x0", + "input": "0x095ea7b3000000000000000000000000e35d1205a523b699785967fffe99b72059b4670700000000000000000000000000000000000000000000000000000000000f4240", + "nonce": "0x2633", + "chainId": "0x1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xe35d1205a523b699785967fffe99b72059b46707", + "function": "deposit(((uint32,uint32,bytes32,uint256,bytes32,uint256,uint256,bytes32,bytes32,uint256,uint256,uint8,bytes),(address,uint256)),bytes)", + "arguments": [ + "((30101, 30367, 0x000000000000000000000000d9f40794367a2ecb0b409ca8dbc55345c0db6e9f, 1000000, 0x00000000000000000000000000000000000000000000000000000000690d4543, 1762480979, 100, 0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d, 0x000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb, 200000, 300000, 0, 0x), (0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D, 200))", + "0x012c2ad356c9063c93c00f6b9d2a68be9b88dbd21ff07f4354026a5e9eb576da07ec271d526830b21ab33d20c070958e0acfb192e02ea816e0e9e1e13a74ac771c" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xe35d1205a523b699785967fffe99b72059b46707", + "gas": "0x96208", + "value": "0xc35dde03454", + "input": "0xfcc5b1e30000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000600000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d00000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000007595000000000000000000000000000000000000000000000000000000000000769f000000000000000000000000d9f40794367a2ecb0b409ca8dbc55345c0db6e9f00000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000690d454300000000000000000000000000000000000000000000000000000000690d535300000000000000000000000000000000000000000000000000000000000000640000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041012c2ad356c9063c93c00f6b9d2a68be9b88dbd21ff07f4354026a5e9eb576da07ec271d526830b21ab33d20c070958e0acfb192e02ea816e0e9e1e13a74ac771c00000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x2634", + "chainId": "0x1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x121af85", + "logs": [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x000000000000000000000000e35d1205a523b699785967fffe99b72059b46707" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0xad1f22fd9e3cec893ebf186688666aeeda5e7eaf0196a589ca95d22de61e8680", + "transactionIndex": "0x8e", + "logIndex": "0x247", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000200000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000008000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000020000000000000000000080000000000000000000100000000000000000000000000000000000800000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xad1f22fd9e3cec893ebf186688666aeeda5e7eaf0196a589ca95d22de61e8680", + "transactionIndex": "0x8e", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "gasUsed": "0xbda5", + "effectiveGasPrice": "0xc9a69b7", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x1287a8d", + "logs": [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x000000000000000000000000e35d1205a523b699785967fffe99b72059b46707" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x248", + "removed": false + }, + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x000000000000000000000000e35d1205a523b699785967fffe99b72059b46707", + "0x0000000000000000000000006c96de32cea08842dcc4058c14d3aaad7fa41dee" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x249", + "removed": false + }, + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000e35d1205a523b699785967fffe99b72059b46707", + "0x0000000000000000000000006c96de32cea08842dcc4058c14d3aaad7fa41dee" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x24a", + "removed": false + }, + { + "address": "0xbb2ea70c9e858123480642cf96acbcce1372dce1", + "topics": ["0x61ed099e74a97a1d7f8bb0952a88ca8b7b8ebd00c126ea04671f92a81213318a"], + "data": "0x000000000000000000000000173272739bd7aa6e4e214714048a9fe69945305900000000000000000000000000000000000000000000000000000b05faae930f", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x24b", + "removed": false + }, + { + "address": "0xbb2ea70c9e858123480642cf96acbcce1372dce1", + "topics": ["0x07ea52d82345d6e838192107d8fd7123d9c2ec8e916cd0aad13fd2b60db24644"], + "data": "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000003b0531eb02ab4ad72e7a531180beef9493a00dd2000000000000000000000000589dedbd617e0cbcb916a9223f4d1300c294236b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000009a30f3b4ca00000000000000000000000000000000000000000000000000000095b23dec7b", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x24c", + "removed": false + }, + { + "address": "0x1a44076050125825900e736c501f859c50fe728c", + "topics": ["0x1ab700d4ced0c005b164c0f789fd09fcbb0156d4c2041b8a3bfbcd961cd1567f"], + "data": "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000260000000000000000000000000bb2ea70c9e858123480642cf96acbcce1372dce100000000000000000000000000000000000000000000000000000000000001d9010000000000001931000075950000000000000000000000006c96de32cea08842dcc4058c14d3aaad7fa41dee0000769f000000000000000000000000904861a24f30ec96ea7cfc3be9ea4b476d237e988776497f38712e1cf03b1a456ad0e75f00ddea17b867fc23745e1e212ab5395c000000000000000000000000d9f40794367a2ecb0b409ca8dbc55345c0db6e9f00000000000f4240000000000000000000000000e35d1205a523b699785967fffe99b72059b4670700000000000000000000000000000000000000000000000000000000690d454300000000000000000000000000000000000000000000000000000000690d5353000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c80000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056000301001101000000000000000000000000000186a0010013030000000000000000000000000000000186a00100110100000000000000000000000000030d40010013030000000000000000000000000000000493e000000000000000000000", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x24d", + "removed": false + }, + { + "address": "0x6c96de32cea08842dcc4058c14d3aaad7fa41dee", + "topics": [ + "0x85496b760a4b7f8d66384b9df21b381f5d1b1e79f229a47aaf4c232edc2fe59a", + "0x8776497f38712e1cf03b1a456ad0e75f00ddea17b867fc23745e1e212ab5395c", + "0x000000000000000000000000e35d1205a523b699785967fffe99b72059b46707" + ], + "data": "0x000000000000000000000000000000000000000000000000000000000000769f00000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x24e", + "removed": false + }, + { + "address": "0xe35d1205a523b699785967fffe99b72059b46707", + "topics": [ + "0x8fb515a2e89f5acfca1124e69e331c2cded0ca216b578ba531720f6841139dbf", + "0x00000000000000000000000000000000000000000000000000000000690d4543", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x000000000000000000000000d9f40794367a2ecb0b409ca8dbc55345c0db6e9f00000000000000000000000000000000000000000000000000000000690d5353000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000041012c2ad356c9063c93c00f6b9d2a68be9b88dbd21ff07f4354026a5e9eb576da07ec271d526830b21ab33d20c070958e0acfb192e02ea816e0e9e1e13a74ac771c00000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "blockTimestamp": "0x690d455b", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "logIndex": "0x24f", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000001000000408000000000000000000000200010000000000000000000100000000200000000202000080100000000018000000004000200000004000000000000000000000000004000000008000000000000000008000000010000000000010040000000000000000008000001000080000400000000000000000000000000000100000020000008000000000100084000400001000000000500000000440000000800000000002000000800000000080000000000000000000001000000000000000000011040000000000000000000000002000000000018000000000020004000000", + "type": "0x2", + "transactionHash": "0x50fb703efb0080e0cceaeda0e57c859005f8842eb89d8e4a52e98c9ecfba80b0", + "transactionIndex": "0x8f", + "blockHash": "0x360d2478a6a3441da84a94ce952c72a9dce4679c14acafe3e57e3a1eb727dd29", + "blockNumber": "0x16a4dfa", + "gasUsed": "0x6cb08", + "effectiveGasPrice": "0xc9a69b7", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xe35d1205a523b699785967fffe99b72059b46707", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1762477406440, + "chain": 1, + "commit": "d93c7b1f" +} diff --git a/broadcast/CreateSponsoredDeposit.s.sol/42161/run-latest.json b/broadcast/CreateSponsoredDeposit.s.sol/42161/run-latest.json new file mode 100644 index 000000000..fb382c58a --- /dev/null +++ b/broadcast/CreateSponsoredDeposit.s.sol/42161/run-latest.json @@ -0,0 +1,257 @@ +{ + "transactions": [ + { + "hash": "0x727ba90684db5bf8034bf6d8fd2aefae7856cb177e049905e3394606d8641cfe", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "function": "approve(address,uint256)", + "arguments": ["0x2ac5Ee3796E027dA274fbDe84c82173a65868940", "1000000"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "gas": "0x1141e", + "value": "0x0", + "input": "0x095ea7b30000000000000000000000002ac5ee3796e027da274fbde84c82173a6586894000000000000000000000000000000000000000000000000000000000000f4240", + "nonce": "0x1570", + "chainId": "0xa4b1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionType": "CALL", + "contractName": "SponsoredOFTSrcPeriphery", + "contractAddress": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "function": "deposit(((uint32,uint32,bytes32,uint256,bytes32,uint256,uint256,bytes32,bytes32,uint256,uint256,uint8,bytes),(address,uint256)),bytes)", + "arguments": [ + "((30110, 30367, 0x00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec67461, 1000000, 0x00000000000000000000000000000000000000000000000000000000692f9f74, 1764732292, 100, 0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d, 0x000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb, 200000, 300000, 0, 0x), (0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D, 200))", + "0x564eeb372a7e7e0ddba42927a0c34f2138658d5fb71f8d4752dedf14ab6a406e2230bfbf5054030e3b4af512f29f70d7e92300eb24b17f3f75b8835d0706a9171b" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "gas": "0xa777f", + "value": "0x914a7d9d2ca", + "input": "0xfcc5b1e30000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000600000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d00000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000759e000000000000000000000000000000000000000000000000000000000000769f00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec6746100000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000692f9f7400000000000000000000000000000000000000000000000000000000692fad8400000000000000000000000000000000000000000000000000000000000000640000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000493e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041564eeb372a7e7e0ddba42927a0c34f2138658d5fb71f8d4752dedf14ab6a406e2230bfbf5054030e3b4af512f29f70d7e92300eb24b17f3f75b8835d0706a9171b00000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x1571", + "chainId": "0xa4b1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x9a1069", + "logs": [ + { + "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x16950265e3ff885b1bd95a316681c3149258e795c5f25ce7775dfadd3156ccaa", + "blockNumber": "0x183c19cb", + "blockTimestamp": "0x692f9f7f", + "transactionHash": "0x727ba90684db5bf8034bf6d8fd2aefae7856cb177e049905e3394606d8641cfe", + "transactionIndex": "0xa", + "logIndex": "0x9a", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000400000000000000200000000010000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000008000000000800000000000000000000000000000000000000000000000000011000000000000000000000000000010000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x727ba90684db5bf8034bf6d8fd2aefae7856cb177e049905e3394606d8641cfe", + "transactionIndex": "0xa", + "blockHash": "0x16950265e3ff885b1bd95a316681c3149258e795c5f25ce7775dfadd3156ccaa", + "blockNumber": "0x183c19cb", + "gasUsed": "0xd148", + "effectiveGasPrice": "0x10eef60", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "contractAddress": null, + "gasUsedForL1": "0x6", + "l1BlockNumber": "0x16d2449", + "timeboosted": false + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x7a4bb", + "logs": [ + { + "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x1", + "removed": false + }, + { + "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x2", + "removed": false + }, + { + "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "topics": [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940", + "0x00000000000000000000000014e4a1b13bf7f943c8ff7c51fb60fa964a298d92" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x3", + "removed": false + }, + { + "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x4", + "removed": false + }, + { + "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9", + "topics": [ + "0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x5", + "removed": false + }, + { + "address": "0x975bcd720be66659e3eb3c0e4f1866a3020e493a", + "topics": ["0x61ed099e74a97a1d7f8bb0952a88ca8b7b8ebd00c126ea04671f92a81213318a"], + "data": "0x00000000000000000000000031cae3b7fb82d847621859fb1585353c5720660d00000000000000000000000000000000000000000000000000000899318ca136", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x6", + "removed": false + }, + { + "address": "0x975bcd720be66659e3eb3c0e4f1866a3020e493a", + "topics": ["0x07ea52d82345d6e838192107d8fd7123d9c2ec8e916cd0aad13fd2b60db24644"], + "data": "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000002f55c492897526677c5b68fb199ea31e2c126416000000000000000000000000a8b8575fa41c305953f519c7d288cd7741727c7b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000003cd165e0c60000000000000000000000000000000000000000000000000000003ea4e750ce", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x7", + "removed": false + }, + { + "address": "0x1a44076050125825900e736c501f859c50fe728c", + "topics": ["0x1ab700d4ced0c005b164c0f789fd09fcbb0156d4c2041b8a3bfbcd961cd1567f"], + "data": "0x00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000260000000000000000000000000975bcd720be66659e3eb3c0e4f1866a3020e493a00000000000000000000000000000000000000000000000000000000000001d90100000000000062b40000759e00000000000000000000000014e4a1b13bf7f943c8ff7c51fb60fa964a298d920000769f000000000000000000000000904861a24f30ec96ea7cfc3be9ea4b476d237e989333579858e48aaa72df628652842a36f63b52733211b4d79dcad038b676454600000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec6746100000000000f42400000000000000000000000002ac5ee3796e027da274fbde84c82173a6586894000000000000000000000000000000000000000000000000000000000692f9f7400000000000000000000000000000000000000000000000000000000692fad84000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c80000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056000301001101000000000000000000000000000186a0010013030000000000000000000000000000000186a00100110100000000000000000000000000030d40010013030000000000000000000000000000000493e000000000000000000000", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x8", + "removed": false + }, + { + "address": "0x14e4a1b13bf7f943c8ff7c51fb60fa964a298d92", + "topics": [ + "0x85496b760a4b7f8d66384b9df21b381f5d1b1e79f229a47aaf4c232edc2fe59a", + "0x9333579858e48aaa72df628652842a36f63b52733211b4d79dcad038b6764546", + "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940" + ], + "data": "0x000000000000000000000000000000000000000000000000000000000000769f00000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000f4240", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0x9", + "removed": false + }, + { + "address": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "topics": [ + "0x8fb515a2e89f5acfca1124e69e331c2cded0ca216b578ba531720f6841139dbf", + "0x00000000000000000000000000000000000000000000000000000000692f9f74", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec6746100000000000000000000000000000000000000000000000000000000692fad84000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000041564eeb372a7e7e0ddba42927a0c34f2138658d5fb71f8d4752dedf14ab6a406e2230bfbf5054030e3b4af512f29f70d7e92300eb24b17f3f75b8835d0706a9171b00000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "blockTimestamp": "0x692f9f82", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "logIndex": "0xa", + "removed": false + } + ], + "logsBloom": "0x000000000000000000000000000000000080000000008010000000000010000000000000000000000000000002000000000000000004400000000000002004000000120000001000000000180048000000000000000000000000400000000000000000000208000080000000000008000820000000100000000000100400000000000000000000000000000a00000000000000000000000000000000002000000a0000008000000000100000100000020000000000400000400440000000008000008002000000800000000080000000000000000000000000001000000020000011040000010000000000000000002010000000018000000080000080000000", + "type": "0x2", + "transactionHash": "0x3821ab22e999bc5c916ad9e335d03f9844cd02247fe3eeb1a08c686552f54f19", + "transactionIndex": "0x2", + "blockHash": "0x1f757ef809f329acc38148659c99068e7e740de2efde03ee66740667edd394c4", + "blockNumber": "0x183c19d5", + "gasUsed": "0x72833", + "effectiveGasPrice": "0x113ecb8", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "contractAddress": null, + "gasUsedForL1": "0x10", + "l1BlockNumber": "0x16d2449", + "timeboosted": false + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764728706358, + "chain": 42161, + "commit": "5d9f89e2" +} diff --git a/broadcast/DeployDstHandler.s.sol/999/run-latest.json b/broadcast/DeployDstHandler.s.sol/999/run-latest.json new file mode 100644 index 000000000..ea47bc78e --- /dev/null +++ b/broadcast/DeployDstHandler.s.sol/999/run-latest.json @@ -0,0 +1,418 @@ +{ + "transactions": [ + { + "hash": "0xf72c3e798991af0e7123197bfd7da40585f936b4353b91159b749008ceac541a", + "transactionType": "CREATE", + "contractName": "DonationBox", + "contractAddress": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "function": null, + "arguments": null, + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x522e7", + "value": "0x0", + "input": "0x6080806040523461005a575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610344908161005f8239f35b5f80fdfe608060409080825260049081361015610016575f80fd5b5f3560e01c908163715018a61461026e5781638da5cb5b1461024c57508063f2fde38b146101d45763f3fef3a31461004c575f80fd5b346101545781600319360112610154578035916001600160a01b0383168093036101545760249261007b6102d2565b8151916020830163a9059cbb60e01b815233868501528535604485015260448452608084019367ffffffffffffffff94818110868211176101c25760c08201818110878211176101b0578452602090527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152515f9182919082865af1923d1561019f573d9181831161018d57805195601f8401601f19908116603f011687019283118784101761017b575052835261013e93503d5f602085013e6102e5565b8051908115918215610158575b50501561015457005b5f80fd5b819250906020918101031261015457602001518015158103610154575f8061014b565b60418891634e487b7160e01b5f52525ffd5b86604187634e487b7160e01b5f52525ffd5b5050915061013e92506060916102e5565b88604189634e487b7160e01b5f52525ffd5b87604188634e487b7160e01b5f52525ffd5b503461015457602036600319011261015457356001600160a01b03808216809203610154576102016102d2565b8115610154575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b34610154575f366003190112610154576020906001600160a01b035f54168152f35b34610154575f366003190112610154576102866102d2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361015457565b90156102ff578151156102f6575090565b3b156101545790565b50805190811561015457602001fdfea2646970667358221220c55691de465342d56d68c31160f5d7d661422ef39ebccaa5223872c823ad6d6964736f6c63430008180033", + "nonce": "0x24a", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xee446c5d024fc2d807a387b7be9dc0e17f7f806efac526cc1db7bebd7fa5f7c7", + "transactionType": "CREATE", + "contractName": "PermissionedMulticallHandler", + "contractAddress": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "function": null, + "arguments": ["0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x15b834", + "value": "0x0", + "input": "0x6080346100d157601f6112d838819003918201601f191683019291906001600160401b038411838510176100d55781602092849260409687528339810103126100d157516001600160a01b038116908190036100d15760015f555f80526001602052815f20815f5260205260ff825f20541615610085575b50516111ee90816100ea8239f35b5f80526001602052815f20815f52602052815f20600160ff1982541617905533905f7f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a45f610077565b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080604090808252600480361015610021575b505050361561001f575f80fd5b005b5f3560e01c91826301ffc9a71461086d57508163248a9ca3146108435781632f2ff15d1461079f57816336568abe1461076d5781633a5be8cb146105635781635f6ee30c1461052957816391d14854146104e4578163a217fddf146104ca578163a58d50d3146102fd578163c41e82951461012b578163d547741f146100f2575063ef8738d3146100b3578080610012565b346100ee573660031901126100ee576100ca6108d5565b6024356001600160a01b03811681036100ee5761001f916100e9610f24565b610f3f565b5f80fd5b82346100ee57806003193601126100ee5761001f913561012660016101156108bf565b93835f52816020525f200154610b02565b610c1a565b9050346100ee5760803660031901126100ee576101466108d5565b6024929067ffffffffffffffff84358181116100ee576101699036908601610975565b916044606435928084116100ee57366023850112156100ee57838701359081116100ee57878401938836918360061b0101116100ee576101a7610f24565b604435935f5b8281106101fa57505050505f91829184519160208601915af16101ce610a9b565b50156101d657005b6020936101f6925194859463b3beda7360e01b86528501528301906109f4565b0390fd5b6001600160a01b0380610216610211848787610aca565b610aee565b16156102f35761022a610211838686610aca565b16885180916370a0823160e01b8252308c830152818d60209485935afa9182156102e9575f926102bb575b50505b602080610266848787610aca565b01358181018082116102a9578a5180821161028f575050890101805190911790526001016101ad565b88918f8f928f5193634d3ae48d60e01b8552840152820152fd5b8d60118e634e487b7160e01b5f52525ffd5b90809250813d83116102e2575b6102d28183610937565b810103126100ee57515f80610255565b503d6102c8565b8a513d5f823e3d90fd5b5047955085610258565b9050346100ee57602091826003193601126100ee5767ffffffffffffffff9082358281116100ee57366023820112156100ee578084013594602491610341876109bb565b9461034e85519687610937565b878652828601908460059960051b840101923684116100ee57858101925b848410610458575050505050610380610f24565b8351955f5b87811061038e57005b8551811015610446578281831b8701015183810190815151151580610433575b61040d575f9181886001600160a01b03859451169101519151918783519301915af16103d8610a9b565b50156103e657600101610385565b84516303918b1160e61b815280880191909152808401859052806101f66044820188610a19565b865163388ddcc360e21b8152808a01849052808701889052806101f6604482018b610a19565b506001600160a01b038151163b156103ae565b83603288634e487b7160e01b5f52525ffd5b83358381116100ee578201606060231982360301126100ee5788519161047d836108eb565b888201356001600160a01b03811681036100ee5783526044820135928584116100ee5760648994936104b586958d3691840101610975565b8584015201358b82015281520193019261036c565b82346100ee575f3660031901126100ee57602090515f8152f35b82346100ee57806003193601126100ee576020916105006108bf565b90355f52600183526001600160a01b03825f2091165f52825260ff815f20541690519015158152f35b82346100ee575f3660031901126100ee57602090517f69048ea73402a715065a3029b4059a4e97d1461c95fa4fabca1084b5f34f4abe8152f35b82346100ee5760803660031901126100ee5761057d6108d5565b916044356001600160a01b038116036100ee5760643567ffffffffffffffff81116100ee576105af9036908301610975565b917f69048ea73402a715065a3029b4059a4e97d1461c95fa4fabca1084b5f34f4abe90815f526001916001602052815f20335f5260205260ff825f2054161561061057858560025f54146100ee5761060a9160025f55610ca2565b60015f55005b610619336110ff565b908251936106268561091b565b604285526020850195606036883785511561075a576030875385516001101561075a5790607860218701536041915b8183116106f0575050506100ee576100ee936106e193604893519485937f416363657373436f6e74726f6c3a206163636f756e742000000000000000000060208601526106ac8151809260206037890191016109d3565b8401917f206973206d697373696e6720726f6c65200000000000000000000000000000006037840152518093868401906109d3565b01036028810183520190610937565b909192600f81166010811015610747576f181899199a1a9b1b9c1cb0b131b232b360811b901a61072085896110ee565b53811c928015610734575f19019190610655565b601182634e487b7160e01b5f525260245ffd5b603283634e487b7160e01b5f525260245ffd5b603290634e487b7160e01b5f525260245ffd5b82346100ee573660031901126100ee576107856108bf565b336001600160a01b038216036100ee5761001f9135610c1a565b9050346100ee57816003193601126100ee5735906107bb6108bf565b90825f5260016020526107d36001825f200154610b02565b825f5260016020526001600160a01b03815f20921691825f5260205260ff815f205416156107fd57005b825f526001602052805f20825f526020525f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4005b82346100ee5760203660031901126100ee57602091355f52600182526001815f2001549051908152f35b90346100ee5760203660031901126100ee57359063ffffffff60e01b82168092036100ee57602091637965db0b60e01b81149081156108ae575b5015158152f35b6301ffc9a760e01b149050836108a7565b602435906001600160a01b03821682036100ee57565b600435906001600160a01b03821682036100ee57565b6060810190811067ffffffffffffffff82111761090757604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761090757604052565b90601f8019910116810190811067ffffffffffffffff82111761090757604052565b67ffffffffffffffff811161090757601f01601f191660200190565b81601f820112156100ee5780359061098c82610959565b9261099a6040519485610937565b828452602083830101116100ee57815f926020809301838601378301015290565b67ffffffffffffffff81116109075760051b60200190565b5f5b8381106109e45750505f910152565b81810151838201526020016109d5565b90602091610a0d815180928185528580860191016109d3565b601f01601f1916010190565b908082519081815260208091019281808460051b8301019501935f915b848310610a465750505050505090565b9091929394958480600192601f19858203018652895190610a8160606001600160a01b038451168352848401519080868501528301906109f4565b916040809101519101529801930193019194939290610a36565b3d15610ac5573d90610aac82610959565b91610aba6040519384610937565b82523d5f602084013e565b606090565b9190811015610ada5760061b0190565b634e487b7160e01b5f52603260045260245ffd5b356001600160a01b03811681036100ee5790565b805f5260018060205260405f20335f5260205260ff60405f20541615610b26575050565b610b2f336110ff565b9160405190610b3d8261091b565b6042825260208201926060368537825115610ada5760308453825160011015610ada57607860218401536041905b808211610bc35750506100ee576100ee926106e1926048926040519485937f416363657373436f6e74726f6c3a206163636f756e742000000000000000000060208601526106ac8151809260206037890191016109d3565b9091600f81166010811015610ada576f181899199a1a9b1b9c1cb0b131b232b360811b901a610bf284866110ee565b5360041c918015610c06575f190190610b6b565b634e487b7160e01b5f52601160045260245ffd5b90815f5260016020526001600160a01b0360405f20911690815f5260205260ff60405f205416610c48575050565b815f52600160205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4565b51906001600160a01b03821682036100ee57565b81516020908301928184019382828203126100ee578282015167ffffffffffffffff928382116100ee57019060409182818303126100ee5782519183830183811086821117610907578452858201518581116100ee5782019088603f830112156100ee5786820151610d13816109bb565b99610d2087519b8c610937565b818b5286898c019260051b850101938185116100ee57908188809695949301925b858410610e7657505050505050610d5b9188845201610c8e565b93808201938585526001600160a01b0380961615610e1a5750610dd296505f8083518551610db281610da48782019463a58d50d360e01b86528860248401526044830190610a19565b03601f198101835282610937565b519082305af1610dc0610a9b565b5015610dd4575b505050511690610f3f565b565b7f5296f22c5d0413b66d0bf45c479c4e2ca5b278634bdbd028b48e49502105f966915190610e0f868651169451928284938452830190610a19565b0390a25f8080610dc7565b94509250509250303b156100ee57610e4e935f918451958692839263a58d50d360e01b845260048401526024830190610a19565b038183305af18015610e6c57610e6357505050565b82116109075752565b82513d5f823e3d90fd5b9091928094959650518a81116100ee578201906060828703601f1901126100ee57895190610ea3826108eb565b610eae8b8401610c8e565b825260608301518c81116100ee5783019185605f840112156100ee57828c01518c94610ee5610edc83610959565b96519687610937565b81865287606083870101116100ee578f95610f0a6080938897606089850191016109d3565b8584015201518c8201528152019301919088959493610d41565b303303610f2d57565b6040516314e1dbf760e11b8152600490fd5b6001600160a01b03919082169081156110be576040516370a0823160e01b8152306004820152926020918285602481875afa9485156110b3575f95611084575b5084610f8d575b5050505050565b169060405181810163a9059cbb60e01b8152836024830152856044830152604482526080820167ffffffffffffffff91838210838311176109075760c08401928311828410176109075761102093855f94938594604052527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152519082885af1611019610a9b565b908561118f565b8051918215918215611064575b50509050156100ee577f74d3741ef03417659087d2ec6af11dade8713f9b7f592569d60cf1ea0c9a44555f80a45f80808080610f86565b8092508193810103126100ee57015180151581036100ee57805f8061102d565b9094508281813d83116110ac575b61109c8183610937565b810103126100ee5751935f610f7f565b503d611092565b6040513d5f823e3d90fd5b91479150816110cc57505050565b8147106100ee575f92839283928392165af16110e6610a9b565b50156100ee57565b908151811015610ada570160200190565b6040519061110c826108eb565b602a8252602082016040368237825115610ada5760309053815160019060011015610ada57607860218401536029905b80821161114c5750506100ee5790565b9091600f81166010811015610ada576f181899199a1a9b1b9c1cb0b131b232b360811b901a61117b84866110ee565b5360041c918015610c06575f19019061113c565b90156111a9578151156111a0575090565b3b156100ee5790565b5080519081156100ee57602001fdfea2646970667358221220109638e2baa3809d56cadab8aaaa168259a1dd097f352abd8b5fc910c56a6d2b64736f6c634300081800330000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "nonce": "0x24b", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7", + "transactionType": "CREATE", + "contractName": "DstOFTHandler", + "contractAddress": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "function": null, + "arguments": [ + "0x3A73033C0b1407574C76BdBAc67f126f6b4a9AA9", + "0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98", + "0x90E2487764E5316a2e4109c2Ed40A3B3ad423659", + "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb", + "0x0980D0F6799CA06C71fFAFdc0E423cF2B0f20502" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x86ec10", + "value": "0x0", + "input": "0x610120604081815234620002a85760a08262007d74803803809162000025828562000337565b833981010312620002a8576200003b826200035b565b6020906200004b8285016200035b565b90620000598486016200035b565b916200007660806200006e606089016200035b565b97016200035b565b60015f558551615d3e808201949291906001600160401b0386118287101762000323576200203682396001600160a01b039586168552888616878601819052948190038801905ff08015620003195785166080525f7f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68082527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680088528160018a82200181815491557fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff92838380a47f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f80835260018a8420019183835493558380a460a0526101009687528551637e062a3560e11b815282851693908681600481885afa9081156200030f579086915f91620002cf575b501603620002be5760c05260e0528351635e280f1160e01b8152908390829060049082905afa928315620002b4575f9362000270575b50508060c051169116036200026057620001fc3362000370565b5051611c14918262000422833960805182818161022c01526110e2015260a0518281816105bb0152818161124d0152611a30015260c05182818161026f0152610df8015260e0518281816101e90152610dcf01525181818161068a01526108db0152f35b516312354ac760e21b8152600490fd5b9080929350813d8311620002ac575b6200028b818362000337565b81010312620002a857518181168103620002a857905f80620001e2565b5f80fd5b503d6200027f565b84513d5f823e3d90fd5b8551633722464560e11b8152600490fd5b809250888092503d831162000307575b620002eb818362000337565b81010312620002a8576200030086916200035b565b5f620001ac565b503d620002df565b88513d5f823e3d90fd5b87513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b038211908210176200032357604052565b51906001600160a01b0382168203620002a857565b6001600160a01b03165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260409020547f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009060ff166200041b575f805260205260405f20815f5260205260405f20600160ff1982541617905533905f7f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b50505f9056fe60806040526004361015610030575b361561002e573461002a5761002236610b73565b602081519101f35b5f80fd5b005b5f3560e01c806301ffc9a71461016a57806305b513bf1461016557806309cfd675146101605780630b2969841461015b57806318f794991461015657806321081d3c14610151578063248a9ca31461014c5780632f2ff15d1461014757806336568abe14610142578063490e662f1461013d5780634b3b029b1461013857806352e12a1414610133578063657cad8a1461012e5780638e35e4451461012457806391d1485414610129578063a217fddf14610124578063c55dae631461011f578063d0a102601461011a578063d547741f146101155763feb617240361000e576109eb565b61098c565b6106dc565b61066b565b6105df565b6105f9565b61059c565b6104a3565b610469565b61042f565b6103e4565b610385565b610328565b6102ee565b610293565b610250565b61020d565b6101ca565b3461002a57602036600319011261002a5760043563ffffffff60e01b811680910361002a57602090637965db0b60e01b81149081156101af575b506040519015158152f35b6301ffc9a760e01b1490505f6101a4565b5f91031261002a57565b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57602036600319011261002a5760043567ffffffffffffffff811680910361002a575f527fe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de00602052602060405f2054604051908152f35b3461002a575f36600319011261002a5760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b3461002a57602036600319011261002a576004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526020600160405f200154604051908152f35b6001600160a01b0381160361002a57565b3461002a57604036600319011261002a5761002e6024356004356103a882610374565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526103df600160405f200154610bd4565b610c43565b3461002a57604036600319011261002a5760243561040181610374565b336001600160a01b0382160361041d5761002e90600435610cfc565b60405163334bd91960e11b8152600490fd5b3461002a575f36600319011261002a5760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b3461002a575f36600319011261002a5760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b3461002a5760408060031936011261002a576004359063ffffffff821680920361002a57602435916104d3610db3565b5f80527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff61051933845f20906001600160a01b03165f5260205260405f2090565b54161561057f577ff3adc8db618887d7b76838e244efb05fc99475bb5a904a914d939fbdc41b7e8d92815f527fe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de0060205280835f205582519182526020820152a160015f55005b815163e2517d3f60e01b81523360048201525f6024820152604490fd5b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a575f36600319011261002a5760206040515f8152f35b3461002a57604036600319011261002a57602060ff61065f60243561061d81610374565b6004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800845260405f20906001600160a01b03165f5260205260405f2090565b54166040519015158152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b9181601f8401121561002a5782359167ffffffffffffffff831161002a576020838186019501011161002a57565b60a036600319011261002a576004356106f481610374565b67ffffffffffffffff9060443582811161002a576107169036906004016106ae565b92610722606435610374565b60843590811161002a5761073a9036906004016106ae565b5050610744610db3565b61077483827fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1009460018655610dc2565b61077e8382610f2f565b80516101201161097b5761079181611575565b916107cd6107c6845f527fe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de0160205260405f2090565b5460ff1690565b61096a575f9461081d9161081861080b865f527fe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de0160205260405f2090565b805460ff19166001179055565b610f74565b9160ff61082983611597565b92610833816115a7565b9361089b610848610843846115b7565b610f83565b61088b610857610843866115c7565b9161086a6108648761106c565b9661108a565b97610873610ac0565b9a8b5260208b01526001600160a01b031660408a0152565b6001600160a01b03166060880152565b60808601528660a08601521691600183148093811561095f575b50861461091d5750610913929161090e916108ce610ae0565b9384526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166020850152604084015215156060830152565b61116b565b5561002e60015f55565b9150506109546109599261094660405193849263073ffe1360e31b602085015260248401610b1c565b03601f198101835282610a9e565b6110d7565b50610913565b60029150145f6108b5565b604051623f613760e71b8152600490fd5b60405162dae46b60e61b8152600490fd5b3461002a57604036600319011261002a5761002e6024356004356109af82610374565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526109e6600160405f200154610bd4565b610cfc565b3461002a57602036600319011261002a576004355f527fe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de01602052602060ff60405f2054166040519015158152f35b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff8111610a6157604052565b610a39565b6040810190811067ffffffffffffffff821117610a6157604052565b6080810190811067ffffffffffffffff821117610a6157604052565b90601f8019910116810190811067ffffffffffffffff821117610a6157604052565b6040519060c0820182811067ffffffffffffffff821117610a6157604052565b60405190610aed82610a82565b565b604051906060820182811067ffffffffffffffff821117610a6157604052565b60405190610aed82610a66565b60c09093929193610b6f8160e081019660a08091805184526020810151602085015260408101516001600160a01b0380911660408601526060820151166060850152608081015160808501520151910152565b0152565b610b7b610db3565b610b8481610bb8565b610b916040519182610a9e565b81815236821161002a575f602083610bb1948383860137830101526110d7565b9060015f55565b67ffffffffffffffff8111610a6157601f01601f191660200190565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff610c1b3360405f20906001600160a01b03165f5260205260405f2090565b541615610c255750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205260ff610c8b8460405f20906001600160a01b03165f5260205260405f2090565b5416610cf557815f52602052610cb58260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916600117905533916001600160a01b0316907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205260ff610d448460405f20906001600160a01b03165f5260205260405f2090565b541615610cf557815f52602052610d6f8260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916905533916001600160a01b0316907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b1561002a57565b60025f541461002a5760025f55565b6001600160a01b039081807f000000000000000000000000000000000000000000000000000000000000000016911603610f0c577f0000000000000000000000000000000000000000000000000000000000000000163303610efa57610e288282610f1e565b7fffffffff0000000000000000000000000000000000000000000000000000000091358281169160048110610ee5575b505060e01c5f8181527fe61a4c968926ec08fb0c5bf5be95077bf8b3ddd75ead66c94187ce8d5509de00602052604090209093915054918215610ec75790610e9f9161178e565b03610ea75750565b604051635f45fa3b60e11b815263ffffffff919091166004820152602490fd5b604051634f1c97f160e11b815263ffffffff85166004820152602490fd5b8391925060040360031b1b1616805f80610e58565b60405163088b800b60e41b8152600490fd5b604051630919067560e41b8152600490fd5b90600c1161002a5760080190600490565b81604c1161002a57604b19820191610f4683610bb8565b92610f546040519485610a9e565b808452368284011161002a57604c5f930160208501378201602b19015290565b90602c1161002a57600c013590565b8060a01c610f97576001600160a01b031690565b6040516379ec0ed760e11b8152600490fd5b5f5b838110610fba5750505f910152565b8181015183820152602001610fab565b81601f8201121561002a578051610fe081610bb8565b92610fee6040519485610a9e565b8184526020828401011161002a5761100c9160208085019101610fa9565b90565b9190916101008184031261002a5780519260208201519260408301519260608101519260808201519260a08301519260c081015160ff8116810361002a579260e082015167ffffffffffffffff811161002a5761100c9201610fca565b61107f906020808251830101910161100f565b509550505050505090565b61109d906020808251830101910161100f565b965050505050505090565b3d156110d2573d906110b982610bb8565b916110c76040519384610a9e565b82523d5f602084013e565b606090565b5f80916020815191017f00000000000000000000000000000000000000000000000000000000000000005af461110b6110a8565b90156111145790565b602081519101fd5b610aed9092919260c081019360a08091805184526020810151602085015260408101516001600160a01b0380911660408601526060820151166060850152608081015160808501520151910152565b6111736115d7565b50604090818101519161119183519360208080968301019101611630565b92808301936111b96111ad6111ad87516001600160a01b031690565b6001600160a01b031690565b83516370a0823160e01b80825230600483015296909290918490849060249082905afa9283156114d3575f9361152f575b506060926112096111ad6111ad868a51016001600160a01b0390511690565b865189815230600482015291908690839060249082905afa9182156114d3575f92611510575b5061128e6112476111ad86516001600160a01b031690565b936112777f000000000000000000000000000000000000000000000000000000000000000095868c51519161179d565b89518701513091906001600160a01b0316906119ab565b6001600160a01b038094166112aa86516001600160a01b031690565b918a515191803b1561002a576112db935f80948d5196879586948593633a5be8cb60e01b8552309160048601611745565b03925af180156114d3576114f7575b506113026111ad6111ad86516001600160a01b031690565b87518a815230600482015291908790839060249082905afa9182156114d3575f926114d8575b5003611442575061141e965061135a61134883516001600160a01b031690565b84885101906001600160a01b03169052565b855151915b61137283885160a0815191015190611b32565b60a088510152828751527fb88fc27be67e678ffb77faf8f8bb00d39b66b4845e4f7ec1e623b0f15abd52138751926113b58785015193516001600160a01b031690565b946113ec6113ce8887519701516001600160a01b031690565b91838b51948594169816968360209093929193604081019481520152565b0390a48351908401511561142157506109466109549293519351938492632132ff4360e11b908401526024830161111c565b50565b92506109466109549251938492627f9f5760e91b908401526024830161111c565b8461145e6111ad6111ad878b51016001600160a01b0390511690565b8751998a523060048b0152899060249082905afa9081156114d35761141e985f926114a4575b5080821061149b5761149591611781565b9161135f565b50505f9161135f565b6114c5919250863d88116114cc575b6114bd8183610a9e565b810190611706565b905f611484565b503d6114b3565b611715565b6114f0919250873d89116114cc576114bd8183610a9e565b905f611328565b8061150461150a92610a4d565b806101c0565b5f6112ea565b611528919250863d88116114cc576114bd8183610a9e565b905f61122f565b611547919350843d86116114cc576114bd8183610a9e565b915f6111ea565b634e487b7160e01b5f52601160045260245ffd5b906001820180921161157057565b61154e565b6020815110611585576020015190565b604051632d0483c560e21b8152600490fd5b6060815110611585576060015190565b6080815110611585576080015190565b60a08151106115855760a0015190565b60c08151106115855760c0015190565b6040519060c0820182811067ffffffffffffffff821117610a61576040525f60a0838281528260208201528260408201528260608201528260808201520152565b67ffffffffffffffff8111610a615760051b60200190565b90602090818382031261002a57825167ffffffffffffffff9384821161002a57019080601f8301121561002a57815161166881611618565b946040906116796040519788610a9e565b828752858088019360051b8601019484861161002a57868101935b8685106116a657505050505050505090565b845183811161002a5782019084601f19838903011261002a578451906116cb82610a66565b898301516116d881610374565b8252858301519185831161002a576116f7898c80969581960101610fca565b83820152815201940193611694565b9081602091031261002a575190565b6040513d5f823e3d90fd5b9060209161173981518092818552858086019101610fa9565b601f01601f1916010190565b9061100c94936080936001600160a01b03809316845260208401521660408201528160608201520190611720565b5f1981019190821161157057565b9190820391821161157057565b90604c1161002a57602c013590565b905f61182493819260405194602086019263a9059cbb60e01b84526001600160a01b0380931660248801526044870152604486526117da86610a82565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602060405161180b81610a66565b8181520152519082855af161181e6110a8565b91611bb5565b8051801590811561183b575b50610aed9150610dac565b6020915082908101031261002a5760200151801515810361002a57610aed905f611830565b9061186a82611618565b60406118796040519283610a9e565b838252819361188a601f1991611618565b01905f5b82811061189b5750505050565b8151906060918281019281841067ffffffffffffffff851117610a615760209385525f82528390818301525f8583015282870101520161188e565b80518210156118ea5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b9190916020908181526060916060820192855193604080848601528551809252608085018460808460051b8801019701935f925b84841061195a5750505050505060409061100c93949501519101906001600160a01b03169052565b9091929394978680600192607f198b82030187528b51906001600160a01b0382511681528580611995858501518a878601528a850190611720565b9301519101529a01940194019294939190611932565b929183516119c06119bb82611562565b611860565b925f5b828110611aa6575060405163ef8738d360e01b60208201526001600160a01b0391821660248201529083166044808301919091528152939450611a9a9361100c93601f19939092611a8b92611a6f9190611a1e606482610a9e565b611a26610aef565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681529060208201525f6040820152611a6882876118d6565b52846118d6565b50611a78610b0f565b9283526001600160a01b03166020830152565b604051938491602083016118fe565b03908101835282610a9e565b80611ac4611ab66001938a6118d6565b51516001600160a01b031690565b602080611ad1848c6118d6565b51015190611aef611ae0610aef565b6001600160a01b039094168452565b8201525f6040820152611b0282886118d6565b52611b0d81876118d6565b50016119c3565b8115611b1e570490565b634e487b7160e01b5f52601260045260245ffd5b919080830180931161157057670de0b6b3a7640000928382029180830485149015171561157057808201809211611570575f19820191821161157057611b7791611b14565b82039180831161157057808202908282041482151715611570578281018091116115705761100c92611bab611bb092611773565b611b14565b611781565b9015611bcf57815115611bc6575090565b3b1561002a5790565b50805190811561002a57602001fdfea26469706673582212204fa917b55b4b9c6370fa7cdd144e4324ce2c961a2790a5ae4b12d18b22af67d064736f6c6343000818003360c0346200010457601f62005d3e38819003918201601f19168301916001600160401b038311848410176200010857808492604094855283398101031262000104576200005a602062000052836200011c565b92016200011c565b6001600160a01b0390911660805260a052604051615c0c90816200013282396080518181816108b7015281816109ff0152818161158b0152818161182f01528181612a1701528181614a0501528181614b7c01528181614f68015261504e015260a051818181610670015281816107de01528181611a7b01528181613a6e01528181614188015281816141e401528181614288015281816142b40152818161435301526146990152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001045756fe6080604052600436101562000012575f80fd5b5f803560e01c806246912e146200257057806301ffc9a71462002515578063037a06a414620022c957806304c73f6014620022a9578063057f037014620021f45780631f74a0b5146200217257806321081d3c1462002135578063248a9ca314620020e75780632e748b211462001f7f5780632f2ff15d1462001f1a578063319adf9f1462001b6657806336568abe1462001b1757806337710e201462001abd57806339fff0981462001a225780633b1c6a0114620017275780633cf3a02514620016eb5780634265fe861462001699578063490e662f146200165c5780634b3b029b146200161f578063502a82e21462001554578063521c98ba1462000d9157806369b97ac71462000d7157806379c7b60b1462000d1757806379c7f2891462000c1557806390a0827b1462000bd457806391d148541462000b6657806396cc2cfb14620008f9578063a217fddf14620008db578063a4b672b61462000895578063af5de6f91462000802578063c55dae6314620007bc578063ccbedaec1462000542578063d06e28ed1462000443578063d547741f14620003da578063e38b73a914620003bc578063ea0aaf241462000359578063eb84e7f2146200024b5763ff3eae0014620001e2575f80fd5b34620002485760c0366003190112620002485762000200366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10054156200023657620002339062004e31565b80f35b60405163cd6d8f7d60e01b8152600490fd5b80fd5b503462000248576020366003190112620002485760408160c09260a0835162000274816200270b565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051620002c8816200270b565b6001600160a01b039060028284541693848352600181015490602084019185811683526001600160401b0393849283604088019360a01c1683520154956060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b50346200024857602036600319011262000248576020620003b36200037d620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b50346200024857806003193601126200024857602060405160068152f35b50346200024857604036600319011262000248576200043f600435620003ff620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262000439600160408620015462002e64565b620039b1565b5080f35b503462000248576040366003190112620002485762000461620025de565b602435906fffffffffffffffffffffffffffffffff82168092036200053e576200048a62002e08565b6200049581620032e8565b90836001600160a01b039263ffffffff84608083015116915116813b156200053a57829160448392604051948593849263435354d360e01b845260048401528a60248401525af180156200052f5762000513575b5050167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b6200051e90620026f7565b6200052b57835f620004e9565b8380fd5b6040513d84823e3d90fd5b8280fd5b5f80fd5b50346200024857606036600319011262000248578062000561620025de565b6200056b6200260c565b906200057662002623565b906200058162002d37565b620005bc816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200071a574362000601826001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0380600162000648846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b01541693816001600160401b03938483166200071f575b5050508183166200066e578480f35b7f00000000000000000000000000000000000000000000000000000000000000001684525f8051602062005bb783398151915260205260046040852001541690823b156200071a5760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156200052f5762000702575b8080808480f35b6200070d90620026f7565b6200024857805f620006fb565b505050fd5b1686525f8051602062005bb783398151915260205282600460408820015416853b15620007b85760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152858160648183895af1908115620007ad57869162000791575b82906200065f565b6200079c90620026f7565b620007a957845f62000789565b8480fd5b6040513d88823e3d90fd5b8680fd5b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503462000248576020366003190112620002485760206001600160a01b03620008346200082e620025de565b620031ff565b6109ab60405162000848858301826200277c565b818152848101916200520c8339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152620008898162002744565b51902016604051908152f35b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346200024857806003193601126200024857602090604051908152f35b503462000248576040366003190112620002485762000917620025de565b906200092262002e08565b6200092d8262002f58565b6200093883620032e8565b926200094e60e083510151840b60243562004da2565b9460208401906001600160401b036200097381845116898360a08a01511691620038d2565b1562000b3657869750620009b7856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b620009c48582546200287c565b90556001600160a01b03808616807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051898152a2817f000000000000000000000000000000000000000000000000000000000000000016803b1562000b325760405163f3fef3a360e01b81526001600160a01b038316600482015260248101889052908a908290604490829084905af1801562000b275787918b9162000b08575b505062000a8290608060e0960192848451169062003353565b511692511694510151850b93813b1562000b0457604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156200052f5762000af15750f35b62000afc90620026f7565b620002485780f35b8580fd5b62000b15919250620026f7565b62000b235785895f62000a69565b8880fd5b6040513d8c823e3d90fd5b8980fd5b6040516377e88bc960e11b81526001600160a01b03861660048201526001600160401b0389166024820152604490fd5b50346200024857604036600319011262000248576001600160a01b03604062000b8e620025f5565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020522091165f52602052602060ff60405f2054166040519015158152f35b50346200024857604036600319011262000248576200023362000bf6620025de565b62000c0062002d37565b602435906001600160a01b0333911662003353565b503462000248576020366003190112620002485760a062000c7a62000c39620025de565b62000c4362002d0b565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b0360016040519262000c9384620026db565b805463ffffffff90818116865260ff8160201c1615156020870152818160281c16604087015260481c166060850152015416608082015262000d1560405180926001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565bf35b50346200024857602036600319011262000248576020620003b362000d3b620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b503462000248578060031936011262000248576020604051620f42408152f35b5034620002485760a0366003190112620002485762000daf620025de565b62000db96200263a565b9062000dc46200264e565b6001600160401b0360643516606435036200053e57608435906001600160401b03821682036200053e5762000df862002db1565b62000e0262002c05565b508480604051602081019063ffffffff881682526020815262000e258162002760565b519061080c5afa62000e3662002cd7565b901562001542578051810190602081830312620007b85760208101516001600160401b038111620015055761010081830184031262001505576040519262000e7e8462002727565b602082840101516001600160401b03811162000b325760208201603f828587010101121562000b32576020818486010101519062000ebc8262002cbb565b9162000ecc60405193846200277c565b8083526020840160408284888a01010101116200152a5762000efa9160406020850191878901010162002691565b8452604082840101516001600160401b03811162000b325760208201603f828587010101121562000b3257602081848601010151916001600160401b0383116200152e578260051b6040519362000f5560208301866200277c565b84526020808501920160408285888a01010101116200152a576040838688010101915b60408285888a01010101831062001509575050505060208401526101009062000fa683820160600162003428565b604085015262000fbb6080828501016200512c565b606085015262000fd060a0828501016200512c565b9283608086015262000fe760c08383010162005141565b60a086015262000ffc60e08383010162005141565b60c08601520101519081880b820362001505578160e08401526001600160a01b03808716911603620007b8576200103890870b60643562003829565b506040519162001048836200270b565b825263ffffffff86166020830152821515604083015260608201526001600160401b036064351660808201526001600160401b03831660a08201526001600160a01b03841686525f8051602062005bb783398151915260205260408620815180518051906001600160401b03821162001438578190620010c9855462002c80565b601f8111620014c5575b50602090601f831160011462001458578b926200144c575b50508160011b915f199060031b1c19161782555b60208101518051906001600160401b038211620014385768010000000000000000821162001438576020906001850154836001870155808410620013e5575b500190600184018a5260208a20908a5b8160021c81106200139d5750600319811680820362001334575b50505050936001600160a01b03937f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd82244353969360068460809860026001600160401b039701876040830151168154907bffffffffffffffffffffffffffffffffffffffff0000000000000000606085015160401b169163ffffffff60e01b16171790556003820190898b820151169082549174ff000000000000000000000000000000000000000060a083015160a01b16907fffffffffffffffffff000000000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff00000000000000000000000000000000000000000000169416171717179055620012c5600482018760208601511688198254161781556040850151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b6060830151600582015501908488820151166fffffffffffffffff000000000000000060a0845493015160401b16916fffffffffffffffffffffffffffffffff19161717905563ffffffff6040519816885215156020880152816064351660408801521660608601521692a280f35b928b938c5b81840381106200135d5750505060021c0155838360066001600160401b0362001168565b9091946020620013926001926001600160401b03895116908560031b60031b916001600160401b03809116831b921b19161790565b960192910162001339565b8b8c5b60048110620013b75750838201556001016200114e565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501620013a0565b6200141890600187018d52838d20600380870160021c820192601888831b16806200141f575b500160021c019062004d8a565b5f6200113e565b5f1990818601918254918a03851b1c1690555f6200140b565b634e487b7160e01b8a52604160045260248afd5b015190505f80620010eb565b9250848b5260208b20908b935b601f1984168510620014a9576001945083601f1981161062001490575b505050811b018255620010ff565b01515f1960f88460031b161c191690555f808062001482565b8181015183556020948501946001909301929091019062001465565b620014f390868d5260208d20601f850160051c81019160208610620014fa575b601f0160051c019062004d8a565b5f620010d3565b9091508190620014e5565b8780fd5b906020806040936200151b8662003428565b81520193019290915062000f78565b8b80fd5b634e487b7160e01b8b52604160045260248bfd5b604051639b0c335d60e01b8152600490fd5b503462000248576040366003190112620002485762001572620025de565b6024356200157f62002d37565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156200161b5760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156200052f5762001603575b50506200023391339062003353565b6200160e90620026f7565b6200053a57825f620015f4565b5080fd5b5034620002485780600319360112620002485760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b5034620002485780600319360112620002485760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b5034620002485760c03660031901126200024857620016b8366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576200023390620046c4565b50346200024857602036600319011262000248576200170962002d37565b80808080600435335af16200171d62002cd7565b5015620002485780f35b503462000248576060366003190112620002485762001745620025f5565b6001600160a01b0360443581811692918382036200053e576200176762002e08565b620017728262002f58565b906200177e8462003257565b62000b045760408201511562000b04576001600160401b0391600183608083015116019083821162001a0e57620017bd60e082510151890b8362003829565b5094620017db602083019386855116908760a08601511691620038d2565b1562000b23576200181c906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b620018298682546200287c565b905587837f000000000000000000000000000000000000000000000000000000000000000016803b156200161b5760405163f3fef3a360e01b81526001600160a01b038a166004820152602481018890529082908290604490829084905af180156200052f57620019f0575b50509183826200192494620018bc60e062001913986024975116925101518c0b8962004da2565b508481620019d0575b505050511695604051916020830191169687825260408301526001606083015260608252620018f48262002744565b6040519485926280000360e11b60208501525180928585019062002691565b81010360048101845201826200277c565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062001970906024830190620026b4565b03925af18015620019c557620019b3575b506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b620019be90620026f7565b5f62001981565b6040513d5f823e3d90fd5b620019df620019e79362002847565b168b62003353565b5f8084620018c5565b620019ff9094929394620026f7565b62001505579190875f62001895565b634e487b7160e01b5f52601160045260245ffd5b5034620002485760e0366003190112620002485762001a41366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576001600160a01b0380606083015116907f000000000000000000000000000000000000000000000000000000000000000016145f1462001aae576200023390620046c4565b620002339060c4359062003a45565b50346200024857602036600319011262000248576020620003b362001ae1620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b503462000248576040366003190112620002485762001b35620025f5565b336001600160a01b0382160362001b54576200043f90600435620039b1565b60405163334bd91960e11b8152600490fd5b50346200024857602036600319011262000248576001600160a01b0362001b8c620025de565b62001b9662002c45565b501681525f8051602062005bb783398151915260205260408120906040519162001bc0836200270b565b60405162001bce8162002727565b6040518254818562001be08362002c80565b808352926001811690811562001ef9575060011462001eb4575b62001c08925003826200277c565b815260405160018301805480835290855260208086209083019186915b81600384011062001e72579360069593819362001c73936001600160401b039997549181811062001e5e575b81811062001e47575b81811062001e30575b1062001e21575b5003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c850b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a0830152604051906020825282519060c0602084015262001d31825161010060e08601526101e0850190620026b4565b60208084015185830360df190161010087015280518084529282019492910190835b81811062001e015750505083946001600160401b039260e08385604060a0960151166101208901526001600160a01b036060820151166101408901526001600160a01b0360808201511661016089015260ff858201511661018089015260ff60c0820151166101a08901520151900b6101c086015282602082015116604086015260408101511515606086015260608101516080860152826080820151168286015201511660c08301520390f35b82516001600160401b031686526020958601959092019160010162001d53565b60c01c81526020015f62001c6a565b9260206001918c8560801c16815201930162001c63565b9260206001918c8560401c16815201930162001c5a565b9260206001918c8516815201930162001c51565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162001c25565b5090848652602086209086915b81831062001edc57505090602062001c089282010162001bfa565b602091935080600191548385880101520191019091839262001ec1565b6020925062001c0894915060ff191682840152151560051b82010162001bfa565b50346200024857604036600319011262000248576200043f60043562001f3f620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262001f79600160408620015462002e64565b62003912565b503462000248576080366003190112620002485762001f9d620025de565b62001fa76200260c565b9062001fb262002623565b90606435926fffffffffffffffffffffffffffffffff84168094036200053e5762001fdc62002e08565b62001fe782620032e8565b856001600160a01b03928360808401511692833b156200053a57620020646101048492836040519586948593636f0d192560e11b855260048501906001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565b6001600160401b038091169b8c60a485015216978860c48401528c60e48401525af180156200052f57620020cb575b5050907f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c92604092835195865260208601521692a380f35b620020d990939293620026f7565b62000b045790855f62002093565b50346200024857602036600319011262000248576001604060209260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008452200154604051908152f35b5034620002485780600319360112620002485760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503462000248576060366003190112620002485762002190620025de565b6001600160401b036024358181116200052b57620021b39036906004016200265e565b91909360443591821162000248576020620021ec868686620021d936600489016200265e565b939092620021e662002e08565b6200288a565b604051908152f35b50346200053e5760403660031901126200053e5762002212620025de565b906024356200222062002d37565b6001600160a01b03928360806200223783620032e8565b015116803b156200053e576040516390a0827b60e01b81526001600160a01b038316600482015260248101849052905f908290604490829084905af18015620019c55762002290575b5062000233929333911662003353565b620002339350620022a190620026f7565b5f9262002280565b346200053e575f3660031901126200053e5760206040516305f5e1008152f35b346200053e5760a03660031901126200053e57620022e6620025de565b620022f06200263a565b620022fa6200264e565b6064359063ffffffff938483168093036200053e57608435908582168092036200053e57620023298162002f58565b506200233462002db1565b6001600160a01b03918260016200237b846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b0154168015620024bc575b8360018960405199620023998b620026db565b16988981526020810197151597888152604082018a81526060830190878252856080850197169d8e8852620023fe8a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b9451166cffffffff00000000000000000068ffffffff000000000064ff0000000087549651151560201b16935160281b16935160481b16936cffffffffffffffffffffffffff19161717171781550191511673ffffffffffffffffffffffffffffffffffffffff19825416179055620024778762003257565b156200053e577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9946080946040519788526020880152604087015260608601521692a3005b50620024c882620031ff565b6040516109ab8082018281106001600160401b03821117620025015782916200520c833903905ff58062002386576040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b346200053e5760203660031901126200053e5760043563ffffffff60e01b81168091036200053e57602090637965db0b60e01b81149081156200255e575b506040519015158152f35b6301ffc9a760e01b1490508262002553565b346200053e5760403660031901126200053e57620025dc62002591620025de565b6001600160a01b03620025a36200260c565b91620025ae62002d37565b165f525f8051602062005bb78339815191526020526001600160401b03600460405f20015416339062002ea6565b005b600435906001600160a01b03821682036200053e57565b602435906001600160a01b03821682036200053e57565b602435906001600160401b03821682036200053e57565b604435906001600160401b03821682036200053e57565b6024359063ffffffff821682036200053e57565b6044359081151582036200053e57565b9181601f840112156200053e578235916001600160401b0383116200053e576020808501948460051b0101116200053e57565b5f5b838110620026a35750505f910152565b818101518382015260200162002693565b90602091620026cf8151809281855285808601910162002691565b601f01601f1916010190565b60a081019081106001600160401b038211176200250157604052565b6001600160401b0381116200250157604052565b60c081019081106001600160401b038211176200250157604052565b61010081019081106001600160401b038211176200250157604052565b608081019081106001600160401b038211176200250157604052565b604081019081106001600160401b038211176200250157604052565b90601f801991011681019081106001600160401b038211176200250157604052565b60c09060031901126200053e5760405190620027ba826200270b565b60043582526024356020830152816001600160a01b0360443581811681036200053e57604083015260643590811681036200053e576060820152608435608082015260a060a435910152565b9190811015620028175760051b0190565b634e487b7160e01b5f52603260045260245ffd5b9190916001600160401b038080941691160191821162001a0e57565b9073200000000000000000000000000000000000000091820180921162001a0e57565b906305f5e0ff820180921162001a0e57565b9190820180921162001a0e57565b919392935f945f948083036200053e57620028d5856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200053e57869796939291620028ef8662002f58565b926080620028fd88620032e8565b01906001600160a01b0392838351169460208701976200292a6001600160401b0397888b5116906200343d565b845f9b5b1062002b94575b50508c15925062002b8791505057436200297f886001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5582861662002993575b5050505050505050565b620029c195620029ab60e0865101515f0b8262003829565b85889992995116908660a08901511691620038d2565b1562002b4d575062002a03876001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62002a108782546200287c565b90558682167f00000000000000000000000000000000000000000000000000000000000000008316803b156200053e5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101899052905f908290604490829084905af18015620019c55762002b29575b509062002a9587849360e096958451169062003353565b511693511691510151850b93823b1562000b0457604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156200052f5762002b11575b80808080808062002989565b62002b1d8291620026f7565b62000248578062002b05565b60e094939291995062002b3c90620026f7565b5f98929350909162002a9562002a7e565b8451608001516040516377e88bc960e11b81529084166001600160a01b031660048201526001600160401b03919091166024820152604490fd5b505f985050505050505050565b62002ba6859e9f809c95968462002806565b359062002bb58c858862002806565b359189831683036200053e578a62002bd2938a8a51169262003520565b9490911562002bfb5762002beb8f93926001926200282b565b9b019e9d8f95949192956200292e565b9a9e9d5062002935565b6040519062002c148262002727565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b6040519062002c54826200270b565b5f60a08362002c6262002c05565b81528260208201528260408201528260608201528260808201520152565b90600182811c9216801562002cb0575b602083101462002c9c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162002c90565b6001600160401b0381116200250157601f01601f191660200190565b3d1562002d06573d9062002ceb8262002cbb565b9162002cfb60405193846200277c565b82523d5f602084013e565b606090565b6040519062002d1a82620026db565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a60205260409020547f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f9060ff161562002d935750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161562002dea57565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd60205260409020547f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef69060ff161562002d935750565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f2054161562002d935750565b620019139262002eec92602492604051926001600160a01b0360208501931683526001600160401b03809216604085015216606083015260608252620018f48262002744565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062002f38906024830190620026b4565b03925af18015620019c55762002f4b5750565b62002f5690620026f7565b565b9062002f6362002c45565b506001600160a01b038092165f5260205f8051602062005bb78339815191528152604092835f2084519062002f98826200270b565b85519562002fa68762002727565b80518254905f8162002fb88462002c80565b918282526001948a86821691825f14620031e05750506001146200319f575b62002fe5925003826200277c565b88528083019082519087829384928282549586815201915f52825f20945f5b8160038201106200315a57620030469654928583831062003140575b83831062003124575b83831062003107575b505010620030f8575b50905003826200277c565b858801526006600283015492856001600160401b0394858116858c0152841c1660608a015260ff60038201549760808b019a888a168c52828a60a01c1660a082015260c0810199838160a81c168b5260b01c5f0b60e0820152875260048301549086821690880152841c161515838601526005810154606086015201549082821660808501521c1660a082015293511615159081620030e9575b50156200053e57565b60ff9150511615155f620030e0565b60c01c81520188905f6200303b565b9091946001600160401b038560801c1681520193015f8562003032565b8192956001600160401b03868d1c168152019401908562003029565b8192956001600160401b0386168152019401908562003020565b86546001600160401b038082168652818b1c811696860196909652608081811c9096168a86015260c01c6060850152958201958795508c949093019260040162003004565b5050845f528188805f20855f915b858310620031c657505062002fe5935082010162002fd7565b8091929450548385880101520191018990858593620031ad565b60ff19168482015262002fe594151560051b840101915062002fd79050565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152606081018181106001600160401b03821117620025015760405251902090565b519081151582036200053e57565b5f80916040516001600160a01b036020820192168252602081526200327c8162002760565b51906108105afa6200328d62002cd7565b9015620032d6576020818051810103126200053e5760405190602082018281106001600160401b038211176200250157604052620032ce9060200162003249565b809152151590565b6040516313dd7ccd60e31b8152600490fd5b620032f79062000c4362002d0b565b906001600160a01b036001604051936200331185620026db565b805463ffffffff90818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156200053e57565b60405163a9059cbb60e01b602082019081526001600160a01b039384166024830152604480830195909552938152620033ea9390925f92839291906200339b6064876200277c565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051620033ce8162002760565b8181520152519082855af1620033e362002cd7565b91620051de565b805190811591821562003402575b5050156200053e57565b81925090602091810103126200053e57602062003420910162003249565b5f80620033f8565b51906001600160401b03821682036200053e57565b604080516001600160a01b0392909216602083019081526001600160401b039390931682820152808252915f918291906200347a6060826200277c565b51906108015afa6200348b62002cd7565b9015620034f5576060818051810103126200053e5781516001600160401b0392606082018481118382101762002501578152620034ee6060620034d16020860162003428565b94858552620034e284820162003428565b60208601520162003428565b9101521690565b8151639d2c8fcb60e01b8152600490fd5b6001600160401b03918216908216039190821162001a0e57565b949192909394805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20906001600160a01b039485835416958615620037ab57600284019788549860ff8a60481c166200379957600186015483608086510151168482160362003780576001600160401b039060a01c166001600160401b038b1660ff8c60401c165f146200371f575080926001600160401b03811682115f146200371657620035d79162003506565b995b620035e58b8d6200282b565b9b6001600160401b038d166001600160401b0385161162003704575069010000000000000000009069ff00000000000000000019161790556200362b8160019b62003506565b976001600160401b036020846200364b8d60e0895101515f0b9062003829565b50991695015116843b156200053e5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152925f908490606490829084905af1918215620019c5577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4393604093620036f2575b5060018187541696015416956001600160401b038351921682526020820152a4565b620036fd90620026f7565b5f620036d0565b5f9c508c9b5099505050505050505050565b50505f620035d7565b9092809b93806001600160401b038316105f1462003773576200374f91620037479162003506565b809c6200282b565b816001600160401b038216115f146200376b57505b91620035d9565b905062003764565b50506200374f5f62003747565b60405163358d72d160e01b815260048101879052602490fd5b60405163f7348a7960e01b8152600490fd5b604051631a40316d60e01b8152600490fd5b60ff16604d811162001a0e57600a0a90565b9190820391821162001a0e57565b8115620037e8570490565b634e487b7160e01b5f52601260045260245ffd5b90620f42409182810292818404149015171562001a0e57565b8181029291811591840414171562001a0e57565b9190805f0b80155f14620038475750506001600160401b0382169190565b5f81131562003877575062003863620038739160ff16620037bd565b6001600160401b03841662003815565b9190565b9050607f19811462001a0e5762003893905f0360ff16620037bd565b91620038aa836001600160401b038093166200287c565b5f19810190811162001a0e57620038c684620038ce92620037dd565b938462003815565b1690565b6200390a6200390392936001600160401b0393836001600160a01b03620038fc8780971662002847565b166200343d565b946200282b565b169116101590565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f205416155f14620039aa57825f5260205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f2054165f14620039aa57825f5260205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b9062003a5e6001600160a01b0360408401511662003257565b1562004618576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165f525f8051602062005bb783398151915260205260405f20906040519162003ab6836200270b565b60405162003ac48162002727565b6040518254815f62003ad68362002c80565b8083529260018116908115620045f75750600114620045b2575b62003afe925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b81600384011062004570579360069593819362003b67936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a08301526001600160a01b036060840151165f525f8051602062005bb783398151915260205260405f206040519062003c2e826200270b565b60405162003c3c8162002727565b6040518254815f62003c4e8362002c80565b80835292600181169081156200454f57506001146200450a575b62003c76925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b816003840110620044bd579360069593819362003cdf936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015262003d896001600160a01b03606086015116620032e8565b9184519062003dd662003dbb62003dac60a089015160808a01511515956200287c565b60e0885101515f0b9062004da2565b905060ff60c0885101511660ff60c087510151169162005165565b91156200448c5781925b5f8063ffffffff875116604051602081019182526020815262003e038162002760565b51906108085afa9062003e1562002cd7565b91156200447a576020828051810103126200053e576001600160401b0362003e44602062003e6d940162003428565b60208801519116919015620044665760ff60a062003e669251015116620037bd565b9062003815565b602085015115620044175763ffffffff62003e8e8160608801511662005026565b16620f4240818101811162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c01511691016200287c565b0416906080870151908115155f146200440e5750905b602085015115620043c5576305f5e1008110156200437e57505f5b62003f2462003f0b60a0890151620037fc565b62003f1d895160a08b0151906200287c565b906200287c565b5f1981019190821162001a0e5762003f1d62003f539262003f4c8a5160a08c0151906200287c565b90620037dd565b606482028281046064148315171562001a0e578111620042df57505062003f84855160e0865101515f0b9062004da2565b92909562003faf6001600160401b03602088015116856001600160401b0360a08a01511691620038d2565b156200423b5791608093917f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91860a0856001600160401b036001600160a01b036040819b990151168a606084015116908a8401511515916040519162004014836200270b565b82526020820152828816604082015282871660608201528a81019182528481015f815262004118602086019384515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b03602052856060600260405f206001600160a01b0388511673ffffffffffffffffffffffffffffffffffffffff19825416178155600181016001600160a01b0360208a0151168154907bffffffffffffffff000000000000000000000000000000000000000060408c015160a01b169163ffffffff60e01b161717905501950151168619855416178455511515839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151569ff00000000000000000082549160481b169069ff00000000000000000019161790555194818b60408501511697818d6060870151169a87875197015160405197885260208801521660408601521660608401521688820152a4015116620041ae83826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001662003353565b60e06001600160401b03602084015116925101515f0b90803b156200053e57604051639c45c34b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160401b03909316602484015260448301939093525f90810b6064830152909182908183816084810162002f38565b94505050905062002f56925060208201516001600160401b03604051921682527f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c60206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b9350949350505060208201516001600160a01b0360608401511691606481019081811162001a0e5760630190811162001a0e5760407fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e9162002f569660648351920482526020820152a36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166060820152620046c4565b6001600160401b036305f5e0ff1982011162001a0e57620043be620043b86001600160401b036305f5e100936305f5e0ff190116620037fc565b6200286a565b0462003ef8565b6305f5e100811115620043da57505f62003ef8565b6001600160401b03816305f5e100031162001a0e57620043be620043b86001600160401b036305f5e10093840316620037fc565b90509062003edd565b63ffffffff6200442d8160608801511662005026565b16620f4240808281031162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c0151169103620037cf565b5062003e6660ff60a08951015116620037bd565b604051635cffc5fb60e11b8152600490fd5b6127108181031162001a0e576001600160401b03612710620044b383820383861662003815565b0416919262003de0565b926001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019162003c95565b5090845f5260205f20905f915b8183106200453257505090602062003c769282010162003c68565b602091935080600191548385880101520191019091839262004517565b6020925062003c7694915060ff191682840152151560051b82010162003c68565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162003b1d565b5090845f5260205f20905f915b818310620045da57505090602062003afe9282010162003af0565b6020919350806001915483858801015201910190918392620045bf565b6020925062003afe94915060ff191682840152151560051b82010162003af0565b506080810151909190156200464c5760246001600160a01b03604084015116604051906320a2097d60e11b82526004820152fd5b9080602062002f569201517f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b03604085015116604051908152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b6060906060810151905f6001600160a01b0380931692835f526020945f8051602062005bb78339815191528652604091825f2096835162004705816200270b565b845193620047138562002727565b85518a5491905f81620047268562002c80565b918282526001958887821691825f1462004d6b57505060011462004d2a575b62004756925094939403826200277c565b8652808b019184885193898586948484549283815201935f52845f20965f915b83600384011062004cde575085620047b998549484841062004cc4575b84841062004ca7575b5083831062004c8a575b50501062004c7b5750905003826200277c565b82850152600289015495836001600160401b039788811688880152871c16606086015260038a0154946080958581168783015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528252600660048b01549a60ff8585019c8d8b82169052891c1615158885015260058101546060850152015490878216868401528760a0840192881c168252868901976200485e868a511662003257565b1562004c0d57899a9b5f9a98999a508c612710620048958b519b8b6200488b60a083019e8f51906200287c565b9101519062003815565b04988a5199808b1162004c04575b508d8a8062004b2e575b620048c2620048d191620048e493516200287c565b60e08a5101515f0b9062004da2565b91909782878088511692511691620038d2565b1562004ae65750908c9695949392918a620049f9575b509260e0896200498f94620049c59b9997947fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b99976200496e906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6200497b8d82546200287c565b9055511696511694510151900b9062004da2565b9190928d84620049ca575b5050505050880151965116965192519351938493846040919493926060820195825260208201520152565b0390a4565b620049ee94620049e89188620049e08562002847565b169062003353565b62002ea6565b5f8080808d6200499a565b919293949596505087807f00000000000000000000000000000000000000000000000000000000000000001691875101511690803b156200053e578b5163f3fef3a360e01b81526001600160a01b03929092166004830152602482018a90525f908290604490829084905af1801562004adc578c948f91998098956200498f957fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b9995620049c59d62004ac0575b509497995094509950939597999a9b620048fa565b60e0945062004acf90620026f7565b6200496e5f945062004aab565b8b513d5f823e3d90fd5b9a50505050955050505093957f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c955084915062004b238162004e31565b0151945191168152a3565b62004b45908a8301518c858c51015116906200503f565b1562004b53575b8a620048ad565b509192939495968881999a50602492508389510151168c51928380926370a0823160e01b82528d7f00000000000000000000000000000000000000000000000000000000000000001660048301525afa90811562004adc57918e918e9a999897969594935f9162004bc7575b509962004b4c565b92505098508681813d831162004bfc575b62004be481836200277c565b810103126200053e57518c988e91620048d162004bbf565b503d62004bd8565b99505f620048a3565b505050509296509450839293015115155f1462004c405750505191516320a2097d60e11b81529116600482015260249150fd5b62002f56948294827f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d9401519451169051908152a262004e31565b60c01c81520185905f6200303b565b9091946001600160401b038560801c1681520193015f85620047a6565b956001600160401b03868495981c16815201940190858e6200479c565b6001600160401b0386168752958101959282019262004793565b95509360049296509284919388546001600160401b039283821683528d8483831c169084015260809382851c169083015260c01c8782015201960191019386948c9296938a9562004776565b50508c5f528186805f20865f915b85831062004d5157505062004756935082010162004745565b809192945054838588010152019101879086859362004d38565b60ff1916848201526200475694151560051b8401019150620047459050565b81811062004d96575050565b5f815560010162004d8a565b9190805f0b9081155f1462004dc05750506001600160401b03821690565b5f82131562004e035762004dd8915060ff16620037bd565b918215620037e857620038ce62004dfb82856001600160401b03940690620037cf565b9384620037dd565b505f0380805f0b0362001a0e57620038ce62004e2a6001600160401b039260ff16620037bd565b8462003815565b805161271062004e5862004e4c60a08501938451906200287c565b60808501519062003815565b0481518181115f146200501d5750905b8190602084019182519162004e8f60608701956001600160a01b039485885116906200503f565b1562005014575b8162004f61575b620049c5837f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9462004ed1858a516200287c565b9662004eeb828a51169860408c0199848b51169062003353565b62004f29828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62004f368782546200287c565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b84518316927f00000000000000000000000000000000000000000000000000000000000000008116803b156200053e5760405163f3fef3a360e01b81526001600160a01b03959095166004860152602485018490525f908590604490829084905af1908115620019c5577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f94620049c59262005002575b5093505062004e9d565b6200500d90620026f7565b5f62004ff8565b5f915062004e96565b90509062004e68565b90606463ffffffff8093160291821691820362001a0e57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa918215620019c5575f92620050f2575b5080821094851595620050b2575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080620050ac565b9091506020813d60201162005123575b8162005111602093836200277c565b810103126200053e5751905f6200509e565b3d915062005102565b51906001600160a01b03821682036200053e57565b519060ff821682036200053e57565b9060ff8091169116039060ff821162001a0e57565b919060ff821660ff82168181145f1462005180575050505090565b929391921015620051b857620038ce9192620051b0620051aa6001600160401b0395869362005150565b620037bd565b911662003815565b620038ce91620051d6620051aa6001600160401b0395869362005150565b9116620037dd565b9015620051fb57815115620051f1575090565b3b156200053e5790565b5080519081156200053e57602001fdfe60a080604052346100455733608052610961908161004a8239608051818181608b015281816101ac0152818161038e015281816104a20152818161051b015261069c0152f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f915f3560e01c908163435354d3146104d757816390a0827b1461047b5781639c45c34b14610469578163a703334f1461033c578163de1a324a146100d457508063e94b77c1146100b35763eba61c0e1461006d575f80fd5b346100af57816003193601126100af57602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b82346100d1576100ce6100c5366105b5565b9291909161068d565b80f35b80fd5b91905034610338576101009136600319018381126103345760a01361033057815167ffffffffffffffff939060a081018581118282101761031d5784526101196105a2565b91828252602435918215158303610319576020810192835263ffffffff9360443585811681036102ce578783015260643585811681036102ce5760608301526084356001600160a01b03928382168203610315576080015260a4358881168091036102ce5760c435918983168093036102ce5760e435936fffffffffffffffffffffffffffffffff85168095036102ce577f000000000000000000000000000000000000000000000000000000000000000016330361031557866101dd9116610663565b9451151581156103055782156102f5578a6102e557885196602088019616865288870152606086015260808501525f60a0850152600260c085015260e084015260e08352820194828610908611176102d25761026891602491868652630100000160e01b6101208301526102598251809261012485019061079c565b81010383810186520184610641565b73333333333333333333333333333333333333333392833b156102ce576102a5935f928385518097819582946317938e1360e01b845283016107bd565b03925af19081156102c557506102b9575080f35b6102c391506105fd565b005b513d5f823e3d90fd5b5f80fd5b604183634e487b7160e01b5f525260245ffd5b88516376d4929560e11b81528890fd5b88516313c0a8df60e01b81528890fd5b885163017461b760e71b81528890fd5b8a80fd5b8780fd5b604184634e487b7160e01b5f525260245ffd5b8380fd5b8480fd5b8280fd5b919050346103385760603660031901126103385781359067ffffffffffffffff928383168093036102ce576024356001600160a01b03948582168092036102ce576044359081168091036102ce5786957f000000000000000000000000000000000000000000000000000000000000000016330361046557610259946103fc9260249286519260208401928352878401526060830152606082526103df82610625565b85519687926280000360e11b60208501525180928585019061079c565b73333333333333333333333333333333333333333392833b15610334576104399385928385518097819582946317938e1360e01b845283016107bd565b03925af190811561045c575061044c5750f35b610455906105fd565b6100d15780f35b513d84823e3d90fd5b8580fd5b83346100d1576100ce6100c5366105b5565b91905034610338573660031901126100af57356001600160a01b038082168092036102ce577f00000000000000000000000000000000000000000000000000000000000000001633036100af576100ce906024359033906107e9565b919050346102ce57806003193601126102ce576104f26105a2565b91602435926fffffffffffffffffffffffffffffffff84168094036102ce576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102ce5761054a90610663565b9282519063ffffffff602083019516855283820152828152606081019381851067ffffffffffffffff8611176102d25761026891602491868652630100000b60e01b608083015261025982518092608485019061079c565b6004359063ffffffff821682036102ce57565b60809060031901126102ce576004356001600160a01b03811681036102ce579060243567ffffffffffffffff811681036102ce579060443590606435805f0b81036102ce5790565b67ffffffffffffffff811161061157604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761061157604052565b90601f8019910116810190811067ffffffffffffffff82111761061157604052565b9061271063ffffffff8093160191821161067957565b634e487b7160e01b5f52601160045260245ffd5b9092916001600160a01b0392837f00000000000000000000000000000000000000000000000000000000000000001633036102ce57805f0b80155f1461071c575050915b826106dd575b50505050565b67ffffffffffffffff732000000000000000000000000000000000000000941684018094116106795780610713941691166107e9565b5f8080806106d7565b5f81131561075c57506107319060ff166108f1565b8015610748578106810390811161067957916106d1565b634e487b7160e01b5f52601260045260245ffd5b90505f9391930380805f0b03610679576107789060ff166108f1565b9280938181810204149015176106d157634e487b7160e01b5f52601160045260245ffd5b5f5b8381106107ad5750505f910152565b818101518382015260200161079e565b604091602082526107dd815180928160208601526020868601910161079c565b601f01601f1916010190565b9190604051906020938483019363a9059cbb60e01b85526001600160a01b03809316602485015260448401526044835261082283610625565b169160405190604082019067ffffffffffffffff9383831085841117610611577f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564875f9586956040528181520152519082865af13d156108e4573d918211610611576108ac926040519261089f86601f19601f8401160185610641565b83523d5f8685013e610902565b80519182159182156108c4575b50509050156102ce57565b8092508193810103126102ce57015180151581036102ce57805f806108b9565b6108ac9260609250610902565b60ff16604d811161067957600a0a90565b901561091c57815115610913575090565b3b156102ce5790565b5080519081156102ce57602001fdfea2646970667358221220c909e7dbe656d7c973709e34eb673837a89b1ae88012e9262063134290e6f21f64736f6c634300081800336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00a2646970667358221220d7009235facd2afacf7dbd784aa03c97dd6444be46f4a33aa486147317b81a7764736f6c634300081800330000000000000000000000003a73033c0b1407574c76bdbac67f126f6b4a9aa9000000000000000000000000904861a24f30ec96ea7cfc3be9ea4b476d237e9800000000000000000000000090e2487764e5316a2e4109c2ed40a3b3ad423659000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb0000000000000000000000000980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "nonce": "0x24c", + "chainId": "0x3e7" + }, + "additionalContracts": [ + { + "transactionType": "CREATE", + "contractName": "HyperCoreFlowExecutor", + "address": "0xf7f570ebac4f4a25ff06b8a49a156f32c2d30e6f", + "initCode": "0x60c0346200010457601f62005d3e38819003918201601f19168301916001600160401b038311848410176200010857808492604094855283398101031262000104576200005a602062000052836200011c565b92016200011c565b6001600160a01b0390911660805260a052604051615c0c90816200013282396080518181816108b7015281816109ff0152818161158b0152818161182f01528181612a1701528181614a0501528181614b7c01528181614f68015261504e015260a051818181610670015281816107de01528181611a7b01528181613a6e01528181614188015281816141e401528181614288015281816142b40152818161435301526146990152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001045756fe6080604052600436101562000012575f80fd5b5f803560e01c806246912e146200257057806301ffc9a71462002515578063037a06a414620022c957806304c73f6014620022a9578063057f037014620021f45780631f74a0b5146200217257806321081d3c1462002135578063248a9ca314620020e75780632e748b211462001f7f5780632f2ff15d1462001f1a578063319adf9f1462001b6657806336568abe1462001b1757806337710e201462001abd57806339fff0981462001a225780633b1c6a0114620017275780633cf3a02514620016eb5780634265fe861462001699578063490e662f146200165c5780634b3b029b146200161f578063502a82e21462001554578063521c98ba1462000d9157806369b97ac71462000d7157806379c7b60b1462000d1757806379c7f2891462000c1557806390a0827b1462000bd457806391d148541462000b6657806396cc2cfb14620008f9578063a217fddf14620008db578063a4b672b61462000895578063af5de6f91462000802578063c55dae6314620007bc578063ccbedaec1462000542578063d06e28ed1462000443578063d547741f14620003da578063e38b73a914620003bc578063ea0aaf241462000359578063eb84e7f2146200024b5763ff3eae0014620001e2575f80fd5b34620002485760c0366003190112620002485762000200366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10054156200023657620002339062004e31565b80f35b60405163cd6d8f7d60e01b8152600490fd5b80fd5b503462000248576020366003190112620002485760408160c09260a0835162000274816200270b565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051620002c8816200270b565b6001600160a01b039060028284541693848352600181015490602084019185811683526001600160401b0393849283604088019360a01c1683520154956060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b50346200024857602036600319011262000248576020620003b36200037d620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b50346200024857806003193601126200024857602060405160068152f35b50346200024857604036600319011262000248576200043f600435620003ff620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262000439600160408620015462002e64565b620039b1565b5080f35b503462000248576040366003190112620002485762000461620025de565b602435906fffffffffffffffffffffffffffffffff82168092036200053e576200048a62002e08565b6200049581620032e8565b90836001600160a01b039263ffffffff84608083015116915116813b156200053a57829160448392604051948593849263435354d360e01b845260048401528a60248401525af180156200052f5762000513575b5050167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b6200051e90620026f7565b6200052b57835f620004e9565b8380fd5b6040513d84823e3d90fd5b8280fd5b5f80fd5b50346200024857606036600319011262000248578062000561620025de565b6200056b6200260c565b906200057662002623565b906200058162002d37565b620005bc816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200071a574362000601826001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0380600162000648846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b01541693816001600160401b03938483166200071f575b5050508183166200066e578480f35b7f00000000000000000000000000000000000000000000000000000000000000001684525f8051602062005bb783398151915260205260046040852001541690823b156200071a5760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156200052f5762000702575b8080808480f35b6200070d90620026f7565b6200024857805f620006fb565b505050fd5b1686525f8051602062005bb783398151915260205282600460408820015416853b15620007b85760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152858160648183895af1908115620007ad57869162000791575b82906200065f565b6200079c90620026f7565b620007a957845f62000789565b8480fd5b6040513d88823e3d90fd5b8680fd5b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503462000248576020366003190112620002485760206001600160a01b03620008346200082e620025de565b620031ff565b6109ab60405162000848858301826200277c565b818152848101916200520c8339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152620008898162002744565b51902016604051908152f35b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346200024857806003193601126200024857602090604051908152f35b503462000248576040366003190112620002485762000917620025de565b906200092262002e08565b6200092d8262002f58565b6200093883620032e8565b926200094e60e083510151840b60243562004da2565b9460208401906001600160401b036200097381845116898360a08a01511691620038d2565b1562000b3657869750620009b7856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b620009c48582546200287c565b90556001600160a01b03808616807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051898152a2817f000000000000000000000000000000000000000000000000000000000000000016803b1562000b325760405163f3fef3a360e01b81526001600160a01b038316600482015260248101889052908a908290604490829084905af1801562000b275787918b9162000b08575b505062000a8290608060e0960192848451169062003353565b511692511694510151850b93813b1562000b0457604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156200052f5762000af15750f35b62000afc90620026f7565b620002485780f35b8580fd5b62000b15919250620026f7565b62000b235785895f62000a69565b8880fd5b6040513d8c823e3d90fd5b8980fd5b6040516377e88bc960e11b81526001600160a01b03861660048201526001600160401b0389166024820152604490fd5b50346200024857604036600319011262000248576001600160a01b03604062000b8e620025f5565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020522091165f52602052602060ff60405f2054166040519015158152f35b50346200024857604036600319011262000248576200023362000bf6620025de565b62000c0062002d37565b602435906001600160a01b0333911662003353565b503462000248576020366003190112620002485760a062000c7a62000c39620025de565b62000c4362002d0b565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b0360016040519262000c9384620026db565b805463ffffffff90818116865260ff8160201c1615156020870152818160281c16604087015260481c166060850152015416608082015262000d1560405180926001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565bf35b50346200024857602036600319011262000248576020620003b362000d3b620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b503462000248578060031936011262000248576020604051620f42408152f35b5034620002485760a0366003190112620002485762000daf620025de565b62000db96200263a565b9062000dc46200264e565b6001600160401b0360643516606435036200053e57608435906001600160401b03821682036200053e5762000df862002db1565b62000e0262002c05565b508480604051602081019063ffffffff881682526020815262000e258162002760565b519061080c5afa62000e3662002cd7565b901562001542578051810190602081830312620007b85760208101516001600160401b038111620015055761010081830184031262001505576040519262000e7e8462002727565b602082840101516001600160401b03811162000b325760208201603f828587010101121562000b32576020818486010101519062000ebc8262002cbb565b9162000ecc60405193846200277c565b8083526020840160408284888a01010101116200152a5762000efa9160406020850191878901010162002691565b8452604082840101516001600160401b03811162000b325760208201603f828587010101121562000b3257602081848601010151916001600160401b0383116200152e578260051b6040519362000f5560208301866200277c565b84526020808501920160408285888a01010101116200152a576040838688010101915b60408285888a01010101831062001509575050505060208401526101009062000fa683820160600162003428565b604085015262000fbb6080828501016200512c565b606085015262000fd060a0828501016200512c565b9283608086015262000fe760c08383010162005141565b60a086015262000ffc60e08383010162005141565b60c08601520101519081880b820362001505578160e08401526001600160a01b03808716911603620007b8576200103890870b60643562003829565b506040519162001048836200270b565b825263ffffffff86166020830152821515604083015260608201526001600160401b036064351660808201526001600160401b03831660a08201526001600160a01b03841686525f8051602062005bb783398151915260205260408620815180518051906001600160401b03821162001438578190620010c9855462002c80565b601f8111620014c5575b50602090601f831160011462001458578b926200144c575b50508160011b915f199060031b1c19161782555b60208101518051906001600160401b038211620014385768010000000000000000821162001438576020906001850154836001870155808410620013e5575b500190600184018a5260208a20908a5b8160021c81106200139d5750600319811680820362001334575b50505050936001600160a01b03937f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd82244353969360068460809860026001600160401b039701876040830151168154907bffffffffffffffffffffffffffffffffffffffff0000000000000000606085015160401b169163ffffffff60e01b16171790556003820190898b820151169082549174ff000000000000000000000000000000000000000060a083015160a01b16907fffffffffffffffffff000000000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff00000000000000000000000000000000000000000000169416171717179055620012c5600482018760208601511688198254161781556040850151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b6060830151600582015501908488820151166fffffffffffffffff000000000000000060a0845493015160401b16916fffffffffffffffffffffffffffffffff19161717905563ffffffff6040519816885215156020880152816064351660408801521660608601521692a280f35b928b938c5b81840381106200135d5750505060021c0155838360066001600160401b0362001168565b9091946020620013926001926001600160401b03895116908560031b60031b916001600160401b03809116831b921b19161790565b960192910162001339565b8b8c5b60048110620013b75750838201556001016200114e565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501620013a0565b6200141890600187018d52838d20600380870160021c820192601888831b16806200141f575b500160021c019062004d8a565b5f6200113e565b5f1990818601918254918a03851b1c1690555f6200140b565b634e487b7160e01b8a52604160045260248afd5b015190505f80620010eb565b9250848b5260208b20908b935b601f1984168510620014a9576001945083601f1981161062001490575b505050811b018255620010ff565b01515f1960f88460031b161c191690555f808062001482565b8181015183556020948501946001909301929091019062001465565b620014f390868d5260208d20601f850160051c81019160208610620014fa575b601f0160051c019062004d8a565b5f620010d3565b9091508190620014e5565b8780fd5b906020806040936200151b8662003428565b81520193019290915062000f78565b8b80fd5b634e487b7160e01b8b52604160045260248bfd5b604051639b0c335d60e01b8152600490fd5b503462000248576040366003190112620002485762001572620025de565b6024356200157f62002d37565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156200161b5760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156200052f5762001603575b50506200023391339062003353565b6200160e90620026f7565b6200053a57825f620015f4565b5080fd5b5034620002485780600319360112620002485760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b5034620002485780600319360112620002485760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b5034620002485760c03660031901126200024857620016b8366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576200023390620046c4565b50346200024857602036600319011262000248576200170962002d37565b80808080600435335af16200171d62002cd7565b5015620002485780f35b503462000248576060366003190112620002485762001745620025f5565b6001600160a01b0360443581811692918382036200053e576200176762002e08565b620017728262002f58565b906200177e8462003257565b62000b045760408201511562000b04576001600160401b0391600183608083015116019083821162001a0e57620017bd60e082510151890b8362003829565b5094620017db602083019386855116908760a08601511691620038d2565b1562000b23576200181c906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b620018298682546200287c565b905587837f000000000000000000000000000000000000000000000000000000000000000016803b156200161b5760405163f3fef3a360e01b81526001600160a01b038a166004820152602481018890529082908290604490829084905af180156200052f57620019f0575b50509183826200192494620018bc60e062001913986024975116925101518c0b8962004da2565b508481620019d0575b505050511695604051916020830191169687825260408301526001606083015260608252620018f48262002744565b6040519485926280000360e11b60208501525180928585019062002691565b81010360048101845201826200277c565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062001970906024830190620026b4565b03925af18015620019c557620019b3575b506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b620019be90620026f7565b5f62001981565b6040513d5f823e3d90fd5b620019df620019e79362002847565b168b62003353565b5f8084620018c5565b620019ff9094929394620026f7565b62001505579190875f62001895565b634e487b7160e01b5f52601160045260245ffd5b5034620002485760e0366003190112620002485762001a41366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576001600160a01b0380606083015116907f000000000000000000000000000000000000000000000000000000000000000016145f1462001aae576200023390620046c4565b620002339060c4359062003a45565b50346200024857602036600319011262000248576020620003b362001ae1620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b503462000248576040366003190112620002485762001b35620025f5565b336001600160a01b0382160362001b54576200043f90600435620039b1565b60405163334bd91960e11b8152600490fd5b50346200024857602036600319011262000248576001600160a01b0362001b8c620025de565b62001b9662002c45565b501681525f8051602062005bb783398151915260205260408120906040519162001bc0836200270b565b60405162001bce8162002727565b6040518254818562001be08362002c80565b808352926001811690811562001ef9575060011462001eb4575b62001c08925003826200277c565b815260405160018301805480835290855260208086209083019186915b81600384011062001e72579360069593819362001c73936001600160401b039997549181811062001e5e575b81811062001e47575b81811062001e30575b1062001e21575b5003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c850b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a0830152604051906020825282519060c0602084015262001d31825161010060e08601526101e0850190620026b4565b60208084015185830360df190161010087015280518084529282019492910190835b81811062001e015750505083946001600160401b039260e08385604060a0960151166101208901526001600160a01b036060820151166101408901526001600160a01b0360808201511661016089015260ff858201511661018089015260ff60c0820151166101a08901520151900b6101c086015282602082015116604086015260408101511515606086015260608101516080860152826080820151168286015201511660c08301520390f35b82516001600160401b031686526020958601959092019160010162001d53565b60c01c81526020015f62001c6a565b9260206001918c8560801c16815201930162001c63565b9260206001918c8560401c16815201930162001c5a565b9260206001918c8516815201930162001c51565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162001c25565b5090848652602086209086915b81831062001edc57505090602062001c089282010162001bfa565b602091935080600191548385880101520191019091839262001ec1565b6020925062001c0894915060ff191682840152151560051b82010162001bfa565b50346200024857604036600319011262000248576200043f60043562001f3f620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262001f79600160408620015462002e64565b62003912565b503462000248576080366003190112620002485762001f9d620025de565b62001fa76200260c565b9062001fb262002623565b90606435926fffffffffffffffffffffffffffffffff84168094036200053e5762001fdc62002e08565b62001fe782620032e8565b856001600160a01b03928360808401511692833b156200053a57620020646101048492836040519586948593636f0d192560e11b855260048501906001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565b6001600160401b038091169b8c60a485015216978860c48401528c60e48401525af180156200052f57620020cb575b5050907f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c92604092835195865260208601521692a380f35b620020d990939293620026f7565b62000b045790855f62002093565b50346200024857602036600319011262000248576001604060209260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008452200154604051908152f35b5034620002485780600319360112620002485760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503462000248576060366003190112620002485762002190620025de565b6001600160401b036024358181116200052b57620021b39036906004016200265e565b91909360443591821162000248576020620021ec868686620021d936600489016200265e565b939092620021e662002e08565b6200288a565b604051908152f35b50346200053e5760403660031901126200053e5762002212620025de565b906024356200222062002d37565b6001600160a01b03928360806200223783620032e8565b015116803b156200053e576040516390a0827b60e01b81526001600160a01b038316600482015260248101849052905f908290604490829084905af18015620019c55762002290575b5062000233929333911662003353565b620002339350620022a190620026f7565b5f9262002280565b346200053e575f3660031901126200053e5760206040516305f5e1008152f35b346200053e5760a03660031901126200053e57620022e6620025de565b620022f06200263a565b620022fa6200264e565b6064359063ffffffff938483168093036200053e57608435908582168092036200053e57620023298162002f58565b506200233462002db1565b6001600160a01b03918260016200237b846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b0154168015620024bc575b8360018960405199620023998b620026db565b16988981526020810197151597888152604082018a81526060830190878252856080850197169d8e8852620023fe8a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b9451166cffffffff00000000000000000068ffffffff000000000064ff0000000087549651151560201b16935160281b16935160481b16936cffffffffffffffffffffffffff19161717171781550191511673ffffffffffffffffffffffffffffffffffffffff19825416179055620024778762003257565b156200053e577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9946080946040519788526020880152604087015260608601521692a3005b50620024c882620031ff565b6040516109ab8082018281106001600160401b03821117620025015782916200520c833903905ff58062002386576040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b346200053e5760203660031901126200053e5760043563ffffffff60e01b81168091036200053e57602090637965db0b60e01b81149081156200255e575b506040519015158152f35b6301ffc9a760e01b1490508262002553565b346200053e5760403660031901126200053e57620025dc62002591620025de565b6001600160a01b03620025a36200260c565b91620025ae62002d37565b165f525f8051602062005bb78339815191526020526001600160401b03600460405f20015416339062002ea6565b005b600435906001600160a01b03821682036200053e57565b602435906001600160a01b03821682036200053e57565b602435906001600160401b03821682036200053e57565b604435906001600160401b03821682036200053e57565b6024359063ffffffff821682036200053e57565b6044359081151582036200053e57565b9181601f840112156200053e578235916001600160401b0383116200053e576020808501948460051b0101116200053e57565b5f5b838110620026a35750505f910152565b818101518382015260200162002693565b90602091620026cf8151809281855285808601910162002691565b601f01601f1916010190565b60a081019081106001600160401b038211176200250157604052565b6001600160401b0381116200250157604052565b60c081019081106001600160401b038211176200250157604052565b61010081019081106001600160401b038211176200250157604052565b608081019081106001600160401b038211176200250157604052565b604081019081106001600160401b038211176200250157604052565b90601f801991011681019081106001600160401b038211176200250157604052565b60c09060031901126200053e5760405190620027ba826200270b565b60043582526024356020830152816001600160a01b0360443581811681036200053e57604083015260643590811681036200053e576060820152608435608082015260a060a435910152565b9190811015620028175760051b0190565b634e487b7160e01b5f52603260045260245ffd5b9190916001600160401b038080941691160191821162001a0e57565b9073200000000000000000000000000000000000000091820180921162001a0e57565b906305f5e0ff820180921162001a0e57565b9190820180921162001a0e57565b919392935f945f948083036200053e57620028d5856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200053e57869796939291620028ef8662002f58565b926080620028fd88620032e8565b01906001600160a01b0392838351169460208701976200292a6001600160401b0397888b5116906200343d565b845f9b5b1062002b94575b50508c15925062002b8791505057436200297f886001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5582861662002993575b5050505050505050565b620029c195620029ab60e0865101515f0b8262003829565b85889992995116908660a08901511691620038d2565b1562002b4d575062002a03876001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62002a108782546200287c565b90558682167f00000000000000000000000000000000000000000000000000000000000000008316803b156200053e5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101899052905f908290604490829084905af18015620019c55762002b29575b509062002a9587849360e096958451169062003353565b511693511691510151850b93823b1562000b0457604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156200052f5762002b11575b80808080808062002989565b62002b1d8291620026f7565b62000248578062002b05565b60e094939291995062002b3c90620026f7565b5f98929350909162002a9562002a7e565b8451608001516040516377e88bc960e11b81529084166001600160a01b031660048201526001600160401b03919091166024820152604490fd5b505f985050505050505050565b62002ba6859e9f809c95968462002806565b359062002bb58c858862002806565b359189831683036200053e578a62002bd2938a8a51169262003520565b9490911562002bfb5762002beb8f93926001926200282b565b9b019e9d8f95949192956200292e565b9a9e9d5062002935565b6040519062002c148262002727565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b6040519062002c54826200270b565b5f60a08362002c6262002c05565b81528260208201528260408201528260608201528260808201520152565b90600182811c9216801562002cb0575b602083101462002c9c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162002c90565b6001600160401b0381116200250157601f01601f191660200190565b3d1562002d06573d9062002ceb8262002cbb565b9162002cfb60405193846200277c565b82523d5f602084013e565b606090565b6040519062002d1a82620026db565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a60205260409020547f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f9060ff161562002d935750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161562002dea57565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd60205260409020547f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef69060ff161562002d935750565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f2054161562002d935750565b620019139262002eec92602492604051926001600160a01b0360208501931683526001600160401b03809216604085015216606083015260608252620018f48262002744565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062002f38906024830190620026b4565b03925af18015620019c55762002f4b5750565b62002f5690620026f7565b565b9062002f6362002c45565b506001600160a01b038092165f5260205f8051602062005bb78339815191528152604092835f2084519062002f98826200270b565b85519562002fa68762002727565b80518254905f8162002fb88462002c80565b918282526001948a86821691825f14620031e05750506001146200319f575b62002fe5925003826200277c565b88528083019082519087829384928282549586815201915f52825f20945f5b8160038201106200315a57620030469654928583831062003140575b83831062003124575b83831062003107575b505010620030f8575b50905003826200277c565b858801526006600283015492856001600160401b0394858116858c0152841c1660608a015260ff60038201549760808b019a888a168c52828a60a01c1660a082015260c0810199838160a81c168b5260b01c5f0b60e0820152875260048301549086821690880152841c161515838601526005810154606086015201549082821660808501521c1660a082015293511615159081620030e9575b50156200053e57565b60ff9150511615155f620030e0565b60c01c81520188905f6200303b565b9091946001600160401b038560801c1681520193015f8562003032565b8192956001600160401b03868d1c168152019401908562003029565b8192956001600160401b0386168152019401908562003020565b86546001600160401b038082168652818b1c811696860196909652608081811c9096168a86015260c01c6060850152958201958795508c949093019260040162003004565b5050845f528188805f20855f915b858310620031c657505062002fe5935082010162002fd7565b8091929450548385880101520191018990858593620031ad565b60ff19168482015262002fe594151560051b840101915062002fd79050565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152606081018181106001600160401b03821117620025015760405251902090565b519081151582036200053e57565b5f80916040516001600160a01b036020820192168252602081526200327c8162002760565b51906108105afa6200328d62002cd7565b9015620032d6576020818051810103126200053e5760405190602082018281106001600160401b038211176200250157604052620032ce9060200162003249565b809152151590565b6040516313dd7ccd60e31b8152600490fd5b620032f79062000c4362002d0b565b906001600160a01b036001604051936200331185620026db565b805463ffffffff90818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156200053e57565b60405163a9059cbb60e01b602082019081526001600160a01b039384166024830152604480830195909552938152620033ea9390925f92839291906200339b6064876200277c565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051620033ce8162002760565b8181520152519082855af1620033e362002cd7565b91620051de565b805190811591821562003402575b5050156200053e57565b81925090602091810103126200053e57602062003420910162003249565b5f80620033f8565b51906001600160401b03821682036200053e57565b604080516001600160a01b0392909216602083019081526001600160401b039390931682820152808252915f918291906200347a6060826200277c565b51906108015afa6200348b62002cd7565b9015620034f5576060818051810103126200053e5781516001600160401b0392606082018481118382101762002501578152620034ee6060620034d16020860162003428565b94858552620034e284820162003428565b60208601520162003428565b9101521690565b8151639d2c8fcb60e01b8152600490fd5b6001600160401b03918216908216039190821162001a0e57565b949192909394805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20906001600160a01b039485835416958615620037ab57600284019788549860ff8a60481c166200379957600186015483608086510151168482160362003780576001600160401b039060a01c166001600160401b038b1660ff8c60401c165f146200371f575080926001600160401b03811682115f146200371657620035d79162003506565b995b620035e58b8d6200282b565b9b6001600160401b038d166001600160401b0385161162003704575069010000000000000000009069ff00000000000000000019161790556200362b8160019b62003506565b976001600160401b036020846200364b8d60e0895101515f0b9062003829565b50991695015116843b156200053e5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152925f908490606490829084905af1918215620019c5577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4393604093620036f2575b5060018187541696015416956001600160401b038351921682526020820152a4565b620036fd90620026f7565b5f620036d0565b5f9c508c9b5099505050505050505050565b50505f620035d7565b9092809b93806001600160401b038316105f1462003773576200374f91620037479162003506565b809c6200282b565b816001600160401b038216115f146200376b57505b91620035d9565b905062003764565b50506200374f5f62003747565b60405163358d72d160e01b815260048101879052602490fd5b60405163f7348a7960e01b8152600490fd5b604051631a40316d60e01b8152600490fd5b60ff16604d811162001a0e57600a0a90565b9190820391821162001a0e57565b8115620037e8570490565b634e487b7160e01b5f52601260045260245ffd5b90620f42409182810292818404149015171562001a0e57565b8181029291811591840414171562001a0e57565b9190805f0b80155f14620038475750506001600160401b0382169190565b5f81131562003877575062003863620038739160ff16620037bd565b6001600160401b03841662003815565b9190565b9050607f19811462001a0e5762003893905f0360ff16620037bd565b91620038aa836001600160401b038093166200287c565b5f19810190811162001a0e57620038c684620038ce92620037dd565b938462003815565b1690565b6200390a6200390392936001600160401b0393836001600160a01b03620038fc8780971662002847565b166200343d565b946200282b565b169116101590565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f205416155f14620039aa57825f5260205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f2054165f14620039aa57825f5260205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b9062003a5e6001600160a01b0360408401511662003257565b1562004618576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165f525f8051602062005bb783398151915260205260405f20906040519162003ab6836200270b565b60405162003ac48162002727565b6040518254815f62003ad68362002c80565b8083529260018116908115620045f75750600114620045b2575b62003afe925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b81600384011062004570579360069593819362003b67936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a08301526001600160a01b036060840151165f525f8051602062005bb783398151915260205260405f206040519062003c2e826200270b565b60405162003c3c8162002727565b6040518254815f62003c4e8362002c80565b80835292600181169081156200454f57506001146200450a575b62003c76925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b816003840110620044bd579360069593819362003cdf936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015262003d896001600160a01b03606086015116620032e8565b9184519062003dd662003dbb62003dac60a089015160808a01511515956200287c565b60e0885101515f0b9062004da2565b905060ff60c0885101511660ff60c087510151169162005165565b91156200448c5781925b5f8063ffffffff875116604051602081019182526020815262003e038162002760565b51906108085afa9062003e1562002cd7565b91156200447a576020828051810103126200053e576001600160401b0362003e44602062003e6d940162003428565b60208801519116919015620044665760ff60a062003e669251015116620037bd565b9062003815565b602085015115620044175763ffffffff62003e8e8160608801511662005026565b16620f4240818101811162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c01511691016200287c565b0416906080870151908115155f146200440e5750905b602085015115620043c5576305f5e1008110156200437e57505f5b62003f2462003f0b60a0890151620037fc565b62003f1d895160a08b0151906200287c565b906200287c565b5f1981019190821162001a0e5762003f1d62003f539262003f4c8a5160a08c0151906200287c565b90620037dd565b606482028281046064148315171562001a0e578111620042df57505062003f84855160e0865101515f0b9062004da2565b92909562003faf6001600160401b03602088015116856001600160401b0360a08a01511691620038d2565b156200423b5791608093917f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91860a0856001600160401b036001600160a01b036040819b990151168a606084015116908a8401511515916040519162004014836200270b565b82526020820152828816604082015282871660608201528a81019182528481015f815262004118602086019384515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b03602052856060600260405f206001600160a01b0388511673ffffffffffffffffffffffffffffffffffffffff19825416178155600181016001600160a01b0360208a0151168154907bffffffffffffffff000000000000000000000000000000000000000060408c015160a01b169163ffffffff60e01b161717905501950151168619855416178455511515839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151569ff00000000000000000082549160481b169069ff00000000000000000019161790555194818b60408501511697818d6060870151169a87875197015160405197885260208801521660408601521660608401521688820152a4015116620041ae83826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001662003353565b60e06001600160401b03602084015116925101515f0b90803b156200053e57604051639c45c34b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160401b03909316602484015260448301939093525f90810b6064830152909182908183816084810162002f38565b94505050905062002f56925060208201516001600160401b03604051921682527f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c60206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b9350949350505060208201516001600160a01b0360608401511691606481019081811162001a0e5760630190811162001a0e5760407fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e9162002f569660648351920482526020820152a36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166060820152620046c4565b6001600160401b036305f5e0ff1982011162001a0e57620043be620043b86001600160401b036305f5e100936305f5e0ff190116620037fc565b6200286a565b0462003ef8565b6305f5e100811115620043da57505f62003ef8565b6001600160401b03816305f5e100031162001a0e57620043be620043b86001600160401b036305f5e10093840316620037fc565b90509062003edd565b63ffffffff6200442d8160608801511662005026565b16620f4240808281031162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c0151169103620037cf565b5062003e6660ff60a08951015116620037bd565b604051635cffc5fb60e11b8152600490fd5b6127108181031162001a0e576001600160401b03612710620044b383820383861662003815565b0416919262003de0565b926001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019162003c95565b5090845f5260205f20905f915b8183106200453257505090602062003c769282010162003c68565b602091935080600191548385880101520191019091839262004517565b6020925062003c7694915060ff191682840152151560051b82010162003c68565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162003b1d565b5090845f5260205f20905f915b818310620045da57505090602062003afe9282010162003af0565b6020919350806001915483858801015201910190918392620045bf565b6020925062003afe94915060ff191682840152151560051b82010162003af0565b506080810151909190156200464c5760246001600160a01b03604084015116604051906320a2097d60e11b82526004820152fd5b9080602062002f569201517f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b03604085015116604051908152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b6060906060810151905f6001600160a01b0380931692835f526020945f8051602062005bb78339815191528652604091825f2096835162004705816200270b565b845193620047138562002727565b85518a5491905f81620047268562002c80565b918282526001958887821691825f1462004d6b57505060011462004d2a575b62004756925094939403826200277c565b8652808b019184885193898586948484549283815201935f52845f20965f915b83600384011062004cde575085620047b998549484841062004cc4575b84841062004ca7575b5083831062004c8a575b50501062004c7b5750905003826200277c565b82850152600289015495836001600160401b039788811688880152871c16606086015260038a0154946080958581168783015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528252600660048b01549a60ff8585019c8d8b82169052891c1615158885015260058101546060850152015490878216868401528760a0840192881c168252868901976200485e868a511662003257565b1562004c0d57899a9b5f9a98999a508c612710620048958b519b8b6200488b60a083019e8f51906200287c565b9101519062003815565b04988a5199808b1162004c04575b508d8a8062004b2e575b620048c2620048d191620048e493516200287c565b60e08a5101515f0b9062004da2565b91909782878088511692511691620038d2565b1562004ae65750908c9695949392918a620049f9575b509260e0896200498f94620049c59b9997947fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b99976200496e906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6200497b8d82546200287c565b9055511696511694510151900b9062004da2565b9190928d84620049ca575b5050505050880151965116965192519351938493846040919493926060820195825260208201520152565b0390a4565b620049ee94620049e89188620049e08562002847565b169062003353565b62002ea6565b5f8080808d6200499a565b919293949596505087807f00000000000000000000000000000000000000000000000000000000000000001691875101511690803b156200053e578b5163f3fef3a360e01b81526001600160a01b03929092166004830152602482018a90525f908290604490829084905af1801562004adc578c948f91998098956200498f957fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b9995620049c59d62004ac0575b509497995094509950939597999a9b620048fa565b60e0945062004acf90620026f7565b6200496e5f945062004aab565b8b513d5f823e3d90fd5b9a50505050955050505093957f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c955084915062004b238162004e31565b0151945191168152a3565b62004b45908a8301518c858c51015116906200503f565b1562004b53575b8a620048ad565b509192939495968881999a50602492508389510151168c51928380926370a0823160e01b82528d7f00000000000000000000000000000000000000000000000000000000000000001660048301525afa90811562004adc57918e918e9a999897969594935f9162004bc7575b509962004b4c565b92505098508681813d831162004bfc575b62004be481836200277c565b810103126200053e57518c988e91620048d162004bbf565b503d62004bd8565b99505f620048a3565b505050509296509450839293015115155f1462004c405750505191516320a2097d60e11b81529116600482015260249150fd5b62002f56948294827f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d9401519451169051908152a262004e31565b60c01c81520185905f6200303b565b9091946001600160401b038560801c1681520193015f85620047a6565b956001600160401b03868495981c16815201940190858e6200479c565b6001600160401b0386168752958101959282019262004793565b95509360049296509284919388546001600160401b039283821683528d8483831c169084015260809382851c169083015260c01c8782015201960191019386948c9296938a9562004776565b50508c5f528186805f20865f915b85831062004d5157505062004756935082010162004745565b809192945054838588010152019101879086859362004d38565b60ff1916848201526200475694151560051b8401019150620047459050565b81811062004d96575050565b5f815560010162004d8a565b9190805f0b9081155f1462004dc05750506001600160401b03821690565b5f82131562004e035762004dd8915060ff16620037bd565b918215620037e857620038ce62004dfb82856001600160401b03940690620037cf565b9384620037dd565b505f0380805f0b0362001a0e57620038ce62004e2a6001600160401b039260ff16620037bd565b8462003815565b805161271062004e5862004e4c60a08501938451906200287c565b60808501519062003815565b0481518181115f146200501d5750905b8190602084019182519162004e8f60608701956001600160a01b039485885116906200503f565b1562005014575b8162004f61575b620049c5837f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9462004ed1858a516200287c565b9662004eeb828a51169860408c0199848b51169062003353565b62004f29828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62004f368782546200287c565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b84518316927f00000000000000000000000000000000000000000000000000000000000000008116803b156200053e5760405163f3fef3a360e01b81526001600160a01b03959095166004860152602485018490525f908590604490829084905af1908115620019c5577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f94620049c59262005002575b5093505062004e9d565b6200500d90620026f7565b5f62004ff8565b5f915062004e96565b90509062004e68565b90606463ffffffff8093160291821691820362001a0e57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa918215620019c5575f92620050f2575b5080821094851595620050b2575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080620050ac565b9091506020813d60201162005123575b8162005111602093836200277c565b810103126200053e5751905f6200509e565b3d915062005102565b51906001600160a01b03821682036200053e57565b519060ff821682036200053e57565b9060ff8091169116039060ff821162001a0e57565b919060ff821660ff82168181145f1462005180575050505090565b929391921015620051b857620038ce9192620051b0620051aa6001600160401b0395869362005150565b620037bd565b911662003815565b620038ce91620051d6620051aa6001600160401b0395869362005150565b9116620037dd565b9015620051fb57815115620051f1575090565b3b156200053e5790565b5080519081156200053e57602001fdfe60a080604052346100455733608052610961908161004a8239608051818181608b015281816101ac0152818161038e015281816104a20152818161051b015261069c0152f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f915f3560e01c908163435354d3146104d757816390a0827b1461047b5781639c45c34b14610469578163a703334f1461033c578163de1a324a146100d457508063e94b77c1146100b35763eba61c0e1461006d575f80fd5b346100af57816003193601126100af57602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b82346100d1576100ce6100c5366105b5565b9291909161068d565b80f35b80fd5b91905034610338576101009136600319018381126103345760a01361033057815167ffffffffffffffff939060a081018581118282101761031d5784526101196105a2565b91828252602435918215158303610319576020810192835263ffffffff9360443585811681036102ce578783015260643585811681036102ce5760608301526084356001600160a01b03928382168203610315576080015260a4358881168091036102ce5760c435918983168093036102ce5760e435936fffffffffffffffffffffffffffffffff85168095036102ce577f000000000000000000000000000000000000000000000000000000000000000016330361031557866101dd9116610663565b9451151581156103055782156102f5578a6102e557885196602088019616865288870152606086015260808501525f60a0850152600260c085015260e084015260e08352820194828610908611176102d25761026891602491868652630100000160e01b6101208301526102598251809261012485019061079c565b81010383810186520184610641565b73333333333333333333333333333333333333333392833b156102ce576102a5935f928385518097819582946317938e1360e01b845283016107bd565b03925af19081156102c557506102b9575080f35b6102c391506105fd565b005b513d5f823e3d90fd5b5f80fd5b604183634e487b7160e01b5f525260245ffd5b88516376d4929560e11b81528890fd5b88516313c0a8df60e01b81528890fd5b885163017461b760e71b81528890fd5b8a80fd5b8780fd5b604184634e487b7160e01b5f525260245ffd5b8380fd5b8480fd5b8280fd5b919050346103385760603660031901126103385781359067ffffffffffffffff928383168093036102ce576024356001600160a01b03948582168092036102ce576044359081168091036102ce5786957f000000000000000000000000000000000000000000000000000000000000000016330361046557610259946103fc9260249286519260208401928352878401526060830152606082526103df82610625565b85519687926280000360e11b60208501525180928585019061079c565b73333333333333333333333333333333333333333392833b15610334576104399385928385518097819582946317938e1360e01b845283016107bd565b03925af190811561045c575061044c5750f35b610455906105fd565b6100d15780f35b513d84823e3d90fd5b8580fd5b83346100d1576100ce6100c5366105b5565b91905034610338573660031901126100af57356001600160a01b038082168092036102ce577f00000000000000000000000000000000000000000000000000000000000000001633036100af576100ce906024359033906107e9565b919050346102ce57806003193601126102ce576104f26105a2565b91602435926fffffffffffffffffffffffffffffffff84168094036102ce576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102ce5761054a90610663565b9282519063ffffffff602083019516855283820152828152606081019381851067ffffffffffffffff8611176102d25761026891602491868652630100000b60e01b608083015261025982518092608485019061079c565b6004359063ffffffff821682036102ce57565b60809060031901126102ce576004356001600160a01b03811681036102ce579060243567ffffffffffffffff811681036102ce579060443590606435805f0b81036102ce5790565b67ffffffffffffffff811161061157604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761061157604052565b90601f8019910116810190811067ffffffffffffffff82111761061157604052565b9061271063ffffffff8093160191821161067957565b634e487b7160e01b5f52601160045260245ffd5b9092916001600160a01b0392837f00000000000000000000000000000000000000000000000000000000000000001633036102ce57805f0b80155f1461071c575050915b826106dd575b50505050565b67ffffffffffffffff732000000000000000000000000000000000000000941684018094116106795780610713941691166107e9565b5f8080806106d7565b5f81131561075c57506107319060ff166108f1565b8015610748578106810390811161067957916106d1565b634e487b7160e01b5f52601260045260245ffd5b90505f9391930380805f0b03610679576107789060ff166108f1565b9280938181810204149015176106d157634e487b7160e01b5f52601160045260245ffd5b5f5b8381106107ad5750505f910152565b818101518382015260200161079e565b604091602082526107dd815180928160208601526020868601910161079c565b601f01601f1916010190565b9190604051906020938483019363a9059cbb60e01b85526001600160a01b03809316602485015260448401526044835261082283610625565b169160405190604082019067ffffffffffffffff9383831085841117610611577f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564875f9586956040528181520152519082865af13d156108e4573d918211610611576108ac926040519261089f86601f19601f8401160185610641565b83523d5f8685013e610902565b80519182159182156108c4575b50509050156102ce57565b8092508193810103126102ce57015180151581036102ce57805f806108b9565b6108ac9260609250610902565b60ff16604d811161067957600a0a90565b901561091c57815115610913575090565b3b156102ce5790565b5080519081156102ce57602001fdfea2646970667358221220c909e7dbe656d7c973709e34eb673837a89b1ae88012e9262063134290e6f21f64736f6c634300081800336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00a2646970667358221220d7009235facd2afacf7dbd784aa03c97dd6444be46f4a33aa486147317b81a7764736f6c6343000818003300000000000000000000000090e2487764e5316a2e4109c2ed40a3b3ad423659000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb" + } + ], + "isFixedGasLimit": false + }, + { + "hash": "0xd74dec165080b5fbff6ccc35308056868c6bfe895b036c17ffe54a6efb5cdcc1", + "transactionType": "CALL", + "contractName": "DonationBox", + "contractAddress": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "function": "transferOwnership(address)", + "arguments": ["0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "gas": "0x9922", + "value": "0x0", + "input": "0xf2fde38b00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "nonce": "0x24d", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xafb3a40d29cb3ed22d77e0f87387aecc0cbef59680adee4e3439a7c6a9ef2751", + "transactionType": "CALL", + "contractName": "PermissionedMulticallHandler", + "contractAddress": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "function": "grantRole(bytes32,address)", + "arguments": [ + "0x69048ea73402a715065a3029b4059a4e97d1461c95fa4fabca1084b5f34f4abe", + "0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "gas": "0x11383", + "value": "0x0", + "input": "0x2f2ff15d69048ea73402a715065a3029b4059a4e97d1461c95fa4fabca1084b5f34f4abe00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "nonce": "0x24e", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xfbc1184185961f34d5eb882a59fd93483395a71af48f34cb177453d38e11a70b", + "transactionType": "CALL", + "contractName": "DstOFTHandler", + "contractAddress": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "function": "setAuthorizedPeriphery(uint32,bytes32)", + "arguments": ["30101", "0x0000000000000000000000004607bceaf7b22cb0c46882ffc9fab3c6efe66e5a"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "gas": "0x10e6b", + "value": "0x0", + "input": "0x52e12a1400000000000000000000000000000000000000000000000000000000000075950000000000000000000000004607bceaf7b22cb0c46882ffc9fab3c6efe66e5a", + "nonce": "0x24f", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x8ffe6c78349baca66e5318acb553afe390cf87a9dca508aad5399946496939ab", + "transactionType": "CALL", + "contractName": "DstOFTHandler", + "contractAddress": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "function": "setAuthorizedPeriphery(uint32,bytes32)", + "arguments": ["30110", "0x0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "gas": "0x10e6b", + "value": "0x0", + "input": "0x52e12a14000000000000000000000000000000000000000000000000000000000000759e0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940", + "nonce": "0x250", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x39da01", + "logs": [ + { + "address": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xf72c3e798991af0e7123197bfd7da40585f936b4353b91159b749008ceac541a", + "transactionIndex": "0x2", + "logIndex": "0x2f", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000081000000000000000000000000000000000000020000000000000000000800000000000000000000000000040000480000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xf72c3e798991af0e7123197bfd7da40585f936b4353b91159b749008ceac541a", + "transactionIndex": "0x2", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0x3f377", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x4a8f16", + "logs": [ + { + "address": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xee446c5d024fc2d807a387b7be9dc0e17f7f806efac526cc1db7bebd7fa5f7c7", + "transactionIndex": "0x3", + "logIndex": "0x30", + "removed": false + } + ], + "logsBloom": "0x00000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000004000000000000020000000000000000000800000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001002000000000000000000000000800000000000000000000000000000000100000000000020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xee446c5d024fc2d807a387b7be9dc0e17f7f806efac526cc1db7bebd7fa5f7c7", + "transactionIndex": "0x3", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0x10b515", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xb2585e", + "logs": [ + { + "address": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "topics": [ + "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", + "0x5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef6", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7", + "transactionIndex": "0x4", + "logIndex": "0x31", + "removed": false + }, + { + "address": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "topics": [ + "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", + "0x880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7", + "transactionIndex": "0x4", + "logIndex": "0x32", + "removed": false + }, + { + "address": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7", + "transactionIndex": "0x4", + "logIndex": "0x33", + "removed": false + } + ], + "logsBloom": "0x00000004000000000800000000000000080080000000000000000080000000000100000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000002000000000000020000400000000000000800000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000800000000000000000000000000000000100000000000020100001000000000800000000000000000100000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7", + "transactionIndex": "0x4", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0x67c948", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xb2c73d", + "logs": [ + { + "address": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "0x00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec67461" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xd74dec165080b5fbff6ccc35308056868c6bfe895b036c17ffe54a6efb5cdcc1", + "transactionIndex": "0x5", + "logIndex": "0x34", + "removed": false + } + ], + "logsBloom": "0x04000000000000000000000000000000000000000000000000800000800001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000081000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000480000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xd74dec165080b5fbff6ccc35308056868c6bfe895b036c17ffe54a6efb5cdcc1", + "transactionIndex": "0x5", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0x6edf", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xb38eb5", + "logs": [ + { + "address": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "topics": [ + "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", + "0x69048ea73402a715065a3029b4059a4e97d1461c95fa4fabca1084b5f34f4abe", + "0x00000000000000000000000040153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xafb3a40d29cb3ed22d77e0f87387aecc0cbef59680adee4e3439a7c6a9ef2751", + "transactionIndex": "0x6", + "logIndex": "0x35", + "removed": false + } + ], + "logsBloom": "0x04000004000000000000000000000000000000000008000000000000800001000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000800000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001002000000000000000000000000800000000000000000000000000000000100200000000000000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xafb3a40d29cb3ed22d77e0f87387aecc0cbef59680adee4e3439a7c6a9ef2751", + "transactionIndex": "0x6", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0xc778", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xb4527d", + "logs": [ + { + "address": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "topics": ["0xf3adc8db618887d7b76838e244efb05fc99475bb5a904a914d939fbdc41b7e8d"], + "data": "0x00000000000000000000000000000000000000000000000000000000000075950000000000000000000000004607bceaf7b22cb0c46882ffc9fab3c6efe66e5a", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0xfbc1184185961f34d5eb882a59fd93483395a71af48f34cb177453d38e11a70b", + "transactionIndex": "0x7", + "logIndex": "0x36", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000800000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000800000000000000400000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xfbc1184185961f34d5eb882a59fd93483395a71af48f34cb177453d38e11a70b", + "transactionIndex": "0x7", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0xc3c8", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0xb51645", + "logs": [ + { + "address": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "topics": ["0xf3adc8db618887d7b76838e244efb05fc99475bb5a904a914d939fbdc41b7e8d"], + "data": "0x000000000000000000000000000000000000000000000000000000000000759e0000000000000000000000002ac5ee3796e027da274fbde84c82173a65868940", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "blockTimestamp": "0x692f9ce8", + "transactionHash": "0x8ffe6c78349baca66e5318acb553afe390cf87a9dca508aad5399946496939ab", + "transactionIndex": "0x8", + "logIndex": "0x37", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000800000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000800000000000000400000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x8ffe6c78349baca66e5318acb553afe390cf87a9dca508aad5399946496939ab", + "transactionIndex": "0x8", + "blockHash": "0xba7536f79ed3b0766763cc1f0a6422fd88383b2ac58e0237f98f0c5cc80c2401", + "blockNumber": "0x13d9f96", + "gasUsed": "0xc3c8", + "effectiveGasPrice": "0x10437e16", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764728130595, + "chain": 999, + "commit": "5d9f89e2" +} diff --git a/broadcast/DeployHyperliquidDepositHandler.s.sol/999/run-latest.json b/broadcast/DeployHyperliquidDepositHandler.s.sol/999/run-latest.json new file mode 100644 index 000000000..881273a0d --- /dev/null +++ b/broadcast/DeployHyperliquidDepositHandler.s.sol/999/run-latest.json @@ -0,0 +1,173 @@ +{ + "transactions": [ + { + "hash": "0x187b45f39be413aff2ff526946c64f76ed98763129e7e2ffb7d2d4c5bd997519", + "transactionType": "CREATE", + "contractName": "HyperliquidDepositHandler", + "contractAddress": "0x861e127036b28d32f3777b4676f6bbb9e007d195", + "function": null, + "arguments": null, + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x160360", + "value": "0x0", + "input": "0x60a080604052346100e15760015f5533156100cc5760018054336001600160a01b03198216811790925560405191906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a36103a38181016001600160401b038111838210176100b8578291610ed2833903905ff080156100ad57608052604051610dec90816100e682396080518181816101b70152818161026a01526108080152f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b631e4fbdf760e01b81525f6004820152602490fd5b5f80fdfe6040608081526004361015610012575f80fd5b5f905f3560e01c80633a5be8cb146105275780633eda20c61461045757806368c4ac26146103f2578063715018a61461038c5780637826a16e146102fa5780637bce887a146102385780638da5cb5b146102115780639b61d2d8146101db578063a4b672b614610198578063c48919d61461012c5763f2fde38b14610095575f80fd5b34610128576020366003190112610128576100ae6105df565b6100b66109f6565b6001600160a01b0380911691821561011157506001548273ffffffffffffffffffffffffffffffffffffffff19821617600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b51631e4fbdf760e01b815260048101849052602490fd5b5080fd5b8234610195576060366003190112610195576101466105df565b61014e610695565b6101566105f5565b61015e6109f6565b60025f54146101915767ffffffffffffffff602061018261018a9560025f55610a65565b015116610b1d565b6001815580f35b5f80fd5b80fd5b5034610128578160031936011261012857602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b8234610195576101ea366106ac565b6101f26109f6565b60025f5414610191576001600160a01b0361018a9360025f5516610a22565b50346101285781600319360112610128576020906001600160a01b03600154169051908152f35b50903461019157610248366106ac565b90916102526109f6565b60025f54146101915760025f556001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016911694813b1561019157805163f3fef3a360e01b81526001600160a01b038716600482015260248101859052915f908390604490829084905af19081156102f157506102dc575b5061018a9293610a22565b61018a93506102ea90610627565b5f926102d1565b513d5f823e3d90fd5b503461019157610309366106ac565b9160025f54146101915760025f5583516323b872dd60e01b60208201523360248201523060448201526064808201849052815260a08101949067ffffffffffffffff861181871017610378576103729561036d92526001600160a01b038316610c49565b610716565b60015f55005b634e487b7160e01b5f52604160045260245ffd5b34610191575f366003190112610191576103a46109f6565b5f6001600160a01b0360015473ffffffffffffffffffffffffffffffffffffffff198116600155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b5034610191576020366003190112610191576080906001600160a01b0390816104196105df565b165f526002602052805f2080549167ffffffffffffffff600260018401549301545f0b938251958116865260a01c1660208501528301526060820152f35b5034610191576080366003190112610191576104716105df565b610479610695565b9060643592835f0b809403610191576002926104936109f6565b81519261049f8461060b565b6001600160a01b038091169283855267ffffffffffffffff6020860193168352808501936044358552606086019788525f52856020525f209351167bffffffffffffffff00000000000000000000000000000000000000008454925160a01b169163ffffffff60e01b161717825551600182015501905160ff198254169060ff161790555f80f35b5034610191576080366003190112610191576105416105df565b9061054a6105f5565b506064359067ffffffffffffffff8211610191573660238301121561019157816004013561058361057a82610679565b92519283610657565b8082526020820192366024838301011161019157815f9260246020930186378301015260025f54146101915760209060025f5580518101031261019157516001600160a01b038116809103610191576103729160243590610716565b600435906001600160a01b038216820361019157565b604435906001600160a01b038216820361019157565b6080810190811067ffffffffffffffff82111761037857604052565b67ffffffffffffffff811161037857604052565b6040810190811067ffffffffffffffff82111761037857604052565b90601f8019910116810190811067ffffffffffffffff82111761037857604052565b67ffffffffffffffff811161037857601f01601f191660200190565b6024359067ffffffffffffffff8216820361019157565b6060906003190112610191576001600160a01b03600435818116810361019157916024359160443590811681036101915790565b9073200000000000000000000000000000000000000091820180921161070257565b634e487b7160e01b5f52601160045260245ffd5b9161072083610a65565b67ffffffffffffffff926020908482840151169260608101515f0b926040805192828401985f806001600160a01b039b8c8c16978882528781526107638161063b565b51906108105afa610772610c0d565b90156109e557848180518101031261019157835191858301908111838210176103785784526107a2908501610c3c565b809152156107f3575b50505050906107b991610cef565b9390806107c9575b505050505050565b6107e895826107e3936107db866106e0565b169116610a22565b610b1d565b5f80808080806107c1565b810151916001830180931161070257898916937f00000000000000000000000000000000000000000000000000000000000000008a16803b1561019157835163f3fef3a360e01b81526001600160a01b038716600482015260248101869052905f908290604490829084905af180156109db576109cc575b506108768785610cef565b50806109b1575b50825191808301918252888484015260016060840152606083526108a08361060b565b6108d56024855180946108c5858301976280000360e11b895251809285850190610afc565b8101036004810185520183610657565b733333333333333333333333333333333333333333803b15610191575f92836044610924948851978896879586936317938e1360e01b8552600485015251809281602486015285850190610afc565b601f01601f191681010301925af180156109a757927f45b9d2d602535b50313ef0fa849df42dd31d8610fc42876e005a5b6806d3e8809261098b926107b998979695610998575b50516001600160a01b038a16815260208101919091529081906040820190565b0390a290915f80806107ab565b6109a190610627565b5f61096b565b82513d5f823e3d90fd5b6109c6908b6109bf8b6106e0565b1687610a22565b5f61087d565b6109d590610627565b5f61086b565b84513d5f823e3d90fd5b83516313dd7ccd60e31b8152600490fd5b6001600160a01b03600154163303610a0a57565b60405163118cdaa760e01b8152336004820152602490fd5b60405163a9059cbb60e01b60208201526001600160a01b03929092166024830152604480830193909352918152610a6391610a5e606483610657565b610c49565b565b60405f60608251610a758161060b565b828152826020820152828482015201526001600160a01b03809216805f52600260205282825f20541615610aeb57906002915f5281602052805f209067ffffffffffffffff815194610ac68661060b565b8354908116865260a01c16602085015260018201549084015201545f0b606082015290565b8151633dd1b30560e01b8152600490fd5b5f5b838110610b0d5750505f910152565b8181015183820152602001610afe565b604051926001600160a01b03602085019316835267ffffffffffffffff809216604085015216606083015260608252610b558261060b565b610b8c60246040518093610b7c60208301966280000360e11b885251809285850190610afc565b8101036004810184520182610657565b73333333333333333333333333333333333333333390813b15610191575f91610bde918360446040518097819682956317938e1360e01b84526020600485015251809281602486015285850190610afc565b601f01601f191681010301925af18015610c0257610bf95750565b610a6390610627565b6040513d5f823e3d90fd5b3d15610c37573d90610c1e82610679565b91610c2c6040519384610657565b82523d5f602084013e565b606090565b5190811515820361019157565b905f806001600160a01b03610ca69416927f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051610c898161063b565b818152015260208151910182855af1610ca0610c0d565b91610d8d565b8051908115918215610cbc575b50501561019157565b8192509060209181010312610191576020610cd79101610c3c565b5f80610cb3565b60ff16604d811161070257600a0a90565b9190805f0b9081155f14610d0d57505067ffffffffffffffff821690565b5f821315610d5757610d22915060ff16610cde565b8015610d4357808306830392831161070257820467ffffffffffffffff1690565b634e487b7160e01b5f52601260045260245ffd5b505f0380805f0b0361070257610d6f9060ff16610cde565b828181029181830414901517156107025767ffffffffffffffff1690565b9015610da757815115610d9e575090565b3b156101915790565b50805190811561019157602001fdfea2646970667358221220c0cba36db139425c71e4049e05ab727a287009647e4f3ca742370867c5d7d0ff64736f6c634300081800336080806040523461005a575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610344908161005f8239f35b5f80fdfe608060409080825260049081361015610016575f80fd5b5f3560e01c908163715018a61461026e5781638da5cb5b1461024c57508063f2fde38b146101d45763f3fef3a31461004c575f80fd5b346101545781600319360112610154578035916001600160a01b0383168093036101545760249261007b6102d2565b8151916020830163a9059cbb60e01b815233868501528535604485015260448452608084019367ffffffffffffffff94818110868211176101c25760c08201818110878211176101b0578452602090527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152515f9182919082865af1923d1561019f573d9181831161018d57805195601f8401601f19908116603f011687019283118784101761017b575052835261013e93503d5f602085013e6102e5565b8051908115918215610158575b50501561015457005b5f80fd5b819250906020918101031261015457602001518015158103610154575f8061014b565b60418891634e487b7160e01b5f52525ffd5b86604187634e487b7160e01b5f52525ffd5b5050915061013e92506060916102e5565b88604189634e487b7160e01b5f52525ffd5b87604188634e487b7160e01b5f52525ffd5b503461015457602036600319011261015457356001600160a01b03808216809203610154576102016102d2565b8115610154575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b34610154575f366003190112610154576020906001600160a01b035f54168152f35b34610154575f366003190112610154576102866102d2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361015457565b90156102ff578151156102f6575090565b3b156101545790565b50805190811561015457602001fdfea2646970667358221220c55691de465342d56d68c31160f5d7d661422ef39ebccaa5223872c823ad6d6964736f6c63430008180033", + "nonce": "0x206", + "chainId": "0x3e7" + }, + "additionalContracts": [ + { + "transactionType": "CREATE", + "address": "0x59ee1342867c200fa8ac052faa5f3df8eef21a67", + "initCode": "0x6080806040523461005a575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610344908161005f8239f35b5f80fdfe608060409080825260049081361015610016575f80fd5b5f3560e01c908163715018a61461026e5781638da5cb5b1461024c57508063f2fde38b146101d45763f3fef3a31461004c575f80fd5b346101545781600319360112610154578035916001600160a01b0383168093036101545760249261007b6102d2565b8151916020830163a9059cbb60e01b815233868501528535604485015260448452608084019367ffffffffffffffff94818110868211176101c25760c08201818110878211176101b0578452602090527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152515f9182919082865af1923d1561019f573d9181831161018d57805195601f8401601f19908116603f011687019283118784101761017b575052835261013e93503d5f602085013e6102e5565b8051908115918215610158575b50501561015457005b5f80fd5b819250906020918101031261015457602001518015158103610154575f8061014b565b60418891634e487b7160e01b5f52525ffd5b86604187634e487b7160e01b5f52525ffd5b5050915061013e92506060916102e5565b88604189634e487b7160e01b5f52525ffd5b87604188634e487b7160e01b5f52525ffd5b503461015457602036600319011261015457356001600160a01b03808216809203610154576102016102d2565b8115610154575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b34610154575f366003190112610154576020906001600160a01b035f54168152f35b34610154575f366003190112610154576102866102d2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361015457565b90156102ff578151156102f6575090565b3b156101545790565b50805190811561015457602001fdfea2646970667358221220c55691de465342d56d68c31160f5d7d661422ef39ebccaa5223872c823ad6d6964736f6c63430008180033" + } + ], + "isFixedGasLimit": false + }, + { + "hash": "0xafaa7c3ef97727b20d46e5cb7f6478ab2e3597d7426aa0b007e52e016aa0cb8f", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x3333333333333333333333333333333333333333", + "function": "sendRawAction(bytes)", + "arguments": [ + "0x01000006000000000000000000000000861e127036b28d32f3777b4676f6bbb9e007d19500000000000000000000000000000000000000000000000000000000000001680000000000000000000000000000000000000000000000000000000000000001" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x3333333333333333333333333333333333333333", + "gas": "0x1039d", + "value": "0x0", + "input": "0x17938e130000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006401000006000000000000000000000000861e127036b28d32f3777b4676f6bbb9e007d1950000000000000000000000000000000000000000000000000000000000000168000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000", + "nonce": "0x207", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xea620a26dd14c70920861794ebb4ad4ed9e20eaa0a9e4a8dd076bc5e6e978c12", + "transactionType": "CALL", + "contractName": "HyperliquidDepositHandler", + "contractAddress": "0x861e127036b28d32f3777b4676f6bbb9e007d195", + "function": "addSupportedToken(address,uint64,uint256,int8)", + "arguments": ["0x111111a1a0667d36bD57c0A9f569b98057111111", "360", "1000000", "-2"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x861e127036b28d32f3777b4676f6bbb9e007d195", + "gas": "0x1ed4c", + "value": "0x0", + "input": "0x3eda20c6000000000000000000000000111111a1a0667d36bd57c0a9f569b98057111111000000000000000000000000000000000000000000000000000000000000016800000000000000000000000000000000000000000000000000000000000f4240fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + "nonce": "0x208", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x10eee8", + "logs": [ + { + "address": "0x861e127036b28d32f3777b4676f6bbb9e007d195", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0x0a8f06eb397fdab2e4392e16237ee54d6f6fe244e7ae4c021885998578c4cfe0", + "blockNumber": "0x135c76f", + "blockTimestamp": "0x6927e55c", + "transactionHash": "0x187b45f39be413aff2ff526946c64f76ed98763129e7e2ffb7d2d4c5bd997519", + "transactionIndex": "0x0", + "logIndex": "0x0", + "removed": false + }, + { + "address": "0x59ee1342867c200fa8ac052faa5f3df8eef21a67", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000861e127036b28d32f3777b4676f6bbb9e007d195" + ], + "data": "0x", + "blockHash": "0x0a8f06eb397fdab2e4392e16237ee54d6f6fe244e7ae4c021885998578c4cfe0", + "blockNumber": "0x135c76f", + "blockTimestamp": "0x6927e55c", + "transactionHash": "0x187b45f39be413aff2ff526946c64f76ed98763129e7e2ffb7d2d4c5bd997519", + "transactionIndex": "0x0", + "logIndex": "0x1", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000020000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000040000002000000000080020000000020000000000800000000000000000000000000040000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000080000000100008000000000000020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x187b45f39be413aff2ff526946c64f76ed98763129e7e2ffb7d2d4c5bd997519", + "transactionIndex": "0x0", + "blockHash": "0x0a8f06eb397fdab2e4392e16237ee54d6f6fe244e7ae4c021885998578c4cfe0", + "blockNumber": "0x135c76f", + "gasUsed": "0x10eee8", + "effectiveGasPrice": "0x23c34600", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x861e127036b28d32f3777b4676f6bbb9e007d195" + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x11aadd", + "logs": [ + { + "address": "0x3333333333333333333333333333333333333333", + "topics": [ + "0x8c7f585fb295f7eb1e6aeb8fba61b23a4fe60beda405f0045073b185c74412e3", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006401000006000000000000000000000000861e127036b28d32f3777b4676f6bbb9e007d1950000000000000000000000000000000000000000000000000000000000000168000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000", + "blockHash": "0x0a8f06eb397fdab2e4392e16237ee54d6f6fe244e7ae4c021885998578c4cfe0", + "blockNumber": "0x135c76f", + "blockTimestamp": "0x6927e55c", + "transactionHash": "0xafaa7c3ef97727b20d46e5cb7f6478ab2e3597d7426aa0b007e52e016aa0cb8f", + "transactionIndex": "0x1", + "logIndex": "0x2", + "removed": false + } + ], + "logsBloom": "0x00000000020000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000004000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000004000000000000000010000000000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xafaa7c3ef97727b20d46e5cb7f6478ab2e3597d7426aa0b007e52e016aa0cb8f", + "transactionIndex": "0x1", + "blockHash": "0x0a8f06eb397fdab2e4392e16237ee54d6f6fe244e7ae4c021885998578c4cfe0", + "blockNumber": "0x135c76f", + "gasUsed": "0xbbf5", + "effectiveGasPrice": "0x23c34600", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x3333333333333333333333333333333333333333", + "contractAddress": null + }, + { + "status": "0x1", + "cumulativeGasUsed": "0x131001", + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0xea620a26dd14c70920861794ebb4ad4ed9e20eaa0a9e4a8dd076bc5e6e978c12", + "transactionIndex": "0x2", + "blockHash": "0x0a8f06eb397fdab2e4392e16237ee54d6f6fe244e7ae4c021885998578c4cfe0", + "blockNumber": "0x135c76f", + "gasUsed": "0x16524", + "effectiveGasPrice": "0x23c34600", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0x861e127036b28d32f3777b4676f6bbb9e007d195", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764222354052, + "chain": 999, + "commit": "1397709" +} diff --git a/broadcast/DeploySrcPeriphery.s.sol/1/run-latest.json b/broadcast/DeploySrcPeriphery.s.sol/1/run-latest.json new file mode 100644 index 000000000..46c83f498 --- /dev/null +++ b/broadcast/DeploySrcPeriphery.s.sol/1/run-latest.json @@ -0,0 +1,68 @@ +{ + "transactions": [ + { + "hash": "0x644921ba43415194748b1054d7610bc78e0b518b93cc703afc1b1f10af87fb11", + "transactionType": "CREATE", + "contractName": "SponsoredOFTSrcPeriphery", + "contractAddress": "0x4607bceaf7b22cb0c46882ffc9fab3c6efe66e5a", + "function": null, + "arguments": [ + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee", + "30101", + "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x198065", + "value": "0x0", + "input": "0x60e0604090808252346200020c57608081620018158038038091620000258285620002be565b8339810103126200020c576200003b81620002f6565b906020906200004c828201620002f6565b91620000686060620000608785016200030b565b9301620002f6565b935f549260018060a01b03938460018060a01b03199633888416175f55895192823391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3846080528060a0528360c0521691635e280f1160e01b82528482600481865afa9182156200027257869286915f916200027c575b5060048b518095819363416ecebf60e01b8352165afa91821562000272575f9262000233575b5063ffffffff8091169116036200022257908260049392885194858092637e062a3560e11b82525afa928315620002185784925f94620001d1575b50508116911603620001c0577fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500921690825416179055516114f790816200031e823960805181818161025401526107ee015260a05181818161012c0152818161082d0152610d75015260c0518181816102b90152610a3b0152f35b8351633722464560e11b8152600490fd5b809294508193503d831162000210575b620001ed8183620002be565b810103126200020c5782620002038192620002f6565b92905f62000145565b5f80fd5b503d620001e1565b87513d5f823e3d90fd5b8651637c68382b60e01b8152600490fd5b9091508481813d83116200026a575b6200024e8183620002be565b810103126200020c5762000262906200030b565b905f6200010a565b503d62000242565b89513d5f823e3d90fd5b9293505081813d8311620002b6575b620002978183620002be565b810103126200020c57519085821682036200020c578486925f620000e4565b503d6200028b565b601f909101601f19168101906001600160401b03821190821017620002e257604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036200020c57565b519063ffffffff821682036200020c5756fe60806040526004361015610011575f80fd5b5f3560e01c8063238ac933146100c45780635b9cae35146100bf5780636c19e783146100ba578063715018a6146100b557806382bfefc8146100b05780638da5cb5b146100ab578063c9279a74146100a6578063f2fde38b146100a1578063f731ce5f1461009c578063fcc5b1e3146100975763feb6172414610092575f80fd5b610516565b6104a8565b610475565b6102dd565b61029d565b610278565b610235565b6101d1565b610161565b61010d565b34610109575f3660031901126101095760206001600160a01b037fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb5005416604051908152f35b5f80fd5b34610109575f3660031901126101095760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b6001600160a01b0381160361010957565b346101095760203660031901126101095760043561017e81610150565b610186610997565b6001600160a01b037fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500911673ffffffffffffffffffffffffffffffffffffffff198254161790555f80f35b34610109575f366003190112610109576101e9610997565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b34610109575f3660031901126101095760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109575f3660031901126101095760206001600160a01b035f5416604051908152f35b34610109575f36600319011261010957602060405163ffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109576020366003190112610109576004356102fa81610150565b610302610997565b6001600160a01b03809116908115610109575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b634e487b7160e01b5f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761038957604052565b610359565b6080810190811067ffffffffffffffff82111761038957604052565b90601f8019910116810190811067ffffffffffffffff82111761038957604052565b6040519060e0820182811067ffffffffffffffff82111761038957604052565b67ffffffffffffffff811161038957601f01601f191660200190565b6040516020810181811067ffffffffffffffff821117610389576040525f8152905f368137565b5f5b8381106104405750505f910152565b8181015183820152602001610431565b906020916104698151809281855285808601910161042f565b601f01601f1916010190565b34610109575f366003190112610109576104a4610490610408565b604051918291602083526020830190610450565b0390f35b600319604036820112610109576004359067ffffffffffffffff908183116101095760609083360301126101095760243591818311610109573660238401121561010957826004013591821161010957366024838501011161010957602461051493019060040161075d565b005b34610109576020366003190112610109576004355f527fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb501602052602060ff60405f2054166040519015158152f35b1561010957565b90359061019e1981360301821215610109570190565b634e487b7160e01b5f52601160045260245ffd5b919082039182116105a257565b610581565b3d156105d1573d906105b8826103ec565b916105c660405193846103aa565b82523d5f602084013e565b606090565b9190826040910312610109576040516105ee8161036d565b6020808294805184520151910152565b919082810360c0811261010957608013610109576040519067ffffffffffffffff60608301818111848210176103895760405284518352602085015190811681036101095782608091602061066895015261065c83604088016105d6565b604082015294016105d6565b90565b6106689163ffffffff825116815260208201516020820152604082015160408201526060820151606082015260c06106c76106b5608085015160e0608086015260e0850190610450565b60a085015184820360a0860152610450565b9201519060c0818403910152610450565b9193926001600160a01b039060206106fa60609460808752608087019061066b565b968051828701520151604085015216910152565b6040513d5f823e3d90fd5b959287959260e09895928852602088015260408701526060860152608085015260c060a08501528160c0850152848401375f828201840152601f01601f1916010190565b909161076a8184846109bb565b6107b86107ab608061077c858061056b565b01355f527fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb50160205260405f2090565b805460ff19166001179055565b6107c182610bd8565b815134106109855761088292826107db60c0945134610595565b8061095d575b506001600160a01b0391827f00000000000000000000000000000000000000000000000000000000000000001692610828606061081e8b8061056b565b0135303387610def565b6108627f00000000000000000000000000000000000000000000000000000000000000009485606061085a8d8061056b565b013591610e47565b8251916040519788968795869363c7c7f5b360e01b8552600485016106d8565b0393165af180156109585761092a575b507f8fb515a2e89f5acfca1124e69e331c2cded0ca216b578ba531720f6841139dbf60806108c0848061056b565b01359160e06108cf858061056b565b01359461092560406108e1878061056b565b01359260a06108f0888061056b565b01359260c06108ff898061056b565b01359461010061090f8a8061056b565b0135906040519687966040339c01359288610719565b0390a4565b61094b9060c03d60c011610951575b61094381836103aa565b8101906105fe565b50610892565b503d610939565b61070e565b5f80808061097f946001600160a01b0389165af16109796105a7565b50610564565b5f6107e1565b604051639c92bdfb60e01b8152600490fd5b6001600160a01b035f5416330361010957565b3563ffffffff811681036101095790565b91610a0590610a09926109f57fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500546001600160a01b031690565b6109ff868061056b565b90610f41565b1590565b610ab65760a0610a19828061056b565b01354211610aa457610a33610a2e828061056b565b6109aa565b63ffffffff807f000000000000000000000000000000000000000000000000000000000000000016911603610a9257610a74608061077c83610a7b9461056b565b5460ff1690565b610a8157565b604051623f613760e71b8152600490fd5b604051637c68382b60e01b8152600490fd5b604051638727a7f960e01b8152600490fd5b60405163c1606c2f60e01b8152600490fd5b6040519060e0820182811067ffffffffffffffff82111761038957604052606060c0835f81525f60208201525f60408201525f838201528260808201528260a08201520152565b60405190610b1c8261036d565b5f6020838281520152565b3560ff811681036101095790565b903590601e1981360301821215610109570180359067ffffffffffffffff82116101095760200191813603831361010957565b929192610b74826103ec565b91610b8260405193846103aa565b829481845281830111610109578281602093845f960137010152565b9060408282031261010957610668916105d6565b91906020610bc95f9260408652604086019061066b565b930152565b3561066881610150565b610be0610ac8565b50610be9610b0f565b506080610bf6828061056b565b013591610c7d60a0610c08848061056b565b013560c0610c16858061056b565b013560409560e0610c27878061056b565b0135610100610c36888061056b565b013591610c4f610160610c498a8061056b565b01610b27565b93610c72610c6b610c608b8061056b565b610180810190610b35565b3691610b68565b958a8a013592611058565b91610ccf610cb9610c8c6110a3565b610cb3610120610c9c868061056b565b01356fffffffffffffffffffffffffffffffff1690565b906110bd565b610cc9610140610c9c858061056b565b906111b9565b610ce46020610cde848061056b565b016109aa565b9385610cf0848061056b565b0135916060610cff858061056b565b01356060610d0d868061056b565b013590610d18610408565b94610d30610d246103cc565b63ffffffff909a168a52565b6020890152888801526060870152608086015260a085015260c08401528351633b6f743b60e01b8152848180610d698760048301610bb2565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa948515610958575f95610db9575b50506020610db39101610bce565b91929190565b610db392955060209181610de192903d10610de8575b610dd981836103aa565b810190610b9e565b9491610da5565b503d610dcf565b9290604051926323b872dd60e01b60208501526001600160a01b03809216602485015216604483015260648201526064815260a081019181831067ffffffffffffffff84111761038957610e45926040526112b8565b565b91909160405191602083015f8063095ea7b360e01b938484526001600160a01b03908189166024890152604488015260448752610e838761038e565b85169286519082855af190610e966105a7565b82610f0f575b5081610f04575b5015610eb0575b50505050565b60405160208101919091526001600160a01b0390931660248401525f6044840152610efb92610ef690610ef081606481015b03601f1981018352826103aa565b826112b8565b6112b8565b5f808080610eaa565b90503b15155f610ea3565b80519192508115918215610f27575b5050905f610e9c565b610f3a92506020809183010191016112a0565b5f80610f1e565b92906110479261104191610f54816109aa565b90611036610f64602083016109aa565b610ee2610f746101608501610b27565b610f85610c6b610180870187610b35565b60208151910120906040519586946020860198610140830135928a610120820135936101008301359360e08401359360c08101359360a08201359360808301359360406060850135940135929a98969492909d9c9b99979593919d6101a08c019e63ffffffff8092168d521660208c015260408b015260608a0152608089015260a088015260c087015260e086015261010085015261012084015261014083015260ff166101608201526101800152565b519020923691610b68565b90611367565b6001600160a01b0390811691161490565b94610ee2946106689792989460ff946040519a8b9960208b015260408a01526060890152608088015260a087015260c08601521660e084015261010080840152610120830190610450565b604051600360f01b6020820152600281526106688161036d565b9061ffff6003816110cd85611345565b16036111ad576040519260208401926fffffffffffffffffffffffffffffffff199060801b168352601084526111028461036d565b60038261110e83611345565b160361118b5783518281116101095782166001019182116105a2576106689260249261117c604051968461114c89965180926020808a01910161042f565b850192600160f81b9081602086015261ffff60f01b9060f01b16602185015260238401525180938684019061042f565b010360048101845201826103aa565b90611197602492611345565b604051633a51740d60e01b815291166004820152fd5b60249061119784611345565b9061ffff6003816111c985611345565b16036111ad576040519260208401925f84526fffffffffffffffffffffffffffffffff199060801b166022850152601284526112048461036d565b60038261121083611345565b160361118b5783518281116101095782166001019182116105a2576106689260249261117c604051968461124e89965180926020808a01910161042f565b600160f81b60209187019182015260f09390931b7fffff000000000000000000000000000000000000000000000000000000000000166021840152600360f81b6023840152519283908684019061042f565b90816020910312610109575180151581036101095790565b905f806001600160a01b036113159416927f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460206040516112f88161036d565b818152015260208151910182855af161130f6105a7565b9161141d565b805190811591821561132b575b50501561010957565b61133e92506020809183010191016112a0565b5f80611322565b6002815110611355576002015190565b604051632d0483c560e21b8152600490fd5b610668916113749161137c565b9190916113cf565b9060418151145f146113a8576113a491602082015190606060408401519301515f1a90611446565b9091565b50505f90600290565b600511156113bb57565b634e487b7160e01b5f52602160045260245ffd5b6113d8816113b1565b806113e05750565b6113e9816113b1565b600181036113f5575f80fd5b6113fe816113b1565b6002810361140a575f80fd5b806114166003926113b1565b1461010957565b90156114375781511561142e575090565b3b156101095790565b50805190811561010957602001fd5b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116114b6576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15610958575f516001600160a01b038116156114ae57905f90565b505f90600190565b505050505f9060039056fea264697066735822122051769784125192cb0975f21ea1bbee4a627e7578e6b15669655ba40a10d38d8b64736f6c63430008180033000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000006c96de32cea08842dcc4058c14d3aaad7fa41dee00000000000000000000000000000000000000000000000000000000000075950000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "nonce": "0x2ce1", + "chainId": "0x1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x2044cc3", + "logs": [ + { + "address": "0x4607bceaf7b22cb0c46882ffc9fab3c6efe66e5a", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0x551db39887921a6406e0fb12e7e5a64728b98e43b4d55dd49077622b1cc56adb", + "blockNumber": "0x16d23fd", + "blockTimestamp": "0x692f9bdf", + "transactionHash": "0x644921ba43415194748b1054d7610bc78e0b518b93cc703afc1b1f10af87fb11", + "transactionIndex": "0xb8", + "logIndex": "0x1a4", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000080000000040000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000800000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x644921ba43415194748b1054d7610bc78e0b518b93cc703afc1b1f10af87fb11", + "transactionIndex": "0xb8", + "blockHash": "0x551db39887921a6406e0fb12e7e5a64728b98e43b4d55dd49077622b1cc56adb", + "blockNumber": "0x16d23fd", + "gasUsed": "0x139dd8", + "effectiveGasPrice": "0x24606e1", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x4607bceaf7b22cb0c46882ffc9fab3c6efe66e5a" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764727805465, + "chain": 1, + "commit": "5d9f89e2" +} diff --git a/broadcast/DeploySrcPeriphery.s.sol/42161/run-latest.json b/broadcast/DeploySrcPeriphery.s.sol/42161/run-latest.json new file mode 100644 index 000000000..0e4475cbb --- /dev/null +++ b/broadcast/DeploySrcPeriphery.s.sol/42161/run-latest.json @@ -0,0 +1,71 @@ +{ + "transactions": [ + { + "hash": "0xaf2427171b1d55e27dc75985001ce5a6be8c89a817f51b479f223b4c9799c7eb", + "transactionType": "CREATE", + "contractName": "SponsoredOFTSrcPeriphery", + "contractAddress": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "function": null, + "arguments": [ + "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", + "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92", + "30110", + "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D" + ], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "gas": "0x19d578", + "value": "0x0", + "input": "0x60e0604090808252346200020c57608081620018158038038091620000258285620002be565b8339810103126200020c576200003b81620002f6565b906020906200004c828201620002f6565b91620000686060620000608785016200030b565b9301620002f6565b935f549260018060a01b03938460018060a01b03199633888416175f55895192823391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3846080528060a0528360c0521691635e280f1160e01b82528482600481865afa9182156200027257869286915f916200027c575b5060048b518095819363416ecebf60e01b8352165afa91821562000272575f9262000233575b5063ffffffff8091169116036200022257908260049392885194858092637e062a3560e11b82525afa928315620002185784925f94620001d1575b50508116911603620001c0577fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500921690825416179055516114f790816200031e823960805181818161025401526107ee015260a05181818161012c0152818161082d0152610d75015260c0518181816102b90152610a3b0152f35b8351633722464560e11b8152600490fd5b809294508193503d831162000210575b620001ed8183620002be565b810103126200020c5782620002038192620002f6565b92905f62000145565b5f80fd5b503d620001e1565b87513d5f823e3d90fd5b8651637c68382b60e01b8152600490fd5b9091508481813d83116200026a575b6200024e8183620002be565b810103126200020c5762000262906200030b565b905f6200010a565b503d62000242565b89513d5f823e3d90fd5b9293505081813d8311620002b6575b620002978183620002be565b810103126200020c57519085821682036200020c578486925f620000e4565b503d6200028b565b601f909101601f19168101906001600160401b03821190821017620002e257604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036200020c57565b519063ffffffff821682036200020c5756fe60806040526004361015610011575f80fd5b5f3560e01c8063238ac933146100c45780635b9cae35146100bf5780636c19e783146100ba578063715018a6146100b557806382bfefc8146100b05780638da5cb5b146100ab578063c9279a74146100a6578063f2fde38b146100a1578063f731ce5f1461009c578063fcc5b1e3146100975763feb6172414610092575f80fd5b610516565b6104a8565b610475565b6102dd565b61029d565b610278565b610235565b6101d1565b610161565b61010d565b34610109575f3660031901126101095760206001600160a01b037fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb5005416604051908152f35b5f80fd5b34610109575f3660031901126101095760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b6001600160a01b0381160361010957565b346101095760203660031901126101095760043561017e81610150565b610186610997565b6001600160a01b037fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500911673ffffffffffffffffffffffffffffffffffffffff198254161790555f80f35b34610109575f366003190112610109576101e9610997565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b34610109575f3660031901126101095760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109575f3660031901126101095760206001600160a01b035f5416604051908152f35b34610109575f36600319011261010957602060405163ffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109576020366003190112610109576004356102fa81610150565b610302610997565b6001600160a01b03809116908115610109575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b634e487b7160e01b5f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761038957604052565b610359565b6080810190811067ffffffffffffffff82111761038957604052565b90601f8019910116810190811067ffffffffffffffff82111761038957604052565b6040519060e0820182811067ffffffffffffffff82111761038957604052565b67ffffffffffffffff811161038957601f01601f191660200190565b6040516020810181811067ffffffffffffffff821117610389576040525f8152905f368137565b5f5b8381106104405750505f910152565b8181015183820152602001610431565b906020916104698151809281855285808601910161042f565b601f01601f1916010190565b34610109575f366003190112610109576104a4610490610408565b604051918291602083526020830190610450565b0390f35b600319604036820112610109576004359067ffffffffffffffff908183116101095760609083360301126101095760243591818311610109573660238401121561010957826004013591821161010957366024838501011161010957602461051493019060040161075d565b005b34610109576020366003190112610109576004355f527fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb501602052602060ff60405f2054166040519015158152f35b1561010957565b90359061019e1981360301821215610109570190565b634e487b7160e01b5f52601160045260245ffd5b919082039182116105a257565b610581565b3d156105d1573d906105b8826103ec565b916105c660405193846103aa565b82523d5f602084013e565b606090565b9190826040910312610109576040516105ee8161036d565b6020808294805184520151910152565b919082810360c0811261010957608013610109576040519067ffffffffffffffff60608301818111848210176103895760405284518352602085015190811681036101095782608091602061066895015261065c83604088016105d6565b604082015294016105d6565b90565b6106689163ffffffff825116815260208201516020820152604082015160408201526060820151606082015260c06106c76106b5608085015160e0608086015260e0850190610450565b60a085015184820360a0860152610450565b9201519060c0818403910152610450565b9193926001600160a01b039060206106fa60609460808752608087019061066b565b968051828701520151604085015216910152565b6040513d5f823e3d90fd5b959287959260e09895928852602088015260408701526060860152608085015260c060a08501528160c0850152848401375f828201840152601f01601f1916010190565b909161076a8184846109bb565b6107b86107ab608061077c858061056b565b01355f527fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb50160205260405f2090565b805460ff19166001179055565b6107c182610bd8565b815134106109855761088292826107db60c0945134610595565b8061095d575b506001600160a01b0391827f00000000000000000000000000000000000000000000000000000000000000001692610828606061081e8b8061056b565b0135303387610def565b6108627f00000000000000000000000000000000000000000000000000000000000000009485606061085a8d8061056b565b013591610e47565b8251916040519788968795869363c7c7f5b360e01b8552600485016106d8565b0393165af180156109585761092a575b507f8fb515a2e89f5acfca1124e69e331c2cded0ca216b578ba531720f6841139dbf60806108c0848061056b565b01359160e06108cf858061056b565b01359461092560406108e1878061056b565b01359260a06108f0888061056b565b01359260c06108ff898061056b565b01359461010061090f8a8061056b565b0135906040519687966040339c01359288610719565b0390a4565b61094b9060c03d60c011610951575b61094381836103aa565b8101906105fe565b50610892565b503d610939565b61070e565b5f80808061097f946001600160a01b0389165af16109796105a7565b50610564565b5f6107e1565b604051639c92bdfb60e01b8152600490fd5b6001600160a01b035f5416330361010957565b3563ffffffff811681036101095790565b91610a0590610a09926109f57fbbe623e022cc184bd276c9a778810da1531bdd4c0bac9d86069eb499aa2eb500546001600160a01b031690565b6109ff868061056b565b90610f41565b1590565b610ab65760a0610a19828061056b565b01354211610aa457610a33610a2e828061056b565b6109aa565b63ffffffff807f000000000000000000000000000000000000000000000000000000000000000016911603610a9257610a74608061077c83610a7b9461056b565b5460ff1690565b610a8157565b604051623f613760e71b8152600490fd5b604051637c68382b60e01b8152600490fd5b604051638727a7f960e01b8152600490fd5b60405163c1606c2f60e01b8152600490fd5b6040519060e0820182811067ffffffffffffffff82111761038957604052606060c0835f81525f60208201525f60408201525f838201528260808201528260a08201520152565b60405190610b1c8261036d565b5f6020838281520152565b3560ff811681036101095790565b903590601e1981360301821215610109570180359067ffffffffffffffff82116101095760200191813603831361010957565b929192610b74826103ec565b91610b8260405193846103aa565b829481845281830111610109578281602093845f960137010152565b9060408282031261010957610668916105d6565b91906020610bc95f9260408652604086019061066b565b930152565b3561066881610150565b610be0610ac8565b50610be9610b0f565b506080610bf6828061056b565b013591610c7d60a0610c08848061056b565b013560c0610c16858061056b565b013560409560e0610c27878061056b565b0135610100610c36888061056b565b013591610c4f610160610c498a8061056b565b01610b27565b93610c72610c6b610c608b8061056b565b610180810190610b35565b3691610b68565b958a8a013592611058565b91610ccf610cb9610c8c6110a3565b610cb3610120610c9c868061056b565b01356fffffffffffffffffffffffffffffffff1690565b906110bd565b610cc9610140610c9c858061056b565b906111b9565b610ce46020610cde848061056b565b016109aa565b9385610cf0848061056b565b0135916060610cff858061056b565b01356060610d0d868061056b565b013590610d18610408565b94610d30610d246103cc565b63ffffffff909a168a52565b6020890152888801526060870152608086015260a085015260c08401528351633b6f743b60e01b8152848180610d698760048301610bb2565b03816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa948515610958575f95610db9575b50506020610db39101610bce565b91929190565b610db392955060209181610de192903d10610de8575b610dd981836103aa565b810190610b9e565b9491610da5565b503d610dcf565b9290604051926323b872dd60e01b60208501526001600160a01b03809216602485015216604483015260648201526064815260a081019181831067ffffffffffffffff84111761038957610e45926040526112b8565b565b91909160405191602083015f8063095ea7b360e01b938484526001600160a01b03908189166024890152604488015260448752610e838761038e565b85169286519082855af190610e966105a7565b82610f0f575b5081610f04575b5015610eb0575b50505050565b60405160208101919091526001600160a01b0390931660248401525f6044840152610efb92610ef690610ef081606481015b03601f1981018352826103aa565b826112b8565b6112b8565b5f808080610eaa565b90503b15155f610ea3565b80519192508115918215610f27575b5050905f610e9c565b610f3a92506020809183010191016112a0565b5f80610f1e565b92906110479261104191610f54816109aa565b90611036610f64602083016109aa565b610ee2610f746101608501610b27565b610f85610c6b610180870187610b35565b60208151910120906040519586946020860198610140830135928a610120820135936101008301359360e08401359360c08101359360a08201359360808301359360406060850135940135929a98969492909d9c9b99979593919d6101a08c019e63ffffffff8092168d521660208c015260408b015260608a0152608089015260a088015260c087015260e086015261010085015261012084015261014083015260ff166101608201526101800152565b519020923691610b68565b90611367565b6001600160a01b0390811691161490565b94610ee2946106689792989460ff946040519a8b9960208b015260408a01526060890152608088015260a087015260c08601521660e084015261010080840152610120830190610450565b604051600360f01b6020820152600281526106688161036d565b9061ffff6003816110cd85611345565b16036111ad576040519260208401926fffffffffffffffffffffffffffffffff199060801b168352601084526111028461036d565b60038261110e83611345565b160361118b5783518281116101095782166001019182116105a2576106689260249261117c604051968461114c89965180926020808a01910161042f565b850192600160f81b9081602086015261ffff60f01b9060f01b16602185015260238401525180938684019061042f565b010360048101845201826103aa565b90611197602492611345565b604051633a51740d60e01b815291166004820152fd5b60249061119784611345565b9061ffff6003816111c985611345565b16036111ad576040519260208401925f84526fffffffffffffffffffffffffffffffff199060801b166022850152601284526112048461036d565b60038261121083611345565b160361118b5783518281116101095782166001019182116105a2576106689260249261117c604051968461124e89965180926020808a01910161042f565b600160f81b60209187019182015260f09390931b7fffff000000000000000000000000000000000000000000000000000000000000166021840152600360f81b6023840152519283908684019061042f565b90816020910312610109575180151581036101095790565b905f806001600160a01b036113159416927f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460206040516112f88161036d565b818152015260208151910182855af161130f6105a7565b9161141d565b805190811591821561132b575b50501561010957565b61133e92506020809183010191016112a0565b5f80611322565b6002815110611355576002015190565b604051632d0483c560e21b8152600490fd5b610668916113749161137c565b9190916113cf565b9060418151145f146113a8576113a491602082015190606060408401519301515f1a90611446565b9091565b50505f90600290565b600511156113bb57565b634e487b7160e01b5f52602160045260245ffd5b6113d8816113b1565b806113e05750565b6113e9816113b1565b600181036113f5575f80fd5b6113fe816113b1565b6002810361140a575f80fd5b806114166003926113b1565b1461010957565b90156114375781511561142e575090565b3b156101095790565b50805190811561010957602001fd5b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116114b6576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15610958575f516001600160a01b038116156114ae57905f90565b505f90600190565b505050505f9060039056fea264697066735822122051769784125192cb0975f21ea1bbee4a627e7578e6b15669655ba40a10d38d8b64736f6c63430008180033000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb900000000000000000000000014e4a1b13bf7f943c8ff7c51fb60fa964a298d92000000000000000000000000000000000000000000000000000000000000759e0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "nonce": "0x156f", + "chainId": "0xa4b1" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x14ebe6", + "logs": [ + { + "address": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" + ], + "data": "0x", + "blockHash": "0xe8654d6f96063f64d25fde9ed46790b3ff86f62251bf1043b0067a27409af110", + "blockNumber": "0x183c08e6", + "blockTimestamp": "0x692f9b49", + "transactionHash": "0xaf2427171b1d55e27dc75985001ce5a6be8c89a817f51b479f223b4c9799c7eb", + "transactionIndex": "0x3", + "logIndex": "0x1", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000001000000000000000000000000000000000000020000000000000000000800002000000000000000000000040000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000080000000000000", + "type": "0x2", + "transactionHash": "0xaf2427171b1d55e27dc75985001ce5a6be8c89a817f51b479f223b4c9799c7eb", + "transactionIndex": "0x3", + "blockHash": "0xe8654d6f96063f64d25fde9ed46790b3ff86f62251bf1043b0067a27409af110", + "blockNumber": "0x183c08e6", + "gasUsed": "0x13ae42", + "effectiveGasPrice": "0xd570a0", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": null, + "contractAddress": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "gasUsedForL1": "0x106a", + "l1BlockNumber": "0x16d23ef", + "timeboosted": false + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1764727648975, + "chain": 42161, + "commit": "5d9f89e2" +} diff --git a/broadcast/UpdateAuthorizedPeripheries.s.sol/999/run-latest.json b/broadcast/UpdateAuthorizedPeripheries.s.sol/999/run-latest.json new file mode 100644 index 000000000..481152b0b --- /dev/null +++ b/broadcast/UpdateAuthorizedPeripheries.s.sol/999/run-latest.json @@ -0,0 +1,60 @@ +{ + "transactions": [ + { + "hash": "0x19ad09c2ffb53f04a2b5e9186ec23f981897b37feb3e30a7a49f300ee25221f6", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xd9f40794367a2ecb0b409ca8dbc55345c0db6e9f", + "function": "setAuthorizedPeriphery(uint32,bytes32)", + "arguments": ["30101", "0x000000000000000000000000e35d1205a523b699785967fffe99b72059b46707"], + "transaction": { + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xd9f40794367a2ecb0b409ca8dbc55345c0db6e9f", + "gas": "0xbcaa", + "value": "0x0", + "input": "0x52e12a140000000000000000000000000000000000000000000000000000000000007595000000000000000000000000e35d1205a523b699785967fffe99b72059b46707", + "nonce": "0x13f", + "chainId": "0x3e7" + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "status": "0x1", + "cumulativeGasUsed": "0x8101", + "logs": [ + { + "address": "0xd9f40794367a2ecb0b409ca8dbc55345c0db6e9f", + "topics": ["0xf3adc8db618887d7b76838e244efb05fc99475bb5a904a914d939fbdc41b7e8d"], + "data": "0x0000000000000000000000000000000000000000000000000000000000007595000000000000000000000000e35d1205a523b699785967fffe99b72059b46707", + "blockHash": "0x57cf2f4386edc7e19f09a4c0abb92715c71526d2750d1ee34f37fd3ea75e2462", + "blockNumber": "0x11ab67e", + "blockTimestamp": "0x690d4508", + "transactionHash": "0x19ad09c2ffb53f04a2b5e9186ec23f981897b37feb3e30a7a49f300ee25221f6", + "transactionIndex": "0x0", + "logIndex": "0x0", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000800000000000000000000000200000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000020000000000000000000000000400000000000000000000000000000000000", + "type": "0x2", + "transactionHash": "0x19ad09c2ffb53f04a2b5e9186ec23f981897b37feb3e30a7a49f300ee25221f6", + "transactionIndex": "0x0", + "blockHash": "0x57cf2f4386edc7e19f09a4c0abb92715c71526d2750d1ee34f37fd3ea75e2462", + "blockNumber": "0x11ab67e", + "gasUsed": "0x8101", + "effectiveGasPrice": "0x3a7dfcb4", + "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", + "to": "0xd9f40794367a2ecb0b409ca8dbc55345c0db6e9f", + "contractAddress": null + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1762477320670, + "chain": 999, + "commit": "d93c7b1f" +} diff --git a/broadcast/deployed-addresses.json b/broadcast/deployed-addresses.json index 16a712104..770fa6288 100644 --- a/broadcast/deployed-addresses.json +++ b/broadcast/deployed-addresses.json @@ -172,6 +172,11 @@ "address": "0x0Bf07B2e415F02711fFBB32491f8ec9e5489B2e7", "block_number": 19084679, "transaction_hash": "0xa2a7b2c6812fb8ae34539fb04cd5f2a9112da1c7f6ffce0ddcf1fee7e43acf48" + }, + "SponsoredOFTSrcPeriphery": { + "address": "0x4607bceaf7b22cb0c46882ffc9fab3c6efe66e5a", + "block_number": 23929853, + "transaction_hash": "0x644921ba43415194748b1054d7610bc78e0b518b93cc703afc1b1f10af87fb11" } } }, @@ -447,6 +452,31 @@ "SpokePoolPeriphery": { "address": "0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb", "block_number": 15142204 + }, + "DonationBox": { + "address": "0x90e2487764e5316a2e4109c2ed40a3b3ad423659", + "block_number": 20815766, + "transaction_hash": "0xf72c3e798991af0e7123197bfd7da40585f936b4353b91159b749008ceac541a" + }, + "SponsoredCCTPDstPeriphery": { + "address": "0x83e245941befbde29682df068bcda006a804eb0c", + "block_number": 20793257, + "transaction_hash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802" + }, + "PermissionedMulticallHandler": { + "address": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", + "block_number": 20815766, + "transaction_hash": "0xee446c5d024fc2d807a387b7be9dc0e17f7f806efac526cc1db7bebd7fa5f7c7" + }, + "DstOFTHandler": { + "address": "0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461", + "block_number": 20815766, + "transaction_hash": "0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7" + }, + "HyperliquidDepositHandler": { + "address": "0x861e127036b28d32f3777b4676f6bbb9e007d195", + "block_number": 20301679, + "transaction_hash": "0x187b45f39be413aff2ff526946c64f76ed98763129e7e2ffb7d2d4c5bd997519" } } }, @@ -550,6 +580,11 @@ "address": "0x2eb5def5cf9635f9d926788a26d424287a045b92", "block_number": 36361964, "transaction_hash": "0x6ec337bdf00c331fabbfb0e98862aa2b766c613b6d6002c44b53668b7240989c" + }, + "SponsoredCCTPSrcPeriphery": { + "address": "0xa7a8d1efc1ee3e69999d370380949092251a5c20", + "block_number": 38957833, + "transaction_hash": "0xa7a480cad9b735b44890b80945b33b22d171820c6343349343813bb25e5be017" } } }, @@ -648,6 +683,16 @@ "address": "0x33d52d76d617126648067401c106923e4a34dbe1", "block_number": 385694983, "transaction_hash": "0x3a645809d7a2d2a0176afc87f8e565419f94547c9b1521b537b6f233c3bb412c" + }, + "SponsoredCCTPSrcPeriphery": { + "address": "0xce1ffe01ebb4f8521c12e74363a396ee3d337e1b", + "block_number": 406495696, + "transaction_hash": "0x6c4f6d7537530911757ecc317e0b5a39b5caf7089ab5549cb4299c830a9d854c" + }, + "SponsoredOFTSrcPeriphery": { + "address": "0x2ac5ee3796e027da274fbde84c82173a65868940", + "block_number": 406587622, + "transaction_hash": "0xaf2427171b1d55e27dc75985001ce5a6be8c89a817f51b479f223b4c9799c7eb" } } }, diff --git a/broadcast/deployed-addresses.md b/broadcast/deployed-addresses.md index 5579874f8..79d739cec 100644 --- a/broadcast/deployed-addresses.md +++ b/broadcast/deployed-addresses.md @@ -242,6 +242,12 @@ This file contains the latest deployed smart contract addresses from the broadca - Transaction Hash: `0xa2a7b2c6812fb8ae34539fb04cd5f2a9112da1c7f6ffce0ddcf1fee7e43acf48` - Block Number: `19084679` +#### SponsoredOFTSrcPeriphery + +- **SponsoredOFTSrcPeriphery**: `0x4607bceaf7b22cb0c46882ffc9fab3c6efe66e5a` + - Transaction Hash: `0x644921ba43415194748b1054d7610bc78e0b518b93cc703afc1b1f10af87fb11` + - Block Number: `23929853` + ### Optimism (Chain ID: 10) #### SpokePool @@ -526,6 +532,36 @@ This file contains the latest deployed smart contract addresses from the broadca - **SpokePoolPeriphery**: `0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb` - Block Number: `15142204` +#### DonationBox + +- **DonationBox**: `0x002e76dc036a1eff1488ee5435ee66c6abf32674` + - Transaction Hash: `0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d` + - Block Number: `20793257` + +- **SponsoredCCTPDstPeriphery**: `0x83e245941befbde29682df068bcda006a804eb0c` + - Transaction Hash: `0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802` + - Block Number: `20793257` + +#### DonationBox + +- **DonationBox**: `0x90e2487764e5316a2e4109c2ed40a3b3ad423659` + - Transaction Hash: `0xf72c3e798991af0e7123197bfd7da40585f936b4353b91159b749008ceac541a` + - Block Number: `20815766` + +- **PermissionedMulticallHandler**: `0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502` + - Transaction Hash: `0xee446c5d024fc2d807a387b7be9dc0e17f7f806efac526cc1db7bebd7fa5f7c7` + - Block Number: `20815766` + +- **DstOFTHandler**: `0x40153ddfad90c49dbe3f5c9f96f2a5b25ec67461` + - Transaction Hash: `0xb29d240d7893c5e112a3407a86d1ae520674cb3799cc0fb88420b386dbf018c7` + - Block Number: `20815766` + +#### HyperliquidDepositHandler + +- **HyperliquidDepositHandler**: `0x861e127036b28d32f3777b4676f6bbb9e007d195` + - Transaction Hash: `0x187b45f39be413aff2ff526946c64f76ed98763129e7e2ffb7d2d4c5bd997519` + - Block Number: `20301679` + ### Lisk (Chain ID: 1135) #### SpokePool @@ -609,6 +645,12 @@ This file contains the latest deployed smart contract addresses from the broadca - Transaction Hash: `0x6ec337bdf00c331fabbfb0e98862aa2b766c613b6d6002c44b53668b7240989c` - Block Number: `36361964` +#### SponsoredCCTPSrcPeriphery + +- **SponsoredCCTPSrcPeriphery**: `0xa7a8d1efc1ee3e69999d370380949092251a5c20` + - Transaction Hash: `0xa7a480cad9b735b44890b80945b33b22d171820c6343349343813bb25e5be017` + - Block Number: `38957833` + ### Plasma (Chain ID: 9745) #### Helios @@ -702,6 +744,18 @@ This file contains the latest deployed smart contract addresses from the broadca - Transaction Hash: `0x3a645809d7a2d2a0176afc87f8e565419f94547c9b1521b537b6f233c3bb412c` - Block Number: `385694983` +#### SponsoredCCTPSrcPeriphery + +- **SponsoredCCTPSrcPeriphery**: `0xce1ffe01ebb4f8521c12e74363a396ee3d337e1b` + - Transaction Hash: `0x6c4f6d7537530911757ecc317e0b5a39b5caf7089ab5549cb4299c830a9d854c` + - Block Number: `406495696` + +#### SponsoredOFTSrcPeriphery + +- **SponsoredOFTSrcPeriphery**: `0x2ac5ee3796e027da274fbde84c82173a65868940` + - Transaction Hash: `0xaf2427171b1d55e27dc75985001ce5a6be8c89a817f51b479f223b4c9799c7eb` + - Block Number: `406587622` + ### Ink (Chain ID: 57073) #### SpokePool diff --git a/contracts/handlers/HyperliquidDepositHandler.sol b/contracts/handlers/HyperliquidDepositHandler.sol new file mode 100644 index 000000000..a7ece2200 --- /dev/null +++ b/contracts/handlers/HyperliquidDepositHandler.sol @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.0; + +import "../interfaces/SpokePoolMessageHandler.sol"; +import "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts-v4/security/ReentrancyGuard.sol"; +import { ECDSA } from "@openzeppelin/contracts-v4/utils/cryptography/ECDSA.sol"; +import { HyperCoreLib } from "../libraries/HyperCoreLib.sol"; +import { Ownable } from "@openzeppelin/contracts-v4/access/Ownable.sol"; +import { DonationBox } from "../chain-adapters/DonationBox.sol"; + +/** + * @title Allows caller to bridge tokens from HyperEVM to Hypercore and send them to an end user's account + * on Hypercore. + * @dev This contract should only be deployed on HyperEVM. + * @dev This contract can replace a MulticallHandler on HyperEVM if the intent only wants to deposit tokens into + * Hypercore and bypass the other complex arbitrary calldata logic. + * @dev This contract can also be called directly to deposit tokens into Hypercore on behalf of an end user. + */ +contract HyperliquidDepositHandler is AcrossMessageHandler, ReentrancyGuard, Ownable { + using SafeERC20 for IERC20; + struct TokenInfo { + // HyperEVM token address. + address evmAddress; + // Hypercore token index. + uint64 tokenId; + // Activation fee in EVM units. e.g. 1000000 ($1) for USDH. + uint256 activationFeeEvm; + // coreDecimals - evmDecimals. e.g. -2 for USDH. + int8 decimalDiff; + } + + // Stores hardcoded Hypercore configurations for tokens that this handler supports. + mapping(address => TokenInfo) public supportedTokens; + + // Donation box contract to store funds for account activation fees. + DonationBox public immutable donationBox; + + // Address of the signer that will sign the payloads used for calling handleV3AcrossMessage. This signer + // should be one controlled by the Across API to prevent griefing attacks that attempt to drain the Donation Box. + address public signer; + + // Address of the SpokePool contract that can call handleV3AcrossMessage. + address public spokePool; + + // Track which accounts we have already sponsored for activation. Used to prevent griefing attacks when the same account is activated multiple times + // due to Hyperliquid's policy of removing dust from small accounts which technically could be taken advantage of by a griefer. + mapping(address => bool) public accountsActivated; + + error InsufficientEvmAmountForActivation(); + error TokenNotSupported(); + error InvalidSignature(); + error NotSpokePool(); + error AccountAlreadyActivated(); + + event UserAccountActivated(address user, address indexed token, uint256 amountRequiredToActivate); + event AddedSupportedToken(address evmAddress, uint64 tokenId, uint256 activationFeeEvm, int8 decimalDiff); + event SignerSet(address signer); + event SpokePoolSet(address spokePool); + + /** + * @notice Constructor. + * @dev Creates a new donation box contract owned by this contract. + * @param _signer Address of the signer that will sign the payloads used for calling handleV3AcrossMessage. This signer + * should be one controlled by the Across API to prevent griefing attacks that attempt to drain the Donation Box. + * @param _spokePool Address of the SpokePool contract that can call handleV3AcrossMessage. + */ + constructor(address _signer, address _spokePool) { + donationBox = new DonationBox(); + signer = _signer; + spokePool = _spokePool; + } + + modifier onlySpokePool() { + if (msg.sender != spokePool) revert NotSpokePool(); + _; + } + + /// ------------------------------------------------------------------------------------------------------------- + /// - PUBLIC FUNCTIONS - + /// ------------------------------------------------------------------------------------------------------------- + + /** + * @notice Bridges tokens from HyperEVM to Hypercore and sends them to the end user's account on Hypercore. + * @dev Requires msg.sender to have approved this contract to spend the tokens. + * @param token The address of the token to deposit. + * @param amount The amount of tokens on HyperEVM to deposit. + * @param user The address of the user on Hypercore to send the tokens to. + * @param signature Encoded signed message containing the end user address. The payload is designed to be signed + * by the Across API to prevent griefing attacks that attempt to drain the Donation Box. + */ + function depositToHypercore( + address token, + uint256 amount, + address user, + bytes memory signature + ) external nonReentrant { + _verifySignature(user, signature); + IERC20(token).safeTransferFrom(msg.sender, address(this), amount); + _depositToHypercore(token, amount, user); + } + + /** + * @notice Entrypoint function if this contract is called by the SpokePool contract following an intent fill. + * @dev Deposits tokens into Hypercore and sends them to the end user's account on Hypercore. + * @dev Requires msg.sender to be the SpokePool contract. This prevents someone from calling this function + * to drain funds that were accidentally dropped onto this contract. + * @param token The address of the token sent. + * @param amount The amount of tokens received by this contract. + * @param message Encoded signed message containing the end user address. The payload is designed to be signed + * by the Across API to prevent griefing attacks that attempt to drain the Donation Box. + */ + function handleV3AcrossMessage( + address token, + uint256 amount, + address /* relayer */, + bytes memory message + ) external nonReentrant onlySpokePool { + (address user, bytes memory signature) = abi.decode(message, (address, bytes)); + _verifySignature(user, signature); + _depositToHypercore(token, amount, user); + } + + /// ------------------------------------------------------------------------------------------------------------- + /// - ONLY OWNER FUNCTIONS - + /// ------------------------------------------------------------------------------------------------------------- + + /** + * @notice Sets the address of the signer that will sign the payloads used for calling handleV3AcrossMessage. + * @dev Caller must be owner of this contract. + * @param _signer Address of the signer that will sign the payloads used for calling handleV3AcrossMessage. This signer + * should be one controlled by the Across API to prevent griefing attacks that attempt to drain the Donation Box. + */ + function setSigner(address _signer) external onlyOwner { + signer = _signer; + emit SignerSet(signer); + } + + /** + * @notice Sets the address of the SpokePool contract that can call handleV3AcrossMessage. + * @dev Caller must be owner of this contract. + * @param _spokePool Address of the SpokePool contract that can call handleV3AcrossMessage. + */ + function setSpokePool(address _spokePool) external onlyOwner { + spokePool = _spokePool; + emit SpokePoolSet(spokePool); + } + + /** + * @notice Adds a new token to the supported tokens list. + * @dev Caller must be owner of this contract. + * @param evmAddress The address of the EVM token. + * @param tokenId The index of the Hypercore token. + * @param activationFeeEvm The activation fee in EVM units. + * @param decimalDiff The difference in decimals between the EVM and Hypercore tokens. + */ + function addSupportedToken( + address evmAddress, + uint64 tokenId, + uint256 activationFeeEvm, + int8 decimalDiff + ) external onlyOwner { + supportedTokens[evmAddress] = TokenInfo({ + evmAddress: evmAddress, + tokenId: tokenId, + activationFeeEvm: activationFeeEvm, + decimalDiff: decimalDiff + }); + emit AddedSupportedToken(evmAddress, tokenId, activationFeeEvm, decimalDiff); + } + + /** + * @notice Send Hypercore funds to a user from this contract's Hypercore account + * @dev The coreAmount parameter is specified in Hypercore units which often differs from the EVM units for the + * same token. + * @param token The token address + * @param coreAmount The amount of tokens on Hypercore to sweep + * @param user The address of the user to send the tokens to + */ + function sweepCoreFundsToUser(address token, uint64 coreAmount, address user) external onlyOwner nonReentrant { + uint64 tokenIndex = _getTokenInfo(token).tokenId; + HyperCoreLib.transferERC20CoreToCore(tokenIndex, user, coreAmount); + } + + /** + * @notice Send donation box funds to a user from this contract's address on HyperEVM + * @param token The token address + * @param amount The amount of tokens to sweep + * @param user The address of the user to send the tokens to + */ + function sweepDonationBoxFundsToUser(address token, uint256 amount, address user) external onlyOwner nonReentrant { + donationBox.withdraw(IERC20(token), amount); + IERC20(token).safeTransfer(user, amount); + } + + /** + * @notice Send ERC20 tokens to a user from this contract's address on HyperEVM + * @param token The token address + * @param evmAmount The amount of tokens to sweep + * @param user The address of the user to send the tokens to + */ + function sweepERC20ToUser(address token, uint256 evmAmount, address user) external onlyOwner nonReentrant { + IERC20(token).safeTransfer(user, evmAmount); + } + + /// ------------------------------------------------------------------------------------------------------------- + /// - INTERNAL FUNCTIONS - + /// ------------------------------------------------------------------------------------------------------------- + + function _depositToHypercore(address token, uint256 evmAmount, address user) internal { + TokenInfo memory tokenInfo = _getTokenInfo(token); + uint64 tokenIndex = tokenInfo.tokenId; + int8 decimalDiff = tokenInfo.decimalDiff; + + bool userExists = HyperCoreLib.coreUserExists(user); + if (!userExists) { + if (accountsActivated[user]) revert AccountAlreadyActivated(); + accountsActivated[user] = true; + // To activate an account, we must pay the activation fee from this contract's core account and then send 1 + // wei to the user's account, so we pull the activation fee + 1 wei from the donation box. This contract + // does not allow the end user subtracting part of their received amount to use for the activation fee. + uint256 activationFee = tokenInfo.activationFeeEvm; + uint256 amountRequiredToActivate = activationFee + 1; + donationBox.withdraw(IERC20(token), amountRequiredToActivate); + // Deposit the activation fee + 1 wei into this contract's core account to pay for the user's + // account activation. + HyperCoreLib.transferERC20EVMToSelfOnCore(token, tokenIndex, amountRequiredToActivate, decimalDiff); + HyperCoreLib.transferERC20CoreToCore(tokenIndex, user, 1); + emit UserAccountActivated(user, token, amountRequiredToActivate); + } + + HyperCoreLib.transferERC20EVMToCore(token, tokenIndex, user, evmAmount, decimalDiff); + } + + function _verifySignature(address expectedUser, bytes memory signature) internal view returns (bool) { + /// @dev There is no nonce in this signature because an account on Hypercore can only be activated once + /// by this contract, so reusing a signature cannot be used to grief the DonationBox. + bytes32 expectedHash = keccak256(abi.encode(expectedUser)); + if (ECDSA.recover(expectedHash, signature) != signer) revert InvalidSignature(); + } + + function _getTokenInfo(address evmAddress) internal view returns (TokenInfo memory) { + if (supportedTokens[evmAddress].evmAddress == address(0)) { + revert TokenNotSupported(); + } + return supportedTokens[evmAddress]; + } + + // Native tokens are not supported by this contract, so there is no fallback function. +} diff --git a/contracts/test/MockEndpoint.sol b/contracts/test/MockEndpoint.sol new file mode 100644 index 000000000..4ee7870d4 --- /dev/null +++ b/contracts/test/MockEndpoint.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { IEndpoint } from "../interfaces/IOFT.sol"; + +contract MockEndpoint is IEndpoint { + uint32 internal _eid; + + constructor(uint32 eid_) { + _eid = eid_; + } + + function eid() external view override returns (uint32) { + return _eid; + } +} diff --git a/contracts/test/MockOFTMessenger.sol b/contracts/test/MockOFTMessenger.sol index 79b2c40ae..8b732eb82 100644 --- a/contracts/test/MockOFTMessenger.sol +++ b/contracts/test/MockOFTMessenger.sol @@ -8,7 +8,7 @@ import "../interfaces/IOFT.sol"; * @dev This contract is intended to be inherited by other chain-specific adapters and spoke pools. * @custom:security-contact bugs@across.to */ -contract MockOFTMessenger is IOFT { +contract MockOFTMessenger is IOFT, IOAppCore { address public token; uint256 public nativeFee; uint256 public lzFee; @@ -16,12 +16,33 @@ contract MockOFTMessenger is IOFT { uint256 public amountReceivedLDToReturn; bool public useCustomReceipt; + // IOAppCore endpoint for tests that require endpoint().eid() + IEndpoint public endpoint_; + + // Captured call data for assertions in tests + SendParam public lastSendParam; + MessagingFee public lastFee; + address public lastRefundAddress; + uint256 public lastMsgValue; + uint256 public sendCallCount; + constructor(address _token) { token = _token; } + // Test helper to set the endpoint used by IOAppCore + function setEndpoint(address endpointAddr) external { + endpoint_ = IEndpoint(endpointAddr); + } + + // IOAppCore + function endpoint() external view returns (IEndpoint iEndpoint) { + return endpoint_; + } + + // IOFT function quoteSend( - SendParam calldata, /*_sendParam*/ + SendParam calldata /*_sendParam*/, bool /*_payInLzToken*/ ) external view returns (MessagingFee memory) { return MessagingFee(nativeFee, lzFee); @@ -29,9 +50,14 @@ contract MockOFTMessenger is IOFT { function send( SendParam calldata _sendParam, - MessagingFee calldata, /*_fee*/ - address /*_refundAddress*/ + MessagingFee calldata _fee, + address _refundAddress ) external payable returns (MessagingReceipt memory, OFTReceipt memory) { + lastSendParam = _sendParam; + lastFee = _fee; + lastRefundAddress = _refundAddress; + lastMsgValue = msg.value; + sendCallCount++; if (useCustomReceipt) { return ( MessagingReceipt(0, 0, MessagingFee(0, 0)), diff --git a/contracts/test/interfaces/IHyperCoreFlowExecutor.sol b/contracts/test/interfaces/IHyperCoreFlowExecutor.sol new file mode 100644 index 000000000..c4bd58a9d --- /dev/null +++ b/contracts/test/interfaces/IHyperCoreFlowExecutor.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { CommonFlowParams } from "../../periphery/mintburn/Structs.sol"; +import { DonationBox } from "../../chain-adapters/DonationBox.sol"; + +/** + * @title IHyperCoreFlowExecutor + * @notice Interface for HyperCoreFlowExecutor contract handling HyperCore interactions for transfer-to-core or swap-with-core actions after stablecoin bridge transactions + * @custom:security-contact bugs@across.to + */ +interface IHyperCoreFlowExecutor { + /************************************** + * PUBLIC VARIABLE GETTERS * + **************************************/ + + /// @notice Common decimals scalars - parts per million decimals + /// @return PPM_DECIMALS Parts per million decimals (6) + function PPM_DECIMALS() external returns (uint256); + + /// @notice Parts per million scalar (10^6) + /// @return PPM_SCALAR Parts per million scalar + function PPM_SCALAR() external returns (uint256); + + /// @notice Decimals to use for Price calculations in limit order-related calculation functions + /// @return PX_D Price decimals (8) + function PX_D() external returns (uint8); + + /// @notice One in 1e8 format (10^8) + /// @return ONEX1e8 One in 1e8 format + function ONEX1e8() external returns (uint64); + + /// @notice The donation box contract. + /// @return donationBox Address of the donation box contract + function donationBox() external returns (DonationBox); + + /// @notice All operations performed in this contract are relative to this baseToken + /// @return baseToken Address of the base token + function baseToken() external returns (address); + + /************************************** + * FLOW FUNCTIONS * + **************************************/ + + /** + * @notice External entrypoint to execute flow when called via delegatecall from a handler. Works with params + * checked by a handler. Params authorization by a handler is enforced via `onlyAuthorizedFlow` modifier + * @param params Common flow parameters + * @param maxUserSlippageBps Maximum user slippage in basis points + */ + function executeFlow(CommonFlowParams memory params, uint256 maxUserSlippageBps) external; + + /// @notice External entrypoint to execute simple transfer flow (see `executeFlow` comment for details) + /// @param params Common flow parameters + function executeSimpleTransferFlow(CommonFlowParams memory params) external; + + /// @notice External entrypoint to execute fallback evm flow (see `executeFlow` comment for details) + /// @param params Common flow parameters + function fallbackHyperEVMFlow(CommonFlowParams memory params) external; + + /** + * @notice Finalizes multiple swap flows associated with a final token, subject to the L1 Hyperliquid balance + * @dev Caller is responsible for providing correct limitOrderOutput amounts per assosicated swap flow. The caller + * has to estimate how much final tokens it received on core based on the input of the corresponding quote nonce + * swap flow + * @param finalToken The final token address + * @param quoteNonces Array of quote nonces to finalize + * @param limitOrderOuts Array of limit order outputs corresponding to each quote nonce + * @return finalized Number of swaps successfully finalized + */ + function finalizeSwapFlows( + address finalToken, + bytes32[] calldata quoteNonces, + uint64[] calldata limitOrderOuts + ) external returns (uint256 finalized); + + /** + * @notice Activates a user account on Core by funding the account activation fee. + * @param quoteNonce The nonce of the quote that is used to identify the user. + * @param finalRecipient The address of the recipient of the funds. + * @param fundingToken The address of the token that is used to fund the account activation fee. + */ + function activateUserAccount(bytes32 quoteNonce, address finalRecipient, address fundingToken) external; + + /** + * @notice Cancells a pending limit order by `cloid` with an intention to submit a new limit order in its place. To + * be used for stale limit orders to speed up executing user transactions + * @param finalToken The final token address + * @param cloid The client order ID to cancel + */ + function cancelLimitOrderByCloid(address finalToken, uint128 cloid) external; + + /** + * @notice Submits a limit order from the bot + * @param finalToken The final token address + * @param priceX1e8 Price in 1e8 format + * @param sizeX1e8 Size in 1e8 format + * @param cloid The client order ID + */ + function submitLimitOrderFromBot(address finalToken, uint64 priceX1e8, uint64 sizeX1e8, uint128 cloid) external; + + /** + * @notice Set or update information for the token to use it in this contract + * @dev To be able to use the token in the swap flow, FinalTokenInfo has to be set as well + * @dev Setting core token info to incorrect values can lead to loss of funds. Should NEVER be unset while the + * finalTokenParams are not unset + * @param token The token address + * @param coreIndex The HyperCore index of the token + * @param canBeUsedForAccountActivation Whether the token can be used for account activation + * @param accountActivationFeeCore The account activation fee in core units + * @param bridgeSafetyBufferCore The bridge safety buffer in core units + */ + function setCoreTokenInfo( + address token, + uint32 coreIndex, + bool canBeUsedForAccountActivation, + uint64 accountActivationFeeCore, + uint64 bridgeSafetyBufferCore + ) external; + + /** + * @notice Sets the parameters for a final token. + * @dev This function deploys a new SwapHandler contract if one is not already set. If the final token + * can't be used for account activation, the handler will be left unactivated and would need to be activated by the caller. + * @param finalToken The address of the final token. + * @param assetIndex The index of the asset in the Hyperliquid market. + * @param isBuy Whether the final token is a buy or a sell. + * @param feePpm The fee in parts per million. + * @param suggestedDiscountBps The suggested slippage in basis points. + * @param accountActivationFeeToken A token to pay account activation fee in. Only used if adding a new final token + */ + function setFinalTokenInfo( + address finalToken, + uint32 assetIndex, + bool isBuy, + uint32 feePpm, + uint32 suggestedDiscountBps, + address accountActivationFeeToken + ) external; + + /** + * @notice Predicts the deterministic address of a SwapHandler for a given finalToken using CREATE2 + * @param finalToken The final token address + * @return The predicted address of the SwapHandler + */ + function predictSwapHandler(address finalToken) external view returns (address); + + /** + * @notice Used for ad-hoc sends of sponsorship funds to associated SwapHandler @ HyperCore + * @param token The final token for which we want to fund the SwapHandler + * @param amount The amount to send + */ + function sendSponsorshipFundsToSwapHandler(address token, uint256 amount) external; + + /** + * @notice Sweeps ERC20 tokens from the contract + * @param token The token address to sweep + * @param amount The amount to sweep + */ + function sweepErc20(address token, uint256 amount) external; + + /** + * @notice Sweeps ERC20 tokens from the donation box + * @param token The token address to sweep + * @param amount The amount to sweep + */ + function sweepErc20FromDonationBox(address token, uint256 amount) external; + + /** + * @notice Sweeps ERC20 tokens from a SwapHandler + * @param token The token address to sweep + * @param amount The amount to sweep + */ + function sweepERC20FromSwapHandler(address token, uint256 amount) external; + + /** + * @notice Sweeps tokens from Core + * @param token The token address + * @param amount The amount to sweep in core units + */ + function sweepOnCore(address token, uint64 amount) external; + + /** + * @notice Sweeps tokens from Core from a SwapHandler + * @param token The token address + * @param amount The amount to sweep in core units + */ + function sweepOnCoreFromSwapHandler(address token, uint64 amount) external; +} diff --git a/foundry.toml b/foundry.toml index 9b7c60c8b..02b0fa0b7 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,29 +8,12 @@ out = "out" test = "test/evm/foundry" libs = ["node_modules", "lib"] cache_path = "cache-foundry" -remappings = [ - "@across-protocol/=node_modules/@across-protocol/", - "@ensdomains/=node_modules/@ensdomains/", - "@eth-optimism/=node_modules/@eth-optimism/", - "@gnosis.pm/=node_modules/@gnosis.pm/", - "@maticnetwork/=node_modules/@maticnetwork/", - "@matterlabs/=node_modules/@matterlabs/", - "@openzeppelin/=node_modules/@openzeppelin/", - "@scroll-tech/=node_modules/@scroll-tech/", - "@uniswap/=node_modules/@uniswap/", - "arb-bridge-eth/=node_modules/arb-bridge-eth/", - "arb-bridge-peripherals/=node_modules/arb-bridge-peripherals/", - "arbos-precompiles/=node_modules/arbos-precompiles/", - "base64-sol/=node_modules/base64-sol/", - "eth-gas-reporter/=node_modules/eth-gas-reporter/", - "hardhat-deploy/=node_modules/hardhat-deploy/", - "hardhat/=node_modules/hardhat/", -] via_ir = true optimizer_runs = 800 solc_version = "0.8.24" revert_strings = "strip" -fs_permissions = [{ access = "read", path = "./"}] +fs_permissions = [{ access = "read-write", path = "./" }] +use_literal_content = true solc = "0.8.24" evm_version = "prague" @@ -65,8 +48,8 @@ zksync = "${NODE_URL_324}" hyperevm = "${NODE_URL_999}" # testnet -arbitrum-testnet = "${NODE_URL_421614}" -hyperevm-testnet = "${NODE_URL_998}" +arbitrum_sepolia = "${NODE_URL_421614}" +hyperevm_testnet = "${NODE_URL_998}" [etherscan] diff --git a/package.json b/package.json index 559cfd4ec..6b6377a16 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@across-protocol/contracts", - "version": "4.1.12", + "version": "4.1.12-beta.7", "author": "UMA Team", "license": "AGPL-3.0-only", "repository": { @@ -141,8 +141,9 @@ } }, "publishConfig": { - "registry": "https://registry.npmjs.com/", - "access": "public" + "registry": "https://registry.npmjs.org/", + "access": "public", + "provenance": false }, "overrides": { "secp256k1@3.7.1": "3.8.1", diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 000000000..37f0a3482 --- /dev/null +++ b/remappings.txt @@ -0,0 +1,18 @@ +@across-protocol/=node_modules/@across-protocol/ +@ensdomains/=node_modules/@ensdomains/ +@eth-optimism/=node_modules/@eth-optimism/ +@gnosis.pm/=node_modules/@gnosis.pm/ +@maticnetwork/=node_modules/@maticnetwork/ +@matterlabs/=node_modules/@matterlabs/ +@openzeppelin/=node_modules/@openzeppelin/ +@scroll-tech/=node_modules/@scroll-tech/ +@uniswap/=node_modules/@uniswap/ +arb-bridge-eth/=node_modules/arb-bridge-eth/ +arb-bridge-peripherals/=node_modules/arb-bridge-peripherals/ +arbos-precompiles/=node_modules/arbos-precompiles/ +base64-sol/=node_modules/base64-sol/ +eth-gas-reporter/=node_modules/eth-gas-reporter/ +hardhat-deploy/=node_modules/hardhat-deploy/ +hardhat/=node_modules/hardhat/ +@uma/=node_modules/@uma/ +forge-std/=lib/forge-std/src/ diff --git a/script/113DeploySponsoredCCTPSrcPeriphery.sol b/script/113DeploySponsoredCCTPSrcPeriphery.sol deleted file mode 100644 index d192aa910..000000000 --- a/script/113DeploySponsoredCCTPSrcPeriphery.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import { Script } from "forge-std/Script.sol"; -import { Test } from "forge-std/Test.sol"; -import { console } from "forge-std/console.sol"; -import { DeploymentUtils } from "./utils/DeploymentUtils.sol"; - -import { SponsoredCCTPSrcPeriphery } from "../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol"; - -// Deploy: forge script script/114DeploySponsoredCCTPSrcPeriphery.sol:DeploySponsoredCCTPSrcPeriphery --rpc-url -vvvv -contract DeploySponsoredCCTPSrcPeriphery is Script, Test, DeploymentUtils { - function run() external { - console.log("Deploying SponsoredCCTPSrcPeriphery..."); - console.log("Chain ID:", block.chainid); - - string memory deployerMnemonic = vm.envString("MNEMONIC"); - uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); - address deployer = vm.addr(deployerPrivateKey); - - address cctpTokenMessenger = getL2Address(block.chainid, "cctpV2TokenMessenger"); - // Testnet - // address cctpTokenMessenger = 0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA; - - uint32 sourceDomain = getCircleDomainId(block.chainid); - - console.log("cctpTokenMessenger:", cctpTokenMessenger); - console.log("sourceDomain:", sourceDomain); - console.log("deployer:", deployer); - - vm.startBroadcast(deployerPrivateKey); - - SponsoredCCTPSrcPeriphery sponsoredCCTPSrcPeriphery = new SponsoredCCTPSrcPeriphery( - cctpTokenMessenger, - sourceDomain, - deployer - ); - - console.log("SponsoredCCTPSrcPeriphery deployed to:", address(sponsoredCCTPSrcPeriphery)); - } -} diff --git a/script/114DeploySponsoredCCTPDstPeriphery.sol b/script/114DeploySponsoredCCTPDstPeriphery.sol deleted file mode 100644 index 25a46b695..000000000 --- a/script/114DeploySponsoredCCTPDstPeriphery.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import { Script } from "forge-std/Script.sol"; -import { Test } from "forge-std/Test.sol"; -import { console } from "forge-std/console.sol"; - -import { DeploymentUtils } from "./utils/DeploymentUtils.sol"; -import { DonationBox } from "../contracts/chain-adapters/DonationBox.sol"; -import { SponsoredCCTPDstPeriphery } from "../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol"; - -// Deploy: forge script script/114DeploySponsoredCCTPDstPeriphery.sol:DeploySponsoredCCTPDstPeriphery --rpc-url -vvvv -contract DeploySponsoredCCTPDstPeriphery is Script, Test, DeploymentUtils { - function run() external { - console.log("Deploying SponsoredCCTPDstPeriphery..."); - console.log("Chain ID:", block.chainid); - - string memory deployerMnemonic = vm.envString("MNEMONIC"); - uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); - address deployer = vm.addr(deployerPrivateKey); - - address cctpMessageTransmitter = getL2Address(block.chainid, "cctpV2MessageTransmitter"); - // address cctpMessageTransmitter = 0x81D40F21F12A8F0E3252Bccb954D722d4c464B64; - - vm.startBroadcast(deployerPrivateKey); - - DonationBox donationBox = new DonationBox(); - console.log("DonationBox deployed to:", address(donationBox)); - - // USDC on HyperEVM Testnet - // address baseToken = 0x2B3370eE501B4a559b57D449569354196457D8Ab; - // USDC on HyperEVM Mainnet - address baseToken = 0xb88339CB7199b77E23DB6E890353E22632Ba630f; - uint32 coreIndex = 0; - bool canBeUsedForAccountActivation = true; - uint64 accountActivationFeeCore = 100000000; // 1 USDC - uint64 bridgeSafetyBufferCore = 1_000_000_00000000; // 1mil USDC (8 decimals) - - SponsoredCCTPDstPeriphery sponsoredCCTPDstPeriphery = new SponsoredCCTPDstPeriphery( - cctpMessageTransmitter, - deployer, - address(donationBox), - baseToken, - address(0) - ); - - console.log("SponsoredCCTPDstPeriphery deployed to:", address(sponsoredCCTPDstPeriphery)); - - donationBox.transferOwnership(address(sponsoredCCTPDstPeriphery)); - - console.log("DonationBox ownership transferred to:", address(sponsoredCCTPDstPeriphery)); - - vm.stopBroadcast(); - } -} diff --git a/script/115DeploySrcOFTPeriphery.sol b/script/115DeploySrcOFTPeriphery.sol deleted file mode 100644 index 7c9c4af01..000000000 --- a/script/115DeploySrcOFTPeriphery.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import { Script } from "forge-std/Script.sol"; -import { Test } from "forge-std/Test.sol"; -import { console } from "forge-std/console.sol"; - -import { DonationBox } from "../contracts/chain-adapters/DonationBox.sol"; -import { DeploymentUtils } from "./utils/DeploymentUtils.sol"; -import { SponsoredOFTSrcPeriphery } from "../contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol"; - -// Deploy: forge script script/115DeploySrcOFTPeriphery.sol:DepoySrcOFTPeriphery --rpc-url -vvvv -contract DepoySrcOFTPeriphery is Script, Test, DeploymentUtils { - function run() external { - console.log("Deploying SponsoredOFTSrcPeriphery..."); - console.log("Chain ID:", block.chainid); - - string memory deployerMnemonic = vm.envString("MNEMONIC"); - uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); - address deployer = vm.addr(deployerPrivateKey); - - vm.startBroadcast(deployerPrivateKey); - - SponsoredOFTSrcPeriphery srcOftPeriphery = new SponsoredOFTSrcPeriphery( - 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9, - 0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92, - 30110, - deployer - ); - - console.log("SponsoredOFTSrcPeriphery deployed to:", address(srcOftPeriphery)); - - vm.stopBroadcast(); - } -} diff --git a/script/116DeployDstOFTHandler.sol b/script/116DeployDstOFTHandler.sol deleted file mode 100644 index 7c87ce7cf..000000000 --- a/script/116DeployDstOFTHandler.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import { Script } from "forge-std/Script.sol"; -import { Test } from "forge-std/Test.sol"; -import { console } from "forge-std/console.sol"; - -import { DonationBox } from "../contracts/chain-adapters/DonationBox.sol"; -import { DeploymentUtils } from "./utils/DeploymentUtils.sol"; -import { DstOFTHandler } from "../contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol"; - -// Deploy: forge script script/116DeployDstOFTHandler.sol:DeployDstOFTHandler --rpc-url -vvvv -contract DeployDstOFTHandler is Script, Test, DeploymentUtils { - function run() external { - console.log("Deploying DstOFTHandler..."); - console.log("Chain ID:", block.chainid); - - string memory deployerMnemonic = vm.envString("MNEMONIC"); - uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); - - vm.startBroadcast(deployerPrivateKey); - - address oftEndpoint = getL2Address(block.chainid, "oftEndpoint"); - address ioft = 0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98; - - address baseToken = 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb; - uint32 coreIndex = 268; - bool canBeUsedForAccountActivation = true; - uint64 accountActivationFeeCore = 100000000; - uint64 bridgeSafetyBufferCore = 1_000_000_000_00000000; // 1billion USDC (8 decimals) - address multicallHandler = getL2Address(block.chainid, "multicallHandler"); - - DonationBox donationBox = new DonationBox(); - - DstOFTHandler dstOFTHandler = new DstOFTHandler( - oftEndpoint, - ioft, - address(donationBox), - baseToken, - multicallHandler - ); - console.log("DstOFTHandler deployed to:", address(dstOFTHandler)); - - donationBox.transferOwnership(address(dstOFTHandler)); - - console.log("DonationBox ownership transferred to:", address(dstOFTHandler)); - - vm.stopBroadcast(); - } -} diff --git a/script/DeployHyperliquidDepositHandler.s.sol b/script/DeployHyperliquidDepositHandler.s.sol new file mode 100644 index 000000000..2c0afa490 --- /dev/null +++ b/script/DeployHyperliquidDepositHandler.s.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Test } from "forge-std/Test.sol"; +import { console } from "forge-std/console.sol"; +import { HyperliquidDepositHandler } from "../contracts/handlers/HyperliquidDepositHandler.sol"; +import { HyperCoreLib } from "../contracts/libraries/HyperCoreLib.sol"; +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; + +// How to run: +// forge script script/DeployHyperliquidDepositHandler.s.sol:DeployHyperliquidDepositHandler --rpc-url hyperevm -vvvv + +contract DeployHyperliquidDepositHandler is Script, Test { + function run() external { + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + // Set the initial signer to the deployer's address. + address signer = vm.addr(deployerPrivateKey); + + address spokePool = 0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04; + + // Get the current chain ID + uint256 chainId = block.chainid; + + // Set up USDH as a supported token for this handler. + IERC20 usdh = IERC20(0x111111a1a0667d36bD57c0A9f569b98057111111); + uint64 usdhTokenIndex = 360; + uint256 usdhActivationFee = 1000000; // 1 USDH + int8 usdhDecimalDiff = -2; // USDH has 2 extra decimals on Core compared to EVM. + + vm.startBroadcast(deployerPrivateKey); + + HyperliquidDepositHandler hyperliquidDepositHandler = new HyperliquidDepositHandler(signer, spokePool); + + // Activate Handler account so it can write to CoreWriter by sending 1 core wei. + HyperCoreLib.transferERC20CoreToCore(usdhTokenIndex, address(hyperliquidDepositHandler), 1); + hyperliquidDepositHandler.addSupportedToken(address(usdh), usdhTokenIndex, 1000000, usdhDecimalDiff); + + // Log the deployed addresses + console.log("Chain ID:", chainId); + console.log("HyperliquidDepositHandler deployed to:", address(hyperliquidDepositHandler)); + console.log("Signer required to sign payloads for handleV3AcrossMessage:", signer); + console.log("USDH token index:", usdhTokenIndex); + console.log("USDH activation fee:", usdhActivationFee); + console.log("USDH decimal diff:", usdhDecimalDiff); + + vm.stopBroadcast(); + } +} diff --git a/script/mintburn/CreateSponsoredDeposit.sol b/script/mintburn/CreateSponsoredDeposit.sol deleted file mode 100644 index 44783ea1b..000000000 --- a/script/mintburn/CreateSponsoredDeposit.sol +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import { Script } from "forge-std/Script.sol"; -import { SponsoredOFTSrcPeriphery } from "../../contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol"; -import { Quote, SignedQuoteParams, UnsignedQuoteParams } from "../../contracts/periphery/mintburn/sponsored-oft/Structs.sol"; -import { AddressToBytes32 } from "../../contracts/libraries/AddressConverters.sol"; -import { ComposeMsgCodec } from "../../contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol"; -import { MinimalLZOptions } from "../../contracts/external/libraries/MinimalLZOptions.sol"; -import { IOFT, SendParam, MessagingFee } from "../../contracts/interfaces/IOFT.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; - -/// @notice Used in place of // import { QuoteSignLib } from "../contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol"; -/// just for the hashing function that works with a memory funciton argument -library DebugQuoteSignLib { - /// @notice Compute the keccak of all `SignedQuoteParams` fields. Accept memory arg - function hashMemory(SignedQuoteParams memory p) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - p.srcEid, - p.dstEid, - p.destinationHandler, - p.amountLD, - p.nonce, - p.deadline, - p.maxBpsToSponsor, - p.finalRecipient, - p.finalToken, - p.lzReceiveGasLimit, - p.lzComposeGasLimit - ) - ); - } -} - -// forge script script/mintburn/CreateSponsoredDeposit.sol:CreateSponsoredDeposit --rpc-url arbitrum -vvvv -contract CreateSponsoredDeposit is Script { - using AddressToBytes32 for address; - using SafeERC20 for IERC20; - using MinimalLZOptions for bytes; - - function run() external { - string memory deployerMnemonic = vm.envString("MNEMONIC"); - uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); - address deployer = vm.addr(deployerPrivateKey); // dev wallet - - // --- START CONFIG --- - uint32 srcEid = 30110; // Arbitrum - address srcPeriphery = 0x2C4413C70Fd1BDB109d7DFEE7310f4B692Dec381; - address token = 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9; // Arbitrum USDT - uint256 amountLD = 2 * 10 ** 6 + 5456; // 1 USDT (6 decimals) - bytes32 nonce = bytes32(uint256(12351)); // Replace with unique nonce per deposit - uint256 deadline = block.timestamp + 1 hours; - uint256 maxBpsToSponsor = 100; // 1% - uint256 maxUserSlippageBps = 50; // 0.5% - uint32 dstEid = 30367; // HyperEVM - address destinationHandler = 0x40ad479382Ad2a5c3061487A5094a677B00f6Cb0; - address finalRecipient = 0xD1A68de1d242B3b98A7230ba003c19f7cF90e360; // alternative dev wallet - address finalToken = 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb; // USDT0 @ HyperEVM - uint256 lzReceiveGasLimit = 200_000; - uint256 lzComposeGasLimit = 300_000; - address refundRecipient = deployer; // dev wallet - // --- END CONFIG --- - - SponsoredOFTSrcPeriphery srcPeripheryContract = SponsoredOFTSrcPeriphery(srcPeriphery); - require(srcPeripheryContract.signer() == deployer, "quote signer mismatch"); - - SignedQuoteParams memory signedParams = SignedQuoteParams({ - srcEid: srcEid, - dstEid: dstEid, - destinationHandler: destinationHandler.toBytes32(), - amountLD: amountLD, - nonce: nonce, - deadline: deadline, - maxBpsToSponsor: maxBpsToSponsor, - finalRecipient: finalRecipient.toBytes32(), - finalToken: finalToken.toBytes32(), - lzReceiveGasLimit: lzReceiveGasLimit, - lzComposeGasLimit: lzComposeGasLimit, - executionMode: 0, // DirectToCore mode - actionData: "" // Empty for DirectToCore mode - }); - - UnsignedQuoteParams memory unsignedParams = UnsignedQuoteParams({ - refundRecipient: refundRecipient, - maxUserSlippageBps: maxUserSlippageBps - }); - - Quote memory quote = Quote({ signedParams: signedParams, unsignedParams: unsignedParams }); - - bytes32 quoteDigest = DebugQuoteSignLib.hashMemory(signedParams); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(deployerPrivateKey, quoteDigest); - bytes memory signature = abi.encodePacked(r, s, v); - - MessagingFee memory fee = _quoteMessagingFee(srcPeripheryContract, quote); - - vm.startBroadcast(deployerPrivateKey); - - IERC20(token).forceApprove(srcPeriphery, amountLD); - - srcPeripheryContract.deposit{ value: fee.nativeFee }(quote, signature); - - vm.stopBroadcast(); - } - - function _quoteMessagingFee( - SponsoredOFTSrcPeriphery srcPeripheryContract, - Quote memory quote - ) internal view returns (MessagingFee memory) { - address oftMessenger = srcPeripheryContract.OFT_MESSENGER(); - - bytes memory composeMsg = ComposeMsgCodec._encode( - quote.signedParams.nonce, - quote.signedParams.deadline, - quote.signedParams.maxBpsToSponsor, - quote.unsignedParams.maxUserSlippageBps, - quote.signedParams.finalRecipient, - quote.signedParams.finalToken, - quote.signedParams.executionMode, - quote.signedParams.actionData - ); - - bytes memory extraOptions = MinimalLZOptions - .newOptions() - .addExecutorLzReceiveOption(uint128(quote.signedParams.lzReceiveGasLimit), uint128(0)) - .addExecutorLzComposeOption(uint16(0), uint128(quote.signedParams.lzComposeGasLimit), uint128(0)); - - SendParam memory sendParam = SendParam({ - dstEid: quote.signedParams.dstEid, - to: quote.signedParams.destinationHandler, - amountLD: quote.signedParams.amountLD, - minAmountLD: quote.signedParams.amountLD, - extraOptions: extraOptions, - composeMsg: composeMsg, - oftCmd: srcPeripheryContract.EMPTY_OFT_COMMAND() - }); - - return IOFT(oftMessenger).quoteSend(sendParam, false); - } -} diff --git a/script/mintburn/LimitOrderCalcCli.s.sol b/script/mintburn/LimitOrderCalcCli.s.sol new file mode 100644 index 000000000..cca991d5d --- /dev/null +++ b/script/mintburn/LimitOrderCalcCli.s.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import "forge-std/Script.sol"; +import "forge-std/console2.sol"; +import "./LimitOrderCalcUtils.sol"; + +/* +@notice Thin CLI wrapper exposing public functions for calling internal library helpers from the command line. +Usage example (Selling USDT0 into USDC): + +forge script script/mintburn/LimitOrderCalcCli.s.sol:LimitOrderCalcCli \ + --sig "calcLOAmounts(uint64,uint64,bool,uint64,uint8,uint8,uint8,uint8)" \ + 100000000 99990000 false 80 8 2 8 8 +*/ +contract LimitOrderCalcCli is Script { + // Suggested price with discount/premium + function calcSuggestedPrice( + uint64 spotX1e8, + bool isBuy, + uint32 suggestedDiscountBps + ) external view returns (uint64 limitPriceX1e8) { + return LimitOrderCalcUtils._getSuggestedPriceX1e8(spotX1e8, isBuy, suggestedDiscountBps); + } + + // Remaining LO budget given size/price/fees + function calcRemainingLOBudget( + uint64 pxX1e8, + uint64 szX1e8, + bool isBuy, + uint64 feePpm, + uint8 weiDecimalsTokenHave, + uint8 weiDecimalsTokenWant + ) external pure returns (uint64 budget) { + return + LimitOrderCalcUtils._calcRemainingLOBudget( + pxX1e8, + szX1e8, + isBuy, + feePpm, + weiDecimalsTokenHave, + weiDecimalsTokenWant + ); + } + + // Calculate limit order size given budget and desired price + function calcLOAmounts( + uint64 coreBudget, + uint64 pxX1e8, + bool isBuy, + uint64 feePpm, + uint8 tokenHaveWeiD, + uint8 tokenHaveSzD, + uint8 tokenWantWeiD, + uint8 tokenWantSzD + ) external pure returns (uint64 szX1e8, uint64 coreToSend, uint64 guaranteedCoreOut) { + return + LimitOrderCalcUtils._calcLOAmounts( + coreBudget, + pxX1e8, + isBuy, + feePpm, + tokenHaveWeiD, + tokenHaveSzD, + tokenWantWeiD, + tokenWantSzD + ); + } +} diff --git a/script/mintburn/LimitOrderCalcUtils.sol b/script/mintburn/LimitOrderCalcUtils.sol new file mode 100644 index 000000000..f26f1628e --- /dev/null +++ b/script/mintburn/LimitOrderCalcUtils.sol @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +library LimitOrderCalcUtils { + uint256 constant PPM_SCALAR = 10 ** 6; + uint256 constant BPS_SCALAR = 10 ** 4; + uint8 constant PX_D = 8; + + /// @notice Reads the current spot price from HyperLiquid and applies a configured suggested discount for faster execution + function _getSuggestedPriceX1e8( + uint64 spotX1e8, + bool isBuy, + uint32 suggestedDiscountBps + ) internal view returns (uint64 limitPriceX1e8) { + // Buy above spot, sell below spot + uint256 adjBps = isBuy ? (BPS_SCALAR + suggestedDiscountBps) : (BPS_SCALAR - suggestedDiscountBps); + limitPriceX1e8 = uint64((uint256(spotX1e8) * adjBps) / BPS_SCALAR); + } + + /************************************** + * LIMIT ORDER CALCULATION UTILS * + **************************************/ + + /// @notice Given the size and price of a limit order, returns the remaining `budget` that Limit order expects to spend + function _calcRemainingLOBudget( + uint64 pxX1e8, + uint64 szX1e8, + bool isBuy, + uint64 feePpm, + uint8 weiDecimalsTokenHave, + uint8 weiDecimalsTokenWant + ) internal pure returns (uint64 budget) { + uint8 quoteWeiD = isBuy ? weiDecimalsTokenHave : weiDecimalsTokenWant; + uint8 baseWeiD = isBuy ? weiDecimalsTokenWant : weiDecimalsTokenHave; + + if (isBuy) { + // We have quoteTokens. Estimate how many quoteTokens we are GUARANTEED to have had to enqueue the LO in the first place (proportional) + // qTR is quote tokens real. qTD quote token decimals. + // szX1e8 * pxX1e8 / 10 ** 8 = qTX1e8Net + // qTR * 10 ** 8 * (10 ** 6 - feePpm) / (10 ** 6 * 10 ** qTD) = qTX1e8Net + // qTR = szX1e8 * pxX1e8 * 10 ** 6 * 10 ** qTD / (10 ** 8 * 10 ** 8 * (10 ** 6 - feePpm)) + budget = uint64( + (uint256(szX1e8) * uint256(pxX1e8) * PPM_SCALAR * 10 ** (quoteWeiD)) / + (10 ** 16 * (PPM_SCALAR - feePpm)) + ); + } else { + // We have baseTokens. Convert `szX1e8` to base token budget. A simple decimals conversion here + budget = uint64((szX1e8 * 10 ** (baseWeiD)) / 10 ** 8); + } + } + + /** + * @notice The purpose of this function is best described by its return params. Given a budget and a price, determines + * size to set, tokens to send, and min amount received. + * @return szX1e8 size value to supply when sending a limit order to HyperCore + * @return coreToSend the number of tokens to send for this trade to suceed; <= coreBudget + * @return guaranteedCoreOut the ABSOLUTE MINIMUM that we're guaranteed to receive when the limit order fully settles + */ + function _calcLOAmounts( + uint64 coreBudget, + uint64 pxX1e8, + bool isBuy, + uint64 feePpm, + uint8 tokenHaveWeiD, + uint8 tokenHaveSzD, + uint8 tokenWantWeiD, + uint8 tokenWantSzD + ) internal pure returns (uint64 szX1e8, uint64 coreToSend, uint64 guaranteedCoreOut) { + if (isBuy) { + return + _calcLOAmountsBuy(coreBudget, pxX1e8, tokenHaveWeiD, tokenHaveSzD, tokenWantWeiD, tokenWantSzD, feePpm); + } else { + return + _calcLOAmountsSell( + coreBudget, + pxX1e8, + tokenWantWeiD, + tokenWantSzD, + tokenHaveWeiD, + tokenHaveSzD, + feePpm + ); + } + } + + /** + * @notice Given the quote budget and the price, this function calculates the size of the buy limit order to set + * as well as the minimum amount of out token to expect. This calculation is based on the HIP-1 spot trading formula. + * Source: https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-1-native-token-standard#spot-trading + * @param quoteBudget The budget of the quote in base token. + * @param pxX1e8 The price of the quote token in base token. + * @param quoteD The decimals of the quote token. + * @param quoteSz The size decimals of the quote token. + * @param baseD The decimals of the base token. + * @param baseSz The size decimals of the base token. + * @param feePpm The fee in ppm that is applied to the quote. + * @return szX1e8 The size of the limit order to set. + * @return tokensToSendCore The number of tokens to send for this trade to suceed. + * @return minAmountOutCore The minimum amount of out token to expect. + */ + function _calcLOAmountsBuy( + uint64 quoteBudget, + uint64 pxX1e8, + uint8 quoteD, + uint8 quoteSz, + uint8 baseD, + uint8 baseSz, + uint64 feePpm + ) internal pure returns (uint64 szX1e8, uint64 tokensToSendCore, uint64 minAmountOutCore) { + uint256 px = (pxX1e8 * 10 ** (PX_D + quoteSz)) / 10 ** (8 + baseSz); + // quoteD >= quoteSz always + uint256 sz = (quoteBudget * (PPM_SCALAR - feePpm) * 10 ** PX_D) / (PPM_SCALAR * px * 10 ** (quoteD - quoteSz)); + // baseD >= baseSz always + uint64 outBaseNet = uint64(sz * 10 ** (baseD - baseSz)); + szX1e8 = uint64((uint256(outBaseNet) * 10 ** 8) / 10 ** baseD); + tokensToSendCore = quoteBudget; + minAmountOutCore = outBaseNet; + } + + /** + * @notice Given the quote budget and the price, this function calculates the size of the sell limit order to set + * as well as the minimum amount of out token to expect. This calculation is based on the HIP-1 spot trading formula. + * Source: https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-1-native-token-standard#spot-trading + * @param baseBudget The budget of the quote in base token. + * @param pxX1e8 The price of the quote token in base token. + * @param quoteD The decimals of the quote token. + * @param quoteSz The size decimals of the quote token. + * @param baseD The decimals of the base token. + * @param baseSz The size decimals of the base token. + * @param feePpm The fee in ppm that is applied to the quote. + * @return szX1e8 The size of the limit order to set. + * @return tokensToSendCore The number of tokens to send for this trade to suceed. + * @return minAmountOutCore The minimum amount of out token to expect. + */ + function _calcLOAmountsSell( + uint64 baseBudget, + uint64 pxX1e8, + uint8 quoteD, + uint8 quoteSz, + uint8 baseD, + uint8 baseSz, + uint64 feePpm + ) internal pure returns (uint64 szX1e8, uint64 tokensToSendCore, uint64 minAmountOutCore) { + uint64 sz = uint64(baseBudget / 10 ** (baseD - baseSz)); + uint256 px = (pxX1e8 * 10 ** (PX_D + quoteSz)) / 10 ** (8 + baseSz); + + // quoteD >= quoteSz always + uint64 outQuoteGross = uint64((px * sz * 10 ** (quoteD - quoteSz)) / 10 ** PX_D); + uint64 outQuoteNet = uint64((outQuoteGross * (PPM_SCALAR - feePpm)) / PPM_SCALAR); + szX1e8 = uint64((sz * 10 ** 8) / 10 ** baseSz); + tokensToSendCore = baseBudget; + minAmountOutCore = outQuoteNet; + } +} diff --git a/script/mintburn/ReadHCoreTokenInfoUtil.s.sol b/script/mintburn/ReadHCoreTokenInfoUtil.s.sol new file mode 100644 index 000000000..6c480bf2f --- /dev/null +++ b/script/mintburn/ReadHCoreTokenInfoUtil.s.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { console } from "forge-std/console.sol"; +import { Constants } from "../utils/Constants.sol"; + +contract ReadHCoreTokenInfoUtil is Script { + string internal constant HCORE_JSON_PATH = "./script/mintburn/hypercore-tokens.json"; + + struct TokenJson { + uint256 index; + address evmAddress; + bool canBeUsedForAccountActivation; + uint256 accountActivationFeeCore; + uint256 bridgeSafetyBufferCore; + } + + function readToken(string memory tokenName) public view returns (TokenJson memory info) { + string memory json = vm.readFile(HCORE_JSON_PATH); + string memory base = string.concat(".", tokenName); + + info.index = vm.parseJsonUint(json, string.concat(base, ".index")); + + // evmAddress can be null in JSON; parseJsonAddress would revert. Try/catch and leave zero if unset. + try this._parseAddress(json, string.concat(base, ".evmAddress")) returns (address a) { + info.evmAddress = a; + } catch { + info.evmAddress = address(0); + } + + // Required fields for CoreTokenInfo + info.canBeUsedForAccountActivation = vm.parseJsonBool( + json, + string.concat(base, ".canBeUsedForAccountActivation") + ); + info.accountActivationFeeCore = vm.parseJsonUint(json, string.concat(base, ".accountActivationFeeCore")); + info.bridgeSafetyBufferCore = vm.parseJsonUint(json, string.concat(base, ".bridgeSafetyBufferCore")); + } + + function resolveEvmAddress(TokenJson memory info, uint256 /* chainId */) public pure returns (address evm) { + require(info.evmAddress != address(0), "evmAddress required in JSON"); + return info.evmAddress; + } + + // Wrapper to make parseJsonAddress usable in try/catch (external visibility) + function _parseAddress(string memory json, string memory key) external pure returns (address) { + return vm.parseJsonAddress(json, key); + } +} diff --git a/script/mintburn/SetAuthorizedPeriphery.s.sol b/script/mintburn/SetAuthorizedPeriphery.s.sol deleted file mode 100644 index 7832befa3..000000000 --- a/script/mintburn/SetAuthorizedPeriphery.s.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import { Script } from "forge-std/Script.sol"; -import { DstOFTHandler } from "../../contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol"; -import { AddressToBytes32 } from "../../contracts/libraries/AddressConverters.sol"; - -// forge script script/mintburn/SetAuthorizedPeriphery.s.sol:SetAuthorizedPeriphery --rpc-url hyper-evm -vvvv -contract SetAuthorizedPeriphery is Script { - using AddressToBytes32 for address; - - function run() external { - string memory deployerMnemonic = vm.envString("MNEMONIC"); - uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); - - // --- START CONFIG --- - uint32 srcEid = 30110; // Arbitrum - address srcPeriphery = 0x2C4413C70Fd1BDB109d7DFEE7310f4B692Dec381; - address dstHandlerAddress = 0x40ad479382Ad2a5c3061487A5094a677B00f6Cb0; - // --- END CONFIG --- - - DstOFTHandler dstHandler = DstOFTHandler(payable(dstHandlerAddress)); - - vm.startBroadcast(deployerPrivateKey); - - dstHandler.setAuthorizedPeriphery(srcEid, srcPeriphery.toBytes32()); - - vm.stopBroadcast(); - } -} diff --git a/script/mintburn/cctp/113DeploySponsoredCCTPSrcPeriphery.sol b/script/mintburn/cctp/113DeploySponsoredCCTPSrcPeriphery.sol new file mode 100644 index 000000000..87b500933 --- /dev/null +++ b/script/mintburn/cctp/113DeploySponsoredCCTPSrcPeriphery.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { console } from "forge-std/console.sol"; +import { DeploymentUtils } from "../../utils/DeploymentUtils.sol"; + +import { SponsoredCCTPSrcPeriphery } from "../../../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol"; + +// Deploy: forge script script/mintburn/cctp/113DeploySponsoredCCTPSrcPeriphery.sol:DeploySponsoredCCTPSrcPeriphery --rpc-url -vvvv +contract DeploySponsoredCCTPSrcPeriphery is DeploymentUtils { + function run() external { + console.log("Deploying SponsoredCCTPSrcPeriphery..."); + console.log("Chain ID:", block.chainid); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + _loadConfig("./script/mintburn/cctp/config.toml", true); + + address cctpTokenMessenger = config.get("cctpTokenMessenger").toAddress(); + uint32 sourceDomain = config.get("cctpDomainId").toUint32(); + + vm.startBroadcast(deployerPrivateKey); + + // TODO: use create2 for final deployment + SponsoredCCTPSrcPeriphery sponsoredCCTPSrcPeriphery = new SponsoredCCTPSrcPeriphery( + cctpTokenMessenger, + sourceDomain, + deployer + ); + + console.log("SponsoredCCTPSrcPeriphery deployed to:", address(sponsoredCCTPSrcPeriphery)); + } +} diff --git a/script/mintburn/cctp/114DeploySponsoredCCTPDstPeriphery.sol b/script/mintburn/cctp/114DeploySponsoredCCTPDstPeriphery.sol new file mode 100644 index 000000000..0faccfa92 --- /dev/null +++ b/script/mintburn/cctp/114DeploySponsoredCCTPDstPeriphery.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { console } from "forge-std/console.sol"; + +import { DeploymentUtils } from "../../utils/DeploymentUtils.sol"; +import { DonationBox } from "../../../contracts/chain-adapters/DonationBox.sol"; +import { SponsoredCCTPDstPeriphery } from "../../../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol"; + +// Deploy: forge script script/mintburn/cctp/114DeploySponsoredCCTPDstPeriphery.sol:DeploySponsoredCCTPDstPeriphery --rpc-url -vvvv +contract DeploySponsoredCCTPDstPeriphery is DeploymentUtils { + function run() external { + console.log("Deploying SponsoredCCTPDstPeriphery..."); + console.log("Chain ID:", block.chainid); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + _loadConfig("./script/mintburn/cctp/config.toml", true); + + address cctpMessageTransmitter = config.get("cctpMessageTransmitter").toAddress(); + + vm.startBroadcast(deployerPrivateKey); + + DonationBox donationBox = new DonationBox(); + console.log("DonationBox deployed to:", address(donationBox)); + + // USDC on HyperEVM + address baseToken = config.get("baseToken").toAddress(); + address multicallHandler = config.get("multicallHandler").toAddress(); + + // TODO: use create2 for final deployment + SponsoredCCTPDstPeriphery sponsoredCCTPDstPeriphery = new SponsoredCCTPDstPeriphery( + cctpMessageTransmitter, + deployer, + address(donationBox), + baseToken, + multicallHandler + ); + + console.log("SponsoredCCTPDstPeriphery deployed to:", address(sponsoredCCTPDstPeriphery)); + + donationBox.transferOwnership(address(sponsoredCCTPDstPeriphery)); + + console.log("DonationBox ownership transferred to:", address(sponsoredCCTPDstPeriphery)); + + vm.stopBroadcast(); + } +} diff --git a/script/mintburn/cctp/config.toml b/script/mintburn/cctp/config.toml new file mode 100644 index 000000000..c1ffd4da8 --- /dev/null +++ b/script/mintburn/cctp/config.toml @@ -0,0 +1,70 @@ +[base] +endpoint_url = "${NODE_URL_8453}" + +[base.address] +cctpTokenMessenger = "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d" +sponsoredCCTPSrcPeriphery = "0xA7A8d1efC1EE3E69999D370380949092251a5c20" +usdc = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" + +[base.uint] +cctpDomainId = 6 + +[arbitrum] +endpoint_url = "${NODE_URL_42161}" + +[arbitrum.address] +cctpTokenMessenger = "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d" +sponsoredCCTPSrcPeriphery = "0xce1FFE01eBB4f8521C12e74363A396ee3d337E1B" +usdc = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" + +[arbitrum.uint] +cctpDomainId = 3 + +# Arbitrum Sepolia +[arbitrum-sepolia] +endpoint_url = "${NODE_URL_421614}" + +[arbitrum-sepolia.address] +cctpTokenMessenger = "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA" + +[arbitrum-sepolia.uint] +cctpDomainId = 3 + + +# HyperEVM Mainnet +[999] +endpoint_url = "${NODE_URL_999}" + +[999.address] +cctpMessageTransmitter = "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64" +# USDC +baseToken = "0xb88339CB7199b77E23DB6E890353E22632Ba630f" +multicallHandler = "0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba" + +[999.uint] +cctpDomainId = 19 +coreIndex = 0 +accountActivationFeeCore = 100000000 +bridgeSafetyBufferCore = 100000000 + +[999.bool] +canBeUsedForAccountActivation = true + +# HyperEVM testnet +[998] +endpoint_url = "${NODE_URL_998}" + +[998.address] +cctpTokenMessenger = "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA" +cctpMessageTransmitter = "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275" +# USDC +baseToken = "0x2B3370eE501B4a559b57D449569354196457D8Ab" + +[998.uint] +cctpDomainId = 19 +coreIndex = 0 +accountActivationFeeCore = 100000000 +bridgeSafetyBufferCore = 100000000 + +[998.bool] +canBeUsedForAccountActivation = true \ No newline at end of file diff --git a/script/mintburn/cctp/createSponsoredDeposit.sol b/script/mintburn/cctp/createSponsoredDeposit.sol new file mode 100644 index 000000000..ea8af75f3 --- /dev/null +++ b/script/mintburn/cctp/createSponsoredDeposit.sol @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { console } from "forge-std/console.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { DeploymentUtils } from "../../utils/DeploymentUtils.sol"; +import { SponsoredCCTPSrcPeriphery } from "../../../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPSrcPeriphery.sol"; +import { ArbitraryEVMFlowExecutor } from "../../../contracts/periphery/mintburn/ArbitraryEVMFlowExecutor.sol"; +import { SponsoredCCTPInterface } from "../../../contracts/interfaces/SponsoredCCTPInterface.sol"; +import { AddressToBytes32 } from "../../../contracts/libraries/AddressConverters.sol"; + +interface IHyperSwapRouter { + struct ExactInputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 amountIn; + uint256 amountOutMinimum; + uint160 sqrtPriceLimitX96; + } + + function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); + + function multicall(uint256 deadline, bytes[] calldata data) external payable returns (bytes[] memory results); +} + +// Run: forge script script/mintburn/cctp/createSponsoredDeposit.sol:CreateSponsoredDeposit --rpc-url -vvvv +contract CreateSponsoredDeposit is DeploymentUtils { + using AddressToBytes32 for address; + + function run() external { + console.log("Creating sponsored deposit..."); + console.log("Chain ID:", block.chainid); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + _loadConfig("./script/mintburn/cctp/config.toml", true); + + address contractAddress = config.get("sponsoredCCTPSrcPeriphery").toAddress(); + SponsoredCCTPSrcPeriphery sponsoredCCTPSrcPeriphery = SponsoredCCTPSrcPeriphery(contractAddress); + + // Verify that the signer matches the contract's signer + require(sponsoredCCTPSrcPeriphery.signer() == deployer, "quote signer mismatch"); + + ArbitraryEVMFlowExecutor.CompressedCall[] + memory compressedCalls = new ArbitraryEVMFlowExecutor.CompressedCall[](2); + compressedCalls[0] = ArbitraryEVMFlowExecutor.CompressedCall({ + target: address(0xb88339CB7199b77E23DB6E890353E22632Ba630f), + callData: abi.encodeWithSelector( + IERC20.approve.selector, + // HyperSwap Router + address(0x6D99e7f6747AF2cDbB5164b6DD50e40D4fDe1e77), + 99990 + ) + }); + bytes[] memory hyperSwapRouterCalls = new bytes[](1); + hyperSwapRouterCalls[0] = abi.encodeWithSelector( + IHyperSwapRouter.exactInputSingle.selector, + IHyperSwapRouter.ExactInputSingleParams({ + tokenIn: address(0xb88339CB7199b77E23DB6E890353E22632Ba630f), + tokenOut: address(0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb), + fee: 100, + recipient: address(0x369232198fBBe6b42921A79B2D3ea4430d378c00), + amountIn: 99990, + amountOutMinimum: 0, + sqrtPriceLimitX96: 0 + }) + ); + compressedCalls[1] = ArbitraryEVMFlowExecutor.CompressedCall({ + target: address(0x6D99e7f6747AF2cDbB5164b6DD50e40D4fDe1e77), + callData: abi.encodeWithSelector( + IHyperSwapRouter.multicall.selector, + block.timestamp + 60 * 20, + hyperSwapRouterCalls + ) + }); + bytes memory actionData = abi.encode(compressedCalls); + bytes memory actionDataEmpty = abi.encode(new ArbitraryEVMFlowExecutor.CompressedCall[](0)); + bytes memory emptyActionData = ""; + + // Create the SponsoredCCTPQuote + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = SponsoredCCTPInterface.SponsoredCCTPQuote({ + sourceDomain: config.get("cctpDomainId").toUint32(), // Arbitrum CCTP domain + destinationDomain: 19, // HyperEVM CCTP domain + mintRecipient: address(0x83e245941BefbDe29682dF068Bcda006A804eb0C).toBytes32(), // Destination handler contract + amount: 10000, // 100 USDC (6 decimals) + burnToken: config.get("usdc").toAddress().toBytes32(), // USDC on Arbitrum + destinationCaller: address(0x83e245941BefbDe29682dF068Bcda006A804eb0C).toBytes32(), // Destination handler contract + maxFee: 1, // 0 max fee + minFinalityThreshold: 1000, // Minimum finality threshold + nonce: keccak256(abi.encodePacked(block.timestamp, deployer, vm.getNonce(deployer))), // Generate nonce + deadline: block.timestamp + 10800, // 3 hours from now + maxBpsToSponsor: 400, // 4% max sponsorship (400 basis points) + maxUserSlippageBps: 0, // 4% max user slippage (400 basis points) + finalRecipient: address(0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D).toBytes32(), // Final recipient + finalToken: address(0xb88339CB7199b77E23DB6E890353E22632Ba630f).toBytes32(), // USDC on HyperEVM + executionMode: uint8(SponsoredCCTPInterface.ExecutionMode.ArbitraryActionsToEVM), // DirectToCore mode + actionData: actionDataEmpty // Empty for DirectToCore mode + }); + + console.log("SponsoredCCTPQuote created:"); + console.log(" sourceDomain:", quote.sourceDomain); + console.log(" destinationDomain:", quote.destinationDomain); + console.log(" amount:", quote.amount); + console.log(" nonce:"); + console.logBytes32(quote.nonce); + console.log(" deadline:", quote.deadline); + console.logBytes(quote.actionData); + + // Create signature hash (same logic as TypeScript) + bytes32 hash1 = keccak256( + abi.encode( + quote.sourceDomain, + quote.destinationDomain, + quote.mintRecipient, + quote.amount, + quote.burnToken, + quote.destinationCaller, + quote.maxFee, + quote.minFinalityThreshold + ) + ); + + bytes32 hash2 = keccak256( + abi.encode( + quote.nonce, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalRecipient, + quote.finalToken, + quote.executionMode, + keccak256(quote.actionData) + ) + ); + + bytes32 typedDataHash = keccak256(abi.encode(hash1, hash2)); + console.log("Signature Hash:"); + console.logBytes32(typedDataHash); + + // Sign the hash using the deployer's private key + (uint8 v, bytes32 r, bytes32 s) = vm.sign(deployerPrivateKey, typedDataHash); + bytes memory signature = abi.encodePacked(r, s, v); + + console.log("Signature created"); + console.log("Signer:", deployer); + + // Call depositForBurn + vm.startBroadcast(deployerPrivateKey); + + console.log("Calling depositForBurn..."); + sponsoredCCTPSrcPeriphery.depositForBurn(quote, signature); + + console.log("Transaction completed successfully!"); + + vm.stopBroadcast(); + } +} diff --git a/script/mintburn/cctp/setUpTokens.sol b/script/mintburn/cctp/setUpTokens.sol new file mode 100644 index 000000000..696785304 --- /dev/null +++ b/script/mintburn/cctp/setUpTokens.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { console } from "forge-std/console.sol"; +import { Script } from "forge-std/Script.sol"; +import { Config } from "forge-std/Config.sol"; + +import { SponsoredCCTPDstPeriphery } from "../../../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol"; +import { HyperCoreFlowExecutor } from "../../../contracts/periphery/mintburn/HyperCoreFlowExecutor.sol"; + +contract setUpTokens is Script, Config { + function run() external { + console.log("Setting up tokens..."); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + _loadConfig("./script/mintburn/cctp/config.toml", true); + + address baseToken = config.get("baseToken").toAddress(); + uint32 coreIndex = config.get("coreIndex").toUint32(); + bool canBeUsedForAccountActivation = config.get("canBeUsedForAccountActivation").toBool(); + uint64 accountActivationFeeCore = config.get("accountActivationFeeCore").toUint64(); + uint64 bridgeSafetyBufferCore = config.get("bridgeSafetyBufferCore").toUint64(); + + // DonationBox@0x6f1Cd5f317a7228269EaB2b496313862de712CCb + // SponsoredCCTPDstPeriphery@0x06C61D54958a0772Ee8aF41789466d39FfeaeB13 + // HyperCoreFlowExecutor@0x82C8aB69e358F354eCb7Ff35239Cd326DeFf2072 + HyperCoreFlowExecutor dstPeriphery = HyperCoreFlowExecutor(payable(0xF962E0e485A5B9f8aDa9a438cEecc35c0020B6e7)); + + vm.startBroadcast(deployerPrivateKey); + console.log( + "Checking if sender has DEFAULT_ADMIN_ROLE:", + dstPeriphery.hasRole(dstPeriphery.DEFAULT_ADMIN_ROLE(), deployer) + ); + // dstPeriphery.setCoreTokenInfo( + // baseToken, + // coreIndex, + // canBeUsedForAccountActivation, + // accountActivationFeeCore, + // bridgeSafetyBufferCore + // ); + + console.log("Core token info set for:", baseToken); + console.log("Core index:", coreIndex); + console.log("Can be used for account activation:", canBeUsedForAccountActivation); + console.log("Account activation fee core:", accountActivationFeeCore); + console.log("Bridge safety buffer core:", bridgeSafetyBufferCore); + + vm.stopBroadcast(); + } +} diff --git a/script/mintburn/hypercore-tokens.json b/script/mintburn/hypercore-tokens.json new file mode 100644 index 000000000..5832fc3d9 --- /dev/null +++ b/script/mintburn/hypercore-tokens.json @@ -0,0 +1,38 @@ +{ + "usdc": { + "name": "USDC", + "index": 0, + "tokenId": "0x6d1e7cde53ba9467b783cb7c530ce054", + "szDecimals": 8, + "weiDecimals": 8, + "isCanonical": true, + "evmAddress": null, + "canBeUsedForAccountActivation": true, + "accountActivationFeeCore": 100000000, + "bridgeSafetyBufferCore": 100000000000000000 + }, + "usdt0": { + "name": "USDT0", + "index": 268, + "tokenId": "0x25faedc3f054130dbb4e4203aca63567", + "szDecimals": 2, + "weiDecimals": 8, + "isCanonical": false, + "evmAddress": "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb", + "canBeUsedForAccountActivation": true, + "accountActivationFeeCore": 100000000, + "bridgeSafetyBufferCore": 100000000000000000 + }, + "usdh": { + "name": "USDH", + "index": 360, + "tokenId": "0x54e00a5988577cb0b0c9ab0cb6ef7f4b", + "szDecimals": 2, + "weiDecimals": 8, + "isCanonical": false, + "evmAddress": "0x111111a1a0667d36bd57c0a9f569b98057111111", + "canBeUsedForAccountActivation": true, + "accountActivationFeeCore": 100000000, + "bridgeSafetyBufferCore": 100000000000000000 + } +} diff --git a/script/mintburn/oft/CreateSponsoredDeposit.s.sol b/script/mintburn/oft/CreateSponsoredDeposit.s.sol new file mode 100644 index 000000000..73c848e96 --- /dev/null +++ b/script/mintburn/oft/CreateSponsoredDeposit.s.sol @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Config } from "forge-std/Config.sol"; +import { Vm } from "forge-std/Vm.sol"; +import { SponsoredOFTSrcPeriphery } from "../../../contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol"; +import { Quote, SignedQuoteParams, UnsignedQuoteParams } from "../../../contracts/periphery/mintburn/sponsored-oft/Structs.sol"; +import { AddressToBytes32 } from "../../../contracts/libraries/AddressConverters.sol"; +import { ComposeMsgCodec } from "../../../contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol"; +import { MinimalLZOptions } from "../../../contracts/external/libraries/MinimalLZOptions.sol"; +import { IOFT, SendParam, MessagingFee, IOAppCore } from "../../../contracts/interfaces/IOFT.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +/// @notice Used in place of // import { QuoteSignLib } from "../contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol"; +/// just for the hashing function that works with a memory funciton argument +library DebugQuoteSignLib { + /// @notice Compute the keccak of all `SignedQuoteParams` fields. Accept memory arg + function hashMemory(SignedQuoteParams memory p) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + p.srcEid, + p.dstEid, + p.destinationHandler, + p.amountLD, + p.nonce, + p.deadline, + p.maxBpsToSponsor, + p.finalRecipient, + p.finalToken, + p.lzReceiveGasLimit, + p.lzComposeGasLimit, + p.executionMode, + keccak256(p.actionData) // Hash the actionData to keep signature size reasonable + ) + ); + } + + /// @notice Sign the quote using Foundry's Vm cheatcode and return concatenated bytes signature (r,s,v). + function signMemory(Vm vm, uint256 pk, SignedQuoteParams memory p) internal pure returns (bytes memory) { + bytes32 digest = hashMemory(p); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(pk, digest); + return abi.encodePacked(r, s, v); + } +} + +/* +Examples: + +- Simple transfer (no swap), sponsored (e.g. 1%): + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,uint256)" usdt0 1000000 100 --rpc-url arbitrum -vvvv + +- Simple transfer (no swap), non-sponsored: + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,uint256)" usdt0 1000000 0 --rpc-url arbitrum -vvvv + +- Simple transfer (no swap) with explicit recipient, sponsored: + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,address,uint256)" usdt0 1000000 0xRecipient 100 --rpc-url arbitrum -vvvv + +- Simple transfer (no swap) with explicit recipient, non-sponsored: + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,address,uint256)" usdt0 1000000 0xRecipient 0 --rpc-url arbitrum -vvvv + +- Swap flow (finalToken specified), sponsored (e.g. 1%): + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,uint256,address)" usdt0 1000000 100 0xFinalToken --rpc-url arbitrum -vvvv + +- Swap flow (finalToken specified), non-sponsored: + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,uint256,address)" usdt0 1000000 0 0xFinalToken --rpc-url arbitrum -vvvv + +- Swap flow with explicit recipient, sponsored: + forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,address,uint256,address)" usdt0 1000000 0xRecipient 100 0xFinalToken --rpc-url arbitrum -vvvv +*/ +contract CreateSponsoredDeposit is Script, Config { + using AddressToBytes32 for address; + using SafeERC20 for IERC20; + using MinimalLZOptions for bytes; + + struct DepositEnv { + address srcPeriphery; + address token; + address destinationHandler; + address dstToken; + uint32 srcEid; + uint32 dstEid; + } + + function run() external pure { + revert("see header for supported run signatures"); + } + + function run(string memory) external pure { + revert("see header for supported run signatures"); + } + + /// @notice Simple transfer entrypoint: finalToken defaults to the input token from config, recipient defaults to signer. + function run(string memory tokenName, uint256 amountLD, uint256 maxBpsToSponsor) external { + require(bytes(tokenName).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenName, ".toml")); + _loadConfigAndForks(configPath, false); + DepositEnv memory env = _resolveEnv(); + + string memory mnemonic = vm.envString("MNEMONIC"); + uint256 pk = vm.deriveKey(mnemonic, 0); + address deployer = vm.addr(pk); + + address recipient = deployer; + address finalToken = env.dstToken; // default to destination token: simple transfer + _execute(env, pk, deployer, amountLD, recipient, maxBpsToSponsor, finalToken); + } + + /// @notice Simple transfer entrypoint with explicit recipient (finalToken defaults to the input token). + function run(string memory tokenName, uint256 amountLD, address finalRecipient, uint256 maxBpsToSponsor) external { + require(bytes(tokenName).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenName, ".toml")); + _loadConfigAndForks(configPath, false); + DepositEnv memory env = _resolveEnv(); + + string memory mnemonic = vm.envString("MNEMONIC"); + uint256 pk = vm.deriveKey(mnemonic, 0); + address deployer = vm.addr(pk); + + address recipient = finalRecipient == address(0) ? deployer : finalRecipient; + address finalToken = env.dstToken; // simple transfer uses destination token + _execute(env, pk, deployer, amountLD, recipient, maxBpsToSponsor, finalToken); + } + + /// @notice Run with default finalRecipient = signer, and custom sponsorship and finalToken (swap) configuration. + function run(string memory tokenName, uint256 amountLD, uint256 maxBpsToSponsor, address finalToken) external { + require(bytes(tokenName).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenName, ".toml")); + _loadConfigAndForks(configPath, false); + DepositEnv memory env = _resolveEnv(); + + string memory mnemonic = vm.envString("MNEMONIC"); + uint256 pk = vm.deriveKey(mnemonic, 0); + address deployer = vm.addr(pk); + + address recipient = deployer; // default to signer address + _execute(env, pk, deployer, amountLD, recipient, maxBpsToSponsor, finalToken); + } + + /// @notice Run with an explicit finalRecipient, and custom sponsorship and finalToken (swap) configuration. + function run( + string memory tokenName, + uint256 amountLD, + address finalRecipient, + uint256 maxBpsToSponsor, + address finalToken + ) external { + require(bytes(tokenName).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenName, ".toml")); + _loadConfigAndForks(configPath, false); + DepositEnv memory env = _resolveEnv(); + + string memory mnemonic = vm.envString("MNEMONIC"); + uint256 pk = vm.deriveKey(mnemonic, 0); + address deployer = vm.addr(pk); + + address recipient = finalRecipient == address(0) ? deployer : finalRecipient; + _execute(env, pk, deployer, amountLD, recipient, maxBpsToSponsor, finalToken); + } + + function _execute( + DepositEnv memory env, + uint256 deployerPrivateKey, + address deployer, + uint256 amountLD, + address finalRecipient, + uint256 maxBpsToSponsor, + address finalToken + ) private { + SponsoredOFTSrcPeriphery srcPeripheryContract = SponsoredOFTSrcPeriphery(env.srcPeriphery); + require(srcPeripheryContract.signer() == deployer, "quote signer mismatch"); + + bytes32 nonce = bytes32(uint256(block.timestamp)); + uint256 deadline = block.timestamp + 1 hours; + uint256 maxUserSlippageBps = 200; // 2% + uint256 lzReceiveGasLimit = 200_000; + uint256 lzComposeGasLimit = 300_000; + address refundRecipient = deployer; + + SignedQuoteParams memory signedParams = SignedQuoteParams({ + srcEid: env.srcEid, + dstEid: env.dstEid, + destinationHandler: env.destinationHandler.toBytes32(), + amountLD: amountLD, + nonce: nonce, + deadline: deadline, + maxBpsToSponsor: maxBpsToSponsor, + finalRecipient: finalRecipient.toBytes32(), + finalToken: finalToken.toBytes32(), + lzReceiveGasLimit: lzReceiveGasLimit, + lzComposeGasLimit: lzComposeGasLimit, + executionMode: 0, + actionData: "" + }); + + UnsignedQuoteParams memory unsignedParams = UnsignedQuoteParams({ + refundRecipient: refundRecipient, + maxUserSlippageBps: maxUserSlippageBps + }); + + Quote memory quote = Quote({ signedParams: signedParams, unsignedParams: unsignedParams }); + + bytes memory signature = DebugQuoteSignLib.signMemory(vm, deployerPrivateKey, signedParams); + + MessagingFee memory fee = _quoteMessagingFee(srcPeripheryContract, quote); + + vm.startBroadcast(deployerPrivateKey); + IERC20(env.token).forceApprove(address(srcPeripheryContract), amountLD); + srcPeripheryContract.deposit{ value: fee.nativeFee }(quote, signature); + vm.stopBroadcast(); + } + + function _resolveEnv() internal returns (DepositEnv memory env) { + uint256 srcChainId = block.chainid; + uint256 srcForkId = forkOf[srcChainId]; + require(srcForkId != 0, "src chain not in config"); + vm.selectFork(srcForkId); + + env.srcPeriphery = config.get("src_periphery").toAddress(); + env.token = config.get("token").toAddress(); + require(env.srcPeriphery != address(0) && env.token != address(0), "missing src config"); + + // Resolve srcEid from the messenger endpoint + address srcMessenger = SponsoredOFTSrcPeriphery(env.srcPeriphery).OFT_MESSENGER(); + env.srcEid = IOAppCore(srcMessenger).endpoint().eid(); + + // Always use HyperEVM as destination for local testing + uint256 dstChainId = 999; + uint256 dstForkId = forkOf[dstChainId]; + require(dstForkId != 0, "dst chain not in config"); + vm.selectFork(dstForkId); + + env.destinationHandler = config.get("dst_handler").toAddress(); + env.dstToken = config.get("token").toAddress(); + address dstMessenger = config.get("oft_messenger").toAddress(); + require( + env.destinationHandler != address(0) && env.dstToken != address(0) && dstMessenger != address(0), + "missing dst config" + ); + env.dstEid = IOAppCore(dstMessenger).endpoint().eid(); + + // Switch back to source fork for execution + vm.selectFork(srcForkId); + } + + function _quoteMessagingFee( + SponsoredOFTSrcPeriphery srcPeripheryContract, + Quote memory quote + ) internal view returns (MessagingFee memory) { + address oftMessenger = srcPeripheryContract.OFT_MESSENGER(); + + bytes memory composeMsg = ComposeMsgCodec._encode( + quote.signedParams.nonce, + quote.signedParams.deadline, + quote.signedParams.maxBpsToSponsor, + quote.unsignedParams.maxUserSlippageBps, + quote.signedParams.finalRecipient, + quote.signedParams.finalToken, + quote.signedParams.executionMode, + quote.signedParams.actionData + ); + + bytes memory extraOptions = MinimalLZOptions + .newOptions() + .addExecutorLzReceiveOption(uint128(quote.signedParams.lzReceiveGasLimit), uint128(0)) + .addExecutorLzComposeOption(uint16(0), uint128(quote.signedParams.lzComposeGasLimit), uint128(0)); + + SendParam memory sendParam = SendParam({ + dstEid: quote.signedParams.dstEid, + to: quote.signedParams.destinationHandler, + amountLD: quote.signedParams.amountLD, + minAmountLD: quote.signedParams.amountLD, + extraOptions: extraOptions, + composeMsg: composeMsg, + oftCmd: srcPeripheryContract.EMPTY_OFT_COMMAND() + }); + + return IOFT(oftMessenger).quoteSend(sendParam, false); + } +} diff --git a/script/mintburn/oft/DeployDstHandler.s.sol b/script/mintburn/oft/DeployDstHandler.s.sol new file mode 100644 index 000000000..7c1cdd38a --- /dev/null +++ b/script/mintburn/oft/DeployDstHandler.s.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Test } from "forge-std/Test.sol"; +import { console } from "forge-std/console.sol"; + +import { DonationBox } from "../../../contracts/chain-adapters/DonationBox.sol"; +import { DeploymentUtils } from "../../utils/DeploymentUtils.sol"; +import { DstOFTHandler } from "../../../contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol"; +import { DstHandlerConfigLib } from "./DstHandlerConfigLib.s.sol"; +import { IOAppCore } from "../../../contracts/interfaces/IOFT.sol"; +import { PermissionedMulticallHandler } from "../../../contracts/handlers/PermissionedMulticallHandler.sol"; + +/* +forge script script/mintburn/oft/DeployDstHandler.s.sol:DeployDstOFTHandler \ + --sig "run(string)" usdt0 \ + --rpc-url hyperevm -vvvv --broadcast --verify + */ +contract DeployDstOFTHandler is Script, Test, DeploymentUtils, DstHandlerConfigLib { + function run(string memory tokenName) external { + console.log("Deploying DstOFTHandler..."); + console.log("Chain ID:", block.chainid); + + _loadTokenConfig(tokenName); + + // Ensure we deploy on the configured destination fork so subsequent configuration + // operates on the same fork where the contract exists. + uint256 dstForkId = forkOf[block.chainid]; + vm.selectFork(dstForkId); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + address ioft = config.get("oft_messenger").toAddress(); + address baseToken = config.get("token").toAddress(); + // address multicallHandler = _getOptionalAddress("multicall_handler"); + address oftEndpoint = address(IOAppCore(ioft).endpoint()); + require(oftEndpoint != address(0) && ioft != address(0) && baseToken != address(0), "config missing"); + + vm.startBroadcast(deployerPrivateKey); + + DonationBox donationBox = new DonationBox(); + // if (multicallHandler == address(0)) { + PermissionedMulticallHandler multicallHandler = new PermissionedMulticallHandler(deployer); + // } + DstOFTHandler dstOFTHandler = new DstOFTHandler( + oftEndpoint, + ioft, + address(donationBox), + baseToken, + address(multicallHandler) + ); + donationBox.transferOwnership(address(dstOFTHandler)); + // TODO: this won't work for multisig-owned `multicallHandler`s + multicallHandler.grantRole(multicallHandler.WHITELISTED_CALLER_ROLE(), address(dstOFTHandler)); + + console.log("DstOFTHandler deployed to:", address(dstOFTHandler)); + + vm.stopBroadcast(); + + // Persist the deployment address under this chain in TOML + config.set("dst_handler", address(dstOFTHandler)); + + // TODO: right, Foundry can't work with precompiles at all :( + // _configureCoreTokenInfo(tokenName, address(dstOFTHandler)); + _configureAuthorizedPeripheries(address(dstOFTHandler), deployerPrivateKey); + } + + /// @notice Returns a default zero address if not present + function _getOptionalAddress(string memory key) internal returns (address addr) { + try this.getAddress(key) returns (address value) { + addr = value; + } catch { + console.log("Optional not found: ", key); + } + } + + function getAddress(string memory key) external returns (address) { + return config.get(key).toAddress(); + } +} diff --git a/script/mintburn/oft/DeploySrcPeriphery.s.sol b/script/mintburn/oft/DeploySrcPeriphery.s.sol new file mode 100644 index 000000000..0d91eb084 --- /dev/null +++ b/script/mintburn/oft/DeploySrcPeriphery.s.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { Test } from "forge-std/Test.sol"; +import { console } from "forge-std/console.sol"; + +import { DeploymentUtils } from "./../../utils/DeploymentUtils.sol"; +import { SponsoredOFTSrcPeriphery } from "../../../contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol"; +import { IOAppCore } from "../../../contracts/interfaces/IOFT.sol"; + +/* +Example usage commands: + +# Deploy using token key (loads ./script/mintburn/oft/.toml) +forge script script/mintburn/oft/DeploySrcPeriphery.s.sol:DepoySrcOFTPeriphery \ + --sig "run(string,address)" usdt0 0xSigner \ + --rpc-url arbitrum --broadcast --verify -vvvv + +# Deploy using token key with explicit final owner +forge script script/mintburn/oft/DeploySrcPeriphery.s.sol:DepoySrcOFTPeriphery \ + --sig "run(string,address,address)" usdt0 0xSigner 0xFinalOwner \ + --rpc-url arbitrum --broadcast --verify -vvvv + +Note that both of these will update: +- the config + deployments file: ./script/mintburn/oft/.toml +- the `broadcast/` folder with the latest_run params (used by the precommit hooks to populate some generated artifacts file) + +*/ + +contract DepoySrcOFTPeriphery is Script, Test, DeploymentUtils { + enum OwnershipInstruction { + KeepDeployer, + Transfer, + Renounce + } + + struct OwnershipConfig { + bool useDefaultOwner; + address finalOwner; + } + + /// Deploy by token name key. Builds path: ./script/mintburn/oft/.toml + /// Final owner assumed to be the deployer. + function run(string memory tokenName, address signer) external { + require(bytes(tokenName).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenName, ".toml")); + OwnershipConfig memory ownershipConfig = OwnershipConfig({ useDefaultOwner: true, finalOwner: address(0) }); + _deployFromConfig(configPath, signer, ownershipConfig); + } + + /// Deploy by token name key with explicit final owner. + function run(string memory tokenName, address signer, address finalOwner) external { + require(bytes(tokenName).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenName, ".toml")); + OwnershipConfig memory ownershipConfig = OwnershipConfig({ useDefaultOwner: false, finalOwner: finalOwner }); + _deployFromConfig(configPath, signer, ownershipConfig); + } + + function _deploy( + address token, + address oftMessenger, + address signer, + OwnershipConfig memory ownershipConfig + ) internal returns (SponsoredOFTSrcPeriphery srcOftPeriphery) { + console.log("Deploying SponsoredOFTSrcPeriphery..."); + console.log("Chain ID:", block.chainid); + + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + require(token != address(0), "Token address cannot be zero"); + require(oftMessenger != address(0), "OFT messenger cannot be zero"); + require(signer != address(0), "Signer cannot be zero"); + + uint32 srcEid = IOAppCore(oftMessenger).endpoint().eid(); + + (OwnershipInstruction ownershipInstruction, address resolvedFinalOwner) = _resolveOwnership( + deployer, + ownershipConfig + ); + + console.log("Token:", token); + console.log("OFT messenger:", oftMessenger); + console.log("Source EID:", uint256(srcEid)); + console.log("Signer:", signer); + console.log("Deployer:", deployer); + + if (ownershipInstruction == OwnershipInstruction.Transfer) { + console.log("Final owner (post-transfer):", resolvedFinalOwner); + } else if (ownershipInstruction == OwnershipInstruction.Renounce) { + console.log("Final owner (post-transfer): "); + } else { + console.log("Final owner (post-transfer): deployer"); + } + + vm.startBroadcast(deployerPrivateKey); + + srcOftPeriphery = new SponsoredOFTSrcPeriphery(token, oftMessenger, srcEid, signer); + + console.log("SponsoredOFTSrcPeriphery deployed to:", address(srcOftPeriphery)); + + if (ownershipInstruction == OwnershipInstruction.Transfer) { + srcOftPeriphery.transferOwnership(resolvedFinalOwner); + console.log("Ownership transferred to:", resolvedFinalOwner); + } else if (ownershipInstruction == OwnershipInstruction.Renounce) { + srcOftPeriphery.renounceOwnership(); + console.log("Ownership renounced to address(0)"); + } else { + console.log("Ownership retained by deployer"); + } + + vm.stopBroadcast(); + return srcOftPeriphery; + } + + function _deployFromConfig( + string memory configPath, + address signer, + OwnershipConfig memory ownershipConfig + ) internal { + // Load config and enable write-back + _loadConfig(configPath, true); + + address token = config.get("token").toAddress(); + address oftMessenger = config.get("oft_messenger").toAddress(); + + require(token != address(0), "token not set"); + require(oftMessenger != address(0), "oft_messenger not set"); + require(signer != address(0), "signer not set"); + + SponsoredOFTSrcPeriphery srcOftPeriphery = _deploy(token, oftMessenger, signer, ownershipConfig); + + // Persist the deployment address under this chain in TOML + config.set("src_periphery", address(srcOftPeriphery)); + } + + function _resolveOwnership( + address deployer, + OwnershipConfig memory config + ) internal pure returns (OwnershipInstruction instruction, address finalOwner) { + if (config.useDefaultOwner) { + return (OwnershipInstruction.KeepDeployer, deployer); + } + + if (config.finalOwner == address(0)) { + return (OwnershipInstruction.Renounce, address(0)); + } + + if (config.finalOwner == deployer) { + return (OwnershipInstruction.KeepDeployer, deployer); + } + + return (OwnershipInstruction.Transfer, config.finalOwner); + } +} diff --git a/script/mintburn/oft/DstHandlerConfigLib.s.sol b/script/mintburn/oft/DstHandlerConfigLib.s.sol new file mode 100644 index 000000000..932d6fdf4 --- /dev/null +++ b/script/mintburn/oft/DstHandlerConfigLib.s.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Config } from "forge-std/Config.sol"; +import { console } from "forge-std/console.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; + +import { ReadHCoreTokenInfoUtil } from "../../mintburn/ReadHCoreTokenInfoUtil.s.sol"; +import { DstOFTHandler } from "../../../contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol"; +import { HyperCoreFlowExecutor } from "../../../contracts/periphery/mintburn/HyperCoreFlowExecutor.sol"; +import { IOAppCore, IEndpoint } from "../../../contracts/interfaces/IOFT.sol"; +import { AddressToBytes32 } from "../../../contracts/libraries/AddressConverters.sol"; + +/// @notice Shared helper for configuring `DstOFTHandler` instances using TOML and JSON metadata. +abstract contract DstHandlerConfigLib is Config { + using AddressToBytes32 for address; + + function _loadTokenConfig(string memory tokenKey) internal { + require(bytes(tokenKey).length != 0, "token key required"); + string memory configPath = string(abi.encodePacked("./script/mintburn/oft/", tokenKey, ".toml")); + _loadConfigAndForks(configPath, true); + } + + function _configureCoreTokenInfo(string memory tokenName, address dstHandlerAddress) internal { + require(dstHandlerAddress != address(0), "dst handler not set"); + + ReadHCoreTokenInfoUtil reader = new ReadHCoreTokenInfoUtil(); + ReadHCoreTokenInfoUtil.TokenJson memory info = reader.readToken(tokenName); + address tokenAddr = reader.resolveEvmAddress(info, block.chainid); + require(tokenAddr != address(0), "token addr missing"); + + console.log("Configuring CoreTokenInfo for", tokenName); + console.log("Dst handler:", dstHandlerAddress); + console.log("Token:", tokenAddr); + + HyperCoreFlowExecutor(dstHandlerAddress).setCoreTokenInfo( + tokenAddr, + uint32(info.index), + info.canBeUsedForAccountActivation, + uint64(info.accountActivationFeeCore), + uint64(info.bridgeSafetyBufferCore) + ); + } + + function _configureAuthorizedPeripheries(address dstHandlerAddress, uint256 configurerPrivateKey) internal { + require(dstHandlerAddress != address(0), "dst handler not set"); + + uint256[] memory chainIdList = config.getChainIds(); + + uint256 dstChainId = block.chainid; + uint256 dstForkId = forkOf[dstChainId]; + require(dstForkId != 0, "dst chain not in config"); + // Ensure active fork is the destination before instantiating the typed contract + vm.selectFork(dstForkId); + DstOFTHandler handler = DstOFTHandler(payable(dstHandlerAddress)); + + for (uint256 i = 0; i < chainIdList.length; i++) { + uint256 srcChainId = chainIdList[i]; + if (srcChainId == dstChainId) { + continue; + } + address srcPeriphery = config.get(srcChainId, "src_periphery").toAddress(); + address oftMessenger = config.get(srcChainId, "oft_messenger").toAddress(); + if (srcPeriphery == address(0) || oftMessenger == address(0)) { + console.log( + "Skipping authorizing periphery for chain", + srcChainId, + "srcPeriphery or oftMessenger not set" + ); + continue; + } + + uint256 srcForkId = forkOf[srcChainId]; + require(srcForkId != 0, "src chain not in config"); + + // Switch to calling src chain contracts to get srcEid + vm.selectFork(srcForkId); + + uint32 srcEid; + try IOAppCore(oftMessenger).endpoint() returns (IEndpoint ep) { + srcEid = ep.eid(); + } catch { + continue; + } + + // Switch to calling dst chain contracts to read dst chain state + update periphery if needed + vm.selectFork(dstForkId); + + bytes32 expected = srcPeriphery.toBytes32(); + if (handler.authorizedSrcPeripheryContracts(uint64(srcEid)) != expected) { + console.log("Authorizing periphery", srcPeriphery, "for srcEid", uint256(srcEid)); + vm.startBroadcast(configurerPrivateKey); + handler.setAuthorizedPeriphery(srcEid, expected); + vm.stopBroadcast(); + } + } + } +} diff --git a/script/mintburn/oft/README.md b/script/mintburn/oft/README.md new file mode 100644 index 000000000..dcebcecad --- /dev/null +++ b/script/mintburn/oft/README.md @@ -0,0 +1,118 @@ +### Deployment process. It's IMPORTANT to perform all steps, otherwise funds WILL get lost + +0. **Deploy all wanted Src periphery contracts** + +See `./DeploySrcPeriphery.s.sol` for commands + +1. **Run deployment script** + +``` +forge script script/mintburn/oft/DeployDstHandler.s.sol:DeployDstOFTHandler \ + --sig "run(string)" usdt0 \ + --rpc-url hyperevm -vvvv --broadcast --verify +``` + +Make sure all the src peripheries are correctly configured (currently addrs for this config are taken from usdt0.toml) + +2. **Configure baseToken** + Example with cast for USDT0: + +``` +cast send $DEPLOYED_DST_OFT "setCoreTokenInfo(address,uint32,bool,uint64,uint64)" 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb 268 true 100000000 100000000000000 --rpc-url hyperevm --account dev +``` + +2.(a) Configure additional tokens, like USDC: + +CoreTokenInfo: + +``` +cast send $DEPLOYED_DST_OFT "setCoreTokenInfo(address,uint32,bool,uint64,uint64)" 0xb88339CB7199b77E23DB6E890353E22632Ba630f 0 true 100000000 100000000000000 --rpc-url hyperevm --account dev +``` + +(before adding FinalTokenInfo): + +predict SwapHandler address: + +cast parse-bytes32-address $(cast call $DEPLOYED_DST_OFT "predictSwapHandler(address)" 0xb88339CB7199b77E23DB6E890353E22632Ba630f --rpc-url hyperevm) + +Activate HyperLiquid account for that (see step 3 for an example). + +And FinalTokenInfo: + +``` +cast send $DEPLOYED_DST_OFT "setFinalTokenInfo(address,uint32,bool,uint32,uint32,address)" 0xb88339CB7199b77E23DB6E890353E22632Ba630f 166 false 140 2 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb --rpc-url hyperevm --account dev +``` + +3. **Activate $DEPLOYED_DST_OFT account on HyperCore** + +Go to Hyperliquid UI and set 1 wei of USDC to it. Check activation status with: + +``` +curl -s https://api.hyperliquid.xyz/info \ + -H 'content-type: application/json' \ + -d "{\"type\":\"userRole\",\"user\":\"$DEPLOYED_DST_OFT\"}" +``` + +You want to see `{"role":"user"}%` as a response. That means that the account was activated. Otherwise, would say `{"role":"missing"}%` + +4. Transfer ownership to multisig / trusted account / renounce ownership of DstOftHandler. TODO: could be enabled in the script if we could set core token info from there ... Foundry doesn't let us interact with precompiles from scripts (can't skip sim) + +5. Test that the baseToken + core accounts are functioning correctly by calling the `CreateSponsoredDeposit` script and waiting for tokens to show up on the Hyperliquid balance of the dev wallet. + +``` +forge script script/mintburn/oft/CreateSponsoredDeposit.s.sol:CreateSponsoredDeposit \ + --sig "run(string,uint256,uint256)" usdt0 1000000 100 --rpc-url arbitrum -vvvv --broadcast +``` + +### Misc + +Grant `PERMISSIONED_BOT_ROLE`: + +``` +cast send $DEPLOYED_DST_OFT "grantRole(bytes32,address)" 0x5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef6 $ROLE_RECIPIENT --rpc-url hyperevm --account dev +``` + +Open orders for an `ADDR`: + +``` +ADDR=0xe199ce8af95937e3a0c61d437b0cbf2481c81c8c +curl -s https://api.hyperliquid.xyz/info \ + -H 'content-type: application/json' \ + -d "{\"type\":\"openOrders\",\"user\":\"$ADDR\"}" +``` + +Spot balances for an addr: + +``` +curl -s https://api.hyperliquid.xyz/info \ + -H 'content-type: application/json' \ + -d "{\"type\":\"spotClearinghouseState\",\"user\":\"$ADDR\"}" +``` + +Activate user account: + +``` +cast send $DEPLOYED_DST_OFT "activateUserAccount(bytes32,address,address)" 0x00000000000000000000000000000000000000000000000000000000690D6445 0x67C6Cf5288E5D8Bc474126949B3A50Cfe5512AF9 0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb --rpc-url hyperevm --account dev +``` + +Calculations + limit order submission: + +Calc size based on coreIn + price + +``` +forge script script/mintburn/LimitOrderCalcCli.s.sol:LimitOrderCalcCli \ + --sig "calcLOAmounts(uint64,uint64,bool,uint64,uint8,uint8,uint8,uint8)" \ + 1033000000 99970000 false 140 8 2 8 8 +``` + +Submit Limit order: + +``` +cast send $DEPLOYED_DST_OFT "submitLimitOrderFromBot(address,uint64,uint64,uint128)" 0xb88339CB7199b77E23DB6E890353E22632Ba630f 99990000 100000000 1 --rpc-url hyperevm --account dev +``` + +Finalize a swap flow: + +``` +cast send $DEPLOYED_DST_OFT "finalizeSwapFlows(address,bytes32[],uint64[])" 0xb88339CB7199b77E23DB6E890353E22632Ba630f "[0x00000000000000000000000000000000000000000000000000000000690D3DBE]" "[122576511]" --rpc-url hyperevm --account dev +``` diff --git a/script/mintburn/oft/UpdateAuthorizedPeripheries.s.sol b/script/mintburn/oft/UpdateAuthorizedPeripheries.s.sol new file mode 100644 index 000000000..ff406ce67 --- /dev/null +++ b/script/mintburn/oft/UpdateAuthorizedPeripheries.s.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { console } from "forge-std/console.sol"; + +import { DstHandlerConfigLib } from "./DstHandlerConfigLib.s.sol"; + +/* +forge script script/mintburn/oft/UpdateAuthorizedPeripheries.s.sol:UpdateAuthorizedPeripheries \ + --sig "run(string)" usdt0 \ + --rpc-url hyperevm --broadcast -vvvv + */ +contract UpdateAuthorizedPeripheries is Script, DstHandlerConfigLib { + function run() external pure { + revert("Missing args. Use run(string tokenKey)"); + } + + function run(string memory tokenKey) external { + require(bytes(tokenKey).length != 0, "args"); + _run(tokenKey); + } + + function _run(string memory tokenKey) internal { + _loadTokenConfig(tokenKey); + + // Resolve deployer + string memory deployerMnemonic = vm.envString("MNEMONIC"); + uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0); + address deployer = vm.addr(deployerPrivateKey); + + address dstHandlerAddress = config.get("dst_handler").toAddress(); + require(dstHandlerAddress != address(0), "dst_handler not set"); + + console.log("Updating authorized peripheries on DstOFTHandler..."); + console.log("Dst chain:", block.chainid); + console.log("Deployer:", deployer); + console.log("Dst handler:", dstHandlerAddress); + + _configureAuthorizedPeripheries(dstHandlerAddress, deployerPrivateKey); + } +} diff --git a/script/mintburn/oft/usdt0.toml b/script/mintburn/oft/usdt0.toml new file mode 100644 index 000000000..e04d31b1a --- /dev/null +++ b/script/mintburn/oft/usdt0.toml @@ -0,0 +1,20 @@ +[1] +endpoint_url = "${NODE_URL_1}" + +[1.address] +token = "0xdAC17F958D2ee523a2206206994597C13D831ec7" +oft_messenger = "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee" +src_periphery = "0x4607BceaF7b22cb0c46882FFc9fAB3c6efe66e5a" + +[arbitrum.address] +token = "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9" +oft_messenger = "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92" +src_periphery = "0x2ac5Ee3796E027dA274fbDe84c82173a65868940" + +[999] +endpoint_url = "${NODE_URL_999}" + +[999.address] +token = "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb" +oft_messenger = "0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98" +dst_handler = "0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461" diff --git a/script/utils/DeploymentUtils.sol b/script/utils/DeploymentUtils.sol index 11b2fcc7d..b9cd1d3e9 100644 --- a/script/utils/DeploymentUtils.sol +++ b/script/utils/DeploymentUtils.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.0; import { Script } from "forge-std/Script.sol"; import { Test } from "forge-std/Test.sol"; import { console } from "forge-std/console.sol"; +import { Config } from "forge-std/Config.sol"; import { Upgrades, Core, UnsafeUpgrades } from "@openzeppelin/foundry-upgrades/src/LegacyUpgrades.sol"; import { Options } from "@openzeppelin/foundry-upgrades/src/Options.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts-v4/proxy/ERC1967/ERC1967Proxy.sol"; @@ -15,7 +16,7 @@ import { DeployedAddresses } from "./DeployedAddresses.sol"; * @notice Foundry smart contract script that provides deployment utilities for Across Protocol contracts * @dev This contract implements the equivalent functionality of utils.hre.ts for Foundry scripts */ -contract DeploymentUtils is Script, Test, Constants, DeployedAddresses { +contract DeploymentUtils is Script, Test, Constants, DeployedAddresses, Config { // Struct to hold deployment information struct DeploymentInfo { address hubPool; diff --git a/scripts/hyperliquidDepositHandler.ts b/scripts/hyperliquidDepositHandler.ts new file mode 100644 index 000000000..16d7e9838 --- /dev/null +++ b/scripts/hyperliquidDepositHandler.ts @@ -0,0 +1,176 @@ +// @notice Utility script to interact with HyperliquidDepositHandler contract. +// @dev Run with `yarn hardhat run ./scripts/hyperliquidDepositHandler.ts --network hyperevm` + +import { getNodeUrl } from "../utils"; +import { Contract, ethers } from "../utils/utils"; +import { hre } from "../utils/utils.hre"; + +async function main() { + const chainId = parseInt(await hre.getChainId()); + const nodeUrl = getNodeUrl(chainId); + const wallet = ethers.Wallet.fromMnemonic((hre.network.config.accounts as any).mnemonic); + console.log(`Connected to node ${nodeUrl} for chain ${chainId}`); + const signer = wallet.connect(new ethers.providers.JsonRpcProvider(nodeUrl)); + + // Deposit USDH to spot: + const amountToDeposit = ethers.utils.parseUnits("1", 6); + const depositHandler = new Contract( + "0x861E127036B28D32f3777B4676F6bbb9e007d195", + [ + { + inputs: [ + { + internalType: "address", + name: "token", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "address", + name: "user", + type: "address", + }, + ], + name: "depositToHypercore", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "evmAmount", type: "uint256" }, + { internalType: "address", name: "user", type: "address" }, + ], + name: "sweepERC20ToUser", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "address", name: "user", type: "address" }, + ], + name: "sweepDonationBoxFundsToUser", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint64", name: "coreAmount", type: "uint64" }, + { internalType: "address", name: "user", type: "address" }, + ], + name: "sweepCoreFundsToUser", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "donationBox", + outputs: [{ internalType: "contract DonationBox", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + ], + signer + ); + const usdh = new Contract( + "0x111111a1a0667d36bD57c0A9f569b98057111111", + [ + { + inputs: [ + { + internalType: "address", + name: "guy", + type: "address", + }, + { + internalType: "uint256", + name: "wad", + type: "uint256", + }, + ], + name: "approve", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "to", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + ], + name: "transfer", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "nonpayable", + type: "function", + }, + ], + signer + ); + + // Approve (1000 x amountToDeposit) USDH to be spent by the deposit handler. + const approvalTxn = await usdh.approve(depositHandler.address, amountToDeposit.mul(1000)); + const receipt = await approvalTxn.wait(); + console.log(`approval:`, receipt); + + // Fund donation box with 1 activation fee + 1 wei. + const donationBox = await depositHandler.donationBox(); + const transferTxn = await usdh.transfer(donationBox, amountToDeposit.add(1)); + const transferReceipt = await transferTxn.wait(); + console.log(`donationBox funding:`, transferReceipt); + + // // Sweep 1 USDH from the deposit handler on HyperEVM to a user's address. + // const txn = await depositHandler.sweepERC20ToUser( + // usdh.address, + // "1000001", + // "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", + // ); + // console.log(`sweepERC20ToUser`, await txn.wait()); + + // // Sweep 1 USDH from the deposit handler on HyperCore to a user's address. + // const txn = await depositHandler.sweepCoreFundsToUser( + // usdh.address, + // "100000001", // USDH has 8 decimals on Core. + // "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", + // ); + // console.log(`sweepCoreFundsToUser`, await txn.wait()); + + // // Sweep 1 USDH from the deposit handler's donation box on HyperEVM to a user's address. + // const txn = await depositHandler.sweepDonationBoxFundsToUser( + // usdh.address, + // "1000000", + // "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", + // ); + // console.log(`sweepDonationBoxFundsToUser`, await txn.wait()); + + // Deposit 1 USDH from the user's address on HyperEVM to the user's account on HyperCore. + // If user account needs to be activated, the deposit handler will pull 1 USDH + 1 wei from its donation box and + // activate the user's account. + const txn = await depositHandler.depositToHypercore(usdh.address, amountToDeposit, signer.address); + console.log(`depositToHypercore`, await txn.wait()); +} + +main().then( + () => process.exit(0), + (error) => { + console.log(error); + process.exit(1); + } +); diff --git a/test/evm/foundry/local/HyperCoreFlowExecutor.t.sol b/test/evm/foundry/local/HyperCoreFlowExecutor.t.sol new file mode 100644 index 000000000..aa8e1f3e9 --- /dev/null +++ b/test/evm/foundry/local/HyperCoreFlowExecutor.t.sol @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; + +import { BaseSimulatorTest } from "./external/hyper-evm-lib/test/BaseSimulatorTest.sol"; +import { PrecompileLib } from "./external/hyper-evm-lib/src/PrecompileLib.sol"; +import { HyperCoreLib } from "../../../../contracts/libraries/HyperCoreLib.sol"; + +import { DonationBox } from "../../../../contracts/chain-adapters/DonationBox.sol"; +import { MockERC20 } from "../../../../contracts/test/MockERC20.sol"; +import { IHyperCoreFlowExecutor } from "../../../../contracts/test/interfaces/IHyperCoreFlowExecutor.sol"; +import { CommonFlowParams } from "../../../../contracts/periphery/mintburn/Structs.sol"; +import { HyperCoreFlowExecutor } from "../../../../contracts/periphery/mintburn/HyperCoreFlowExecutor.sol"; +import { BaseModuleHandler } from "../../../../contracts/periphery/mintburn/BaseModuleHandler.sol"; + +contract TestHyperCoreHandler is BaseModuleHandler { + constructor(address donationBox, address baseToken) BaseModuleHandler(donationBox, baseToken, DEFAULT_ADMIN_ROLE) { + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _grantRole(PERMISSIONED_BOT_ROLE, msg.sender); + _grantRole(FUNDS_SWEEPER_ROLE, msg.sender); + } + + function callExecuteSimpleTransferFlow(CommonFlowParams memory params) external authorizeFundedFlow { + _delegateToHyperCore(abi.encodeWithSelector(HyperCoreFlowExecutor.executeSimpleTransferFlow.selector, params)); + } + + function callFallbackHyperEVMFlow(CommonFlowParams memory params) external authorizeFundedFlow { + _delegateToHyperCore(abi.encodeWithSelector(HyperCoreFlowExecutor.fallbackHyperEVMFlow.selector, params)); + } + + function callSetCoreTokenInfo( + address token, + uint32 coreIndex, + bool canBeUsedForAccountActivation, + uint64 accountActivationFeeCore, + uint64 bridgeSafetyBufferCore + ) external { + _delegateToHyperCore( + abi.encodeWithSelector( + HyperCoreFlowExecutor.setCoreTokenInfo.selector, + token, + coreIndex, + canBeUsedForAccountActivation, + accountActivationFeeCore, + bridgeSafetyBufferCore + ) + ); + } +} + +contract HyperCoreFlowExecutorTest is BaseSimulatorTest { + using PrecompileLib for address; + + TestHyperCoreHandler internal handler; + DonationBox internal donationBox; + MockERC20 internal token; + + address internal finalRecipient; + bytes32 internal constant QUOTE_NONCE = keccak256("quote-1"); + uint32 internal constant CORE_INDEX = 0; // USDC index per BaseSimulatorTest + + function setUp() public override { + super.setUp(); + + finalRecipient = makeAddr("finalRecipient"); + + token = new MockERC20(); + donationBox = new DonationBox(); + handler = new TestHyperCoreHandler(address(donationBox), address(token)); + + // Make the handler the owner of DonationBox so it can withdraw during sponsorship + vm.prank(donationBox.owner()); + donationBox.transferOwnership(address(handler)); + + // Link token to HyperCore and set token info in the module via delegatecall + // name, weiDecimals=8, szDecimals=8, evmExtraWeiDecimals=0 + hyperCore.forceTokenInfo(CORE_INDEX, "MOCK", address(token), 8, 8, 0); + handler.callSetCoreTokenInfo(address(token), CORE_INDEX, true, 1e6, 1e6); + + // Ensure recipient has an active HyperCore account for sponsored simple transfer path + hyperCore.forceAccountActivation(finalRecipient); + } + + function _defaultParams( + uint256 amountInEVM, + uint256 maxBpsToSponsor, + uint256 extraFeesIncurred + ) internal view returns (CommonFlowParams memory) { + return + CommonFlowParams({ + amountInEVM: amountInEVM, + quoteNonce: QUOTE_NONCE, + finalRecipient: finalRecipient, + finalToken: address(token), + maxBpsToSponsor: maxBpsToSponsor, + extraFeesIncurred: extraFeesIncurred + }); + } + + function testExecuteSimpleTransferFlow_Sponsored_EmitsAndUsesDonation() public { + uint256 amountIn = 1_000e6; + uint256 extraFees = 50e6; // 5% of amount + uint256 maxBps = 500; // 5% + + // Ensure bridge has enough liquidity so safety check passes + address bridgeAddr = HyperCoreLib.toAssetBridgeAddress(CORE_INDEX); + hyperCore.forceSpot(bridgeAddr, CORE_INDEX, uint64(10_000_000e8)); // large buffer + + // Handler must hold user funds that arrived via bridge + deal(address(token), address(handler), amountIn, true); + // Donation box must hold enough to sponsor the extra fees + deal(address(token), address(donationBox), extraFees, true); + + CommonFlowParams memory params = _defaultParams(amountIn, maxBps, extraFees); + + // Expect the SimpleTransferFlowCompleted event with sponsorship equal to extraFees (capped by bps) + vm.expectEmit(address(handler)); + emit HyperCoreFlowExecutor.SimpleTransferFlowCompleted( + // delegated event emitted by handler context + params.quoteNonce, + params.finalRecipient, + params.finalToken, + params.amountInEVM, + params.extraFeesIncurred, + extraFees + ); + handler.callExecuteSimpleTransferFlow(params); + } + + function testExecuteSimpleTransferFlow_Unsponsored_NotActivated_FallsBackToEVM() public { + // Make recipient not activated + address unactivated = makeAddr("unactivated"); + + uint256 amountIn = 2_000e6; + uint256 extraFees = 25e6; // ignored as maxBpsToSponsor=0 + uint256 maxBps = 0; + + // Handler holds bridged funds to forward on EVM fallback + deal(address(token), address(handler), amountIn, true); + + CommonFlowParams memory params = CommonFlowParams({ + amountInEVM: amountIn, + quoteNonce: keccak256("quote-2"), + finalRecipient: unactivated, + finalToken: address(token), + maxBpsToSponsor: maxBps, + extraFeesIncurred: extraFees + }); + + uint256 balBefore = IERC20(address(token)).balanceOf(unactivated); + + // Expect FallbackHyperEVMFlowCompleted with zero sponsorship + vm.expectEmit(address(handler)); + emit HyperCoreFlowExecutor.FallbackHyperEVMFlowCompleted( + params.quoteNonce, + params.finalRecipient, + params.finalToken, + params.amountInEVM, + params.extraFeesIncurred, + 0 + ); + handler.callFallbackHyperEVMFlow(params); + + // Recipient received amountIn on EVM + uint256 balAfter = IERC20(address(token)).balanceOf(unactivated); + assertEq(balAfter - balBefore, amountIn, "fallback EVM transfer incorrect"); + } +} diff --git a/test/evm/foundry/local/HyperCoreMockHelper.sol b/test/evm/foundry/local/HyperCoreMockHelper.sol new file mode 100644 index 000000000..afbec6eb0 --- /dev/null +++ b/test/evm/foundry/local/HyperCoreMockHelper.sol @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; +import { HyperCoreLib } from "../../../../contracts/libraries/HyperCoreLib.sol"; + +/** + * @title HyperCoreMockHelper + * @notice Helper contract for setting up HyperCore precompile mocks in tests + * @dev Inherit from this contract in your test contracts to easily mock HyperCore precompiles + */ +abstract contract HyperCoreMockHelper is Test { + // HyperCore precompile addresses + address internal constant CORE_USER_EXISTS_PRECOMPILE = address(0x0000000000000000000000000000000000000810); + address internal constant TOKEN_INFO_PRECOMPILE = address(0x000000000000000000000000000000000000080C); + address internal constant SPOT_BALANCE_PRECOMPILE = address(0x0000000000000000000000000000000000000801); + address internal constant CORE_WRITER_PRECOMPILE = address(0x3333333333333333333333333333333333333333); + + /** + * @notice Mock the CoreUserExists precompile + * @param exists Whether the core user exists + */ + function mockCoreUserExists(bool exists) internal { + vm.mockCall( + CORE_USER_EXISTS_PRECOMPILE, + bytes(""), // Match any calldata + abi.encode(exists) + ); + } + + /** + * @notice Mock the TokenInfo precompile with custom token information + * @param tokenInfo The token info struct to return from the mock + */ + function mockTokenInfo(HyperCoreLib.TokenInfo memory tokenInfo) internal { + vm.mockCall( + TOKEN_INFO_PRECOMPILE, + bytes(""), // Match any calldata + abi.encode(tokenInfo) + ); + } + + /** + * @notice Mock the TokenInfo precompile with default values for a token + * @param evmContract The EVM contract address for the token + * @param name The token name + * @param decimals The token decimals + */ + function mockTokenInfoDefault(address evmContract, string memory name, uint8 decimals) internal { + HyperCoreLib.TokenInfo memory tokenInfo = HyperCoreLib.TokenInfo({ + name: name, + spots: new uint64[](0), + deployerTradingFeeShare: 0, + deployer: address(0), + evmContract: evmContract, + szDecimals: decimals, + weiDecimals: decimals, + evmExtraWeiDecimals: 0 + }); + mockTokenInfo(tokenInfo); + } + + /** + * @notice Mock the SpotBalance precompile + * @param spotBalance The spot balance struct to return from the mock + */ + function mockSpotBalance(HyperCoreLib.SpotBalance memory spotBalance) internal { + vm.mockCall( + SPOT_BALANCE_PRECOMPILE, + bytes(""), // Match any calldata + abi.encode(spotBalance) + ); + } + + /** + * @notice Mock the SpotBalance precompile with default values + * @param total The total balance + * @param hold The held balance + * @param entryNtl The entry notional value + */ + function mockSpotBalanceDefault(uint64 total, uint64 hold, uint64 entryNtl) internal { + HyperCoreLib.SpotBalance memory spotBalance = HyperCoreLib.SpotBalance({ + total: total, + hold: hold, + entryNtl: entryNtl + }); + mockSpotBalance(spotBalance); + } + + /** + * @notice Mock the CoreWriter precompile + * @param success Whether the core writer operation should succeed + */ + function mockCoreWriter(bool success) internal { + vm.mockCall( + CORE_WRITER_PRECOMPILE, + bytes(""), // Match any calldata + abi.encode(success) + ); + } + + /** + * @notice Setup all HyperCore precompile mocks with default values + * @param tokenAddress The EVM token contract address + * @param tokenName The token name + * @param tokenDecimals The token decimals + * @dev This is a convenience function that mocks all precompiles with sensible defaults + */ + function setupDefaultHyperCoreMocks(address tokenAddress, string memory tokenName, uint8 tokenDecimals) internal { + // 1. Mock CoreUserExists precompile - user exists + mockCoreUserExists(true); + + // 2. Mock TokenInfo precompile with default values + mockTokenInfoDefault(tokenAddress, tokenName, tokenDecimals); + + // 3. Mock SpotBalance precompile with default balance + mockSpotBalanceDefault(10e8, 0, 0); + + // 4. Mock CoreWriter precompile - operations succeed + mockCoreWriter(true); + } + + /** + * @notice Setup all HyperCore precompile mocks with default values for multiple tokens + * @param tokenAddresses Array of EVM token contract addresses + * @param tokenNames Array of token names + * @param tokenDecimals Array of token decimals + * @dev All arrays must be the same length + */ + function setupDefaultHyperCoreMocksMultiToken( + address[] memory tokenAddresses, + string[] memory tokenNames, + uint8[] memory tokenDecimals + ) internal { + require( + tokenAddresses.length == tokenNames.length && tokenNames.length == tokenDecimals.length, + "Array length mismatch" + ); + + // Mock core user exists and core writer once + mockCoreUserExists(true); + mockCoreWriter(true); + mockSpotBalanceDefault(10e8, 0, 0); + + // Mock token info for each token + // Note: This mocks with "any calldata", so all tokens will return the last mocked value + // For more specific mocking per token, use mockTokenInfo() with specific calldata + for (uint256 i = 0; i < tokenAddresses.length; i++) { + mockTokenInfoDefault(tokenAddresses[i], tokenNames[i], tokenDecimals[i]); + } + } + + /** + * @notice Clear all HyperCore precompile mocks + * @dev Useful when you need to change mock behavior mid-test + */ + function clearHyperCoreMocks() internal { + vm.clearMockedCalls(); + } +} diff --git a/test/evm/foundry/local/SponsoredOFTSrcPeriphery.t.sol b/test/evm/foundry/local/SponsoredOFTSrcPeriphery.t.sol new file mode 100644 index 000000000..39c2c0465 --- /dev/null +++ b/test/evm/foundry/local/SponsoredOFTSrcPeriphery.t.sol @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.23; + +import { Test } from "forge-std/Test.sol"; + +import { SponsoredOFTSrcPeriphery } from "../../../../contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol"; +import { Quote, SignedQuoteParams, UnsignedQuoteParams } from "../../../../contracts/periphery/mintburn/sponsored-oft/Structs.sol"; +import { AddressToBytes32 } from "../../../../contracts/libraries/AddressConverters.sol"; + +import { MockERC20 } from "../../../../contracts/test/MockERC20.sol"; +import { MockOFTMessenger } from "../../../../contracts/test/MockOFTMessenger.sol"; +import { MockEndpoint } from "../../../../contracts/test/MockEndpoint.sol"; + +import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import { DebugQuoteSignLib } from "../../../../script/mintburn/oft/CreateSponsoredDeposit.s.sol"; + +contract SponsoredOFTSrcPeripheryTest is Test { + using AddressToBytes32 for address; + + uint32 internal constant SRC_EID = 101; + + address internal owner; + address internal user; + uint256 internal signerPk; + address internal signer; + address internal refundRecipient; + + MockERC20 internal token; + MockEndpoint internal endpoint; + MockOFTMessenger internal oft; + SponsoredOFTSrcPeriphery internal periphery; + + uint256 internal constant USER_INITIAL_BAL = 1_000_000 ether; + uint256 internal constant SEND_AMOUNT = 1_000 ether; + uint256 internal constant QUOTED_NATIVE_FEE = 0.01 ether; + + function setUp() public { + owner = address(this); + user = vm.addr(111); + signerPk = 0xA11CE; + signer = vm.addr(signerPk); + refundRecipient = vm.addr(222); + + token = new MockERC20(); + endpoint = new MockEndpoint(SRC_EID); + oft = new MockOFTMessenger(address(token)); + oft.setEndpoint(address(endpoint)); + oft.setFeesToReturn(QUOTED_NATIVE_FEE, 0); + + periphery = new SponsoredOFTSrcPeriphery(address(token), address(oft), SRC_EID, signer); + + // Fund user with tokens and ETH + deal(address(token), user, USER_INITIAL_BAL, true); + vm.deal(user, 100 ether); + + // Pre-approve the periphery to pull SEND_AMOUNT + vm.startPrank(user); + IERC20(address(token)).approve(address(periphery), type(uint256).max); + vm.stopPrank(); + } + + // Helpers + function createDefaultQuote( + bytes32 nonce, + uint256 deadline, + address destHandlerAddr, + address finalRecipientAddr, + address finalTokenAddr + ) internal view returns (Quote memory q) { + SignedQuoteParams memory sp = SignedQuoteParams({ + srcEid: SRC_EID, + dstEid: uint32(201), + destinationHandler: destHandlerAddr.toBytes32(), + amountLD: SEND_AMOUNT, + nonce: nonce, + deadline: deadline, + maxBpsToSponsor: 500, // 5% + finalRecipient: finalRecipientAddr.toBytes32(), + finalToken: finalTokenAddr.toBytes32(), + lzReceiveGasLimit: 500_000, + lzComposeGasLimit: 500_000, + executionMode: uint8(0), // DirectToCore + actionData: "" + }); + + UnsignedQuoteParams memory up = UnsignedQuoteParams({ + refundRecipient: refundRecipient, + maxUserSlippageBps: 300 // 3% + }); + + q = Quote({ signedParams: sp, unsignedParams: up }); + } + + function signQuote(uint256 pk, Quote memory q) internal view returns (bytes memory sig) { + sig = DebugQuoteSignLib.signMemory(vm, pk, q.signedParams); + } + + function testDepositHappyPath() public { + bytes32 nonce = keccak256("q-1"); + uint256 deadline = block.timestamp + 1 days; + address destHandler = address(0x1234); + address finalRecipientAddr = address(0xBEEF); + address finalTokenAddr = address(0xCAFE); + + Quote memory quote = createDefaultQuote(nonce, deadline, destHandler, finalRecipientAddr, finalTokenAddr); + bytes memory signature = signQuote(signerPk, quote); + + uint256 extra = 0.123 ether; + uint256 refundRecipientBalBefore = refundRecipient.balance; + + vm.prank(user); + vm.expectEmit(address(periphery)); + emit SponsoredOFTSrcPeriphery.SponsoredOFTSend( + nonce, + user, + finalRecipientAddr.toBytes32(), + destHandler.toBytes32(), + deadline, + 500, + 300, + finalTokenAddr.toBytes32(), + signature + ); + periphery.deposit{ value: QUOTED_NATIVE_FEE + extra }(quote, signature); + + // Refund only the extra portion + assertEq(refundRecipient.balance - refundRecipientBalBefore, extra, "unexpected refund amount"); + assertEq(address(periphery).balance, 0, "periphery should not retain ETH"); + + // OFT was called with precise native fee as msg.value + assertEq(oft.lastMsgValue(), QUOTED_NATIVE_FEE, "incorrect msg.value to OFT"); + assertEq(oft.sendCallCount(), 1, "send not called exactly once"); + + // Validate send params + ( + uint32 spDstEid, + bytes32 spTo, + uint256 spAmountLD, + uint256 spMinAmountLD, + bytes memory spExtraOptions, + bytes memory spComposeMsg, + bytes memory spOftCmd + ) = oft.lastSendParam(); + spExtraOptions; // silence - structure validated implicitly by OFT quote/send success + assertEq(spDstEid, quote.signedParams.dstEid, "dstEid mismatch"); + assertEq(spTo, quote.signedParams.destinationHandler, "destination handler mismatch"); + assertEq(spAmountLD, SEND_AMOUNT, "amountLD mismatch"); + assertEq(spMinAmountLD, SEND_AMOUNT, "minAmountLD should equal amountLD (no fee-in-token)"); + assertEq(spOftCmd.length, 0, "oftCmd must be empty"); + + // Validate composeMsg encoding (layout from ComposeMsgCodec._encode) + ( + bytes32 gotNonce, + uint256 gotDeadline, + uint256 gotMaxBpsToSponsor, + uint256 gotMaxUserSlippageBps, + bytes32 gotFinalRecipient, + bytes32 gotFinalToken, + uint8 gotExecutionMode, + bytes memory gotActionData + ) = abi.decode(spComposeMsg, (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes)); + + assertEq(gotNonce, nonce, "nonce mismatch"); + assertEq(gotDeadline, deadline, "deadline mismatch"); + assertEq(gotMaxBpsToSponsor, 500, "maxBpsToSponsor mismatch"); + assertEq(gotMaxUserSlippageBps, 300, "maxUserSlippageBps mismatch"); + assertEq(gotFinalRecipient, finalRecipientAddr.toBytes32(), "finalRecipient mismatch"); + assertEq(gotFinalToken, finalTokenAddr.toBytes32(), "finalToken mismatch"); + assertEq(gotExecutionMode, 0, "executionMode mismatch"); + assertEq(keccak256(gotActionData), keccak256(""), "actionData mismatch"); + + // ERC20 was pulled and approved + assertEq(IERC20(address(token)).balanceOf(user), USER_INITIAL_BAL - SEND_AMOUNT, "user balance mismatch"); + assertEq(IERC20(address(token)).balanceOf(address(periphery)), SEND_AMOUNT, "periphery balance mismatch"); + assertEq(IERC20(address(token)).allowance(address(periphery), address(oft)), SEND_AMOUNT, "allowance mismatch"); + + // Nonce is marked used + // assertTrue(periphery.getMainStorage().usedNonces[nonce], "nonce should be marked used"); + } + + function testDepositRevertsOnInsufficientNativeFee() public { + bytes32 nonce = keccak256("q-2"); + uint256 deadline = block.timestamp + 1 days; + Quote memory quote = createDefaultQuote(nonce, deadline, address(0x1234), address(0xBEEF), address(0xCAFE)); + bytes memory signature = signQuote(signerPk, quote); + + vm.prank(user); + vm.expectRevert(SponsoredOFTSrcPeriphery.InsufficientNativeFee.selector); + periphery.deposit{ value: QUOTED_NATIVE_FEE - 1 }(quote, signature); + } + + function testDepositRevertsOnInvalidSignature() public { + bytes32 nonce = keccak256("q-3"); + uint256 deadline = block.timestamp + 1 days; + Quote memory quote = createDefaultQuote(nonce, deadline, address(0x9999), address(0x8888), address(0x7777)); + bytes memory signature = signQuote(signerPk, quote); + + // Corrupt the signature (flip 1 bit) + signature[0] = bytes1(uint8(signature[0]) ^ 0x01); + + vm.prank(user); + // ECDSA may revert with its own error for malformed signatures; accept any revert here + vm.expectRevert(); + periphery.deposit{ value: QUOTED_NATIVE_FEE }(quote, signature); + } + + function testDepositRevertsOnIncorrectSigner() public { + // Produce a well-formed signature from a non-authorized key + uint256 wrongPk = 0xB0B; + address wrongSigner = vm.addr(wrongPk); + vm.assume(wrongSigner != signer); + + bytes32 nonce = keccak256("q-3b"); + uint256 deadline = block.timestamp + 1 days; + + Quote memory quote = createDefaultQuote(nonce, deadline, address(0x1111), address(0x2222), address(0x3333)); + // Sign with wrong private key + bytes memory signature = signQuote(wrongPk, quote); + + vm.prank(user); + vm.expectRevert(SponsoredOFTSrcPeriphery.IncorrectSignature.selector); + periphery.deposit{ value: QUOTED_NATIVE_FEE }(quote, signature); + } + + function testDepositRevertsOnExpiredQuote() public { + bytes32 nonce = keccak256("q-4"); + uint256 pastDeadline = block.timestamp - 1; + Quote memory quote = createDefaultQuote(nonce, pastDeadline, address(0xA1), address(0xB2), address(0xC3)); + bytes memory signature = signQuote(signerPk, quote); + + vm.prank(user); + vm.expectRevert(SponsoredOFTSrcPeriphery.QuoteExpired.selector); + periphery.deposit{ value: QUOTED_NATIVE_FEE }(quote, signature); + } + + function testDepositRevertsOnNonceReuse() public { + bytes32 nonce = keccak256("q-5"); + uint256 deadline = block.timestamp + 1 days; + Quote memory quote = createDefaultQuote(nonce, deadline, address(0xD1), address(0xD2), address(0xD3)); + bytes memory signature = signQuote(signerPk, quote); + + vm.prank(user); + periphery.deposit{ value: QUOTED_NATIVE_FEE }(quote, signature); + + // Try again with the same quote/nonce + vm.prank(user); + vm.expectRevert(SponsoredOFTSrcPeriphery.NonceAlreadyUsed.selector); + periphery.deposit{ value: QUOTED_NATIVE_FEE }(quote, signature); + } +} diff --git a/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol b/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol new file mode 100644 index 000000000..fc2411a69 --- /dev/null +++ b/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol @@ -0,0 +1,650 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Test, console } from "forge-std/Test.sol"; +import { SponsoredCCTPDstPeriphery } from "../../../../contracts/periphery/mintburn/sponsored-cctp/SponsoredCCTPDstPeriphery.sol"; +import { IHyperCoreFlowExecutor } from "../../../../contracts/test/interfaces/IHyperCoreFlowExecutor.sol"; +import { SponsoredCCTPInterface } from "../../../../contracts/interfaces/SponsoredCCTPInterface.sol"; +import { IMessageTransmitterV2 } from "../../../../contracts/external/interfaces/CCTPInterfaces.sol"; +import { AddressToBytes32, Bytes32ToAddress } from "../../../../contracts/libraries/AddressConverters.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { HyperCoreMockHelper } from "./HyperCoreMockHelper.sol"; +import { BaseSimulatorTest } from "./external/hyper-evm-lib/test/BaseSimulatorTest.sol"; +import { PrecompileLib } from "./external/hyper-evm-lib/src/PrecompileLib.sol"; + +contract MockMessageTransmitter is IMessageTransmitterV2 { + bool internal shouldSucceed = true; + + function setShouldSucceed(bool _shouldSucceed) external { + shouldSucceed = _shouldSucceed; + } + + function receiveMessage(bytes calldata, bytes calldata) external view override returns (bool) { + return shouldSucceed; + } +} + +contract MockDonationBox { + function withdraw(IERC20 token, uint256 amount) external { + token.transfer(msg.sender, amount); + } + + function mockTransfer(address token, uint256 amount) external { + IERC20(token).transfer(msg.sender, amount); + } +} + +contract MockUSDC is Test { + string public name = "USD Coin"; + string public symbol = "USDC"; + uint8 public decimals = 6; + mapping(address => uint256) public balanceOf; + mapping(address => mapping(address => uint256)) public allowance; + + function mint(address to, uint256 amount) external { + balanceOf[to] += amount; + } + + function transfer(address to, uint256 amount) external returns (bool) { + balanceOf[msg.sender] -= amount; + balanceOf[to] += amount; + return true; + } + + function transferFrom(address from, address to, uint256 amount) external returns (bool) { + allowance[from][msg.sender] -= amount; + balanceOf[from] -= amount; + balanceOf[to] += amount; + return true; + } + + function approve(address spender, uint256 amount) external returns (bool) { + allowance[msg.sender][spender] = amount; + return true; + } +} + +contract SponsoredCCTPDstPeripheryTest is BaseSimulatorTest { + using AddressToBytes32 for address; + using Bytes32ToAddress for bytes32; + + SponsoredCCTPDstPeriphery public periphery; + MockMessageTransmitter public messageTransmitter; + MockDonationBox public donationBox; + MockUSDC public usdc; + + address public signer; + uint256 public signerPrivateKey; + address public admin; + // address public user; + address public finalRecipient; + address public multicallHandler; + + uint32 constant SOURCE_DOMAIN = 0; + uint32 constant DESTINATION_DOMAIN = 1; + uint32 constant CORE_INDEX = 0; + uint32 constant MIN_FINALITY_THRESHOLD = 100; + + uint256 constant DEFAULT_AMOUNT = 1000e6; // 1000 USDC + uint256 constant DEFAULT_MAX_FEE = 10e6; // 10 USDC + uint256 constant FEE_EXECUTED = 5e6; // 5 USDC + + function setUp() public override { + super.setUp(); + + admin = makeAddr("admin"); + // user = makeAddr("user"); + finalRecipient = makeAddr("finalRecipient"); + multicallHandler = makeAddr("multicallHandler"); + + // Create signer + signerPrivateKey = 0x1234; + signer = vm.addr(signerPrivateKey); + + // Deploy mock contracts + messageTransmitter = new MockMessageTransmitter(); + donationBox = new MockDonationBox(); + usdc = new MockUSDC(); + + // Setup HyperCore precompile mocks using the helper + // setupDefaultHyperCoreMocks(address(usdc), "Mock USDC", 6); + hyperCore.forceAccountActivation(finalRecipient); + hyperCore.forceTokenInfo(CORE_INDEX, "USDC", address(usdc), 8, 8, 0); + + // Deploy periphery + vm.startPrank(admin); + periphery = new SponsoredCCTPDstPeriphery( + address(messageTransmitter), + signer, + address(donationBox), + address(usdc), + multicallHandler + ); + + IHyperCoreFlowExecutor(address(periphery)).setCoreTokenInfo(address(usdc), CORE_INDEX, true, 1e6, 1e6); + vm.stopPrank(); + + // Mint USDC to periphery for testing + usdc.mint(address(periphery), 10000e6); + } + + /// @dev Helper function to create a valid CCTP message + function createCCTPMessage( + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, + uint256 feeExecuted + ) internal view returns (bytes memory) { + // (bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes) + // BurnMessage body + bytes memory hookData = abi.encode( + quote.nonce, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalRecipient, + quote.finalToken, + quote.executionMode, + quote.actionData + ); + + bytes memory messageBody = abi.encodePacked( + uint32(2), // version + quote.burnToken, // burnToken + quote.mintRecipient, // mintRecipient + uint256(quote.amount), // amount (32 bytes) + bytes32(0), // padding (32 bytes - for alignment) + quote.maxFee, // maxFee (32 bytes) + feeExecuted, // feeExecuted (32 bytes) + uint256(0), // hookDataRecipient (32 bytes - not used) + hookData // hookData + ); + + // CCTP Message format as per MessageV2 + bytes memory message = abi.encodePacked( + uint32(2), // version + quote.sourceDomain, // sourceDomain + quote.destinationDomain, // destinationDomain + bytes32(uint256(1)), // nonce (CCTP nonce) + quote.burnToken, // sender (token messenger on source) + bytes32(uint256(uint160(address(messageTransmitter)))), // recipient (token messenger on dest) + quote.destinationCaller, // destinationCaller + quote.minFinalityThreshold, // minFinalityThreshold + uint32(0), // finalityThresholdExecuted + messageBody // messageBody + ); + + return message; + } + + /// @dev Helper function to sign a quote + function signQuote( + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote, + uint256 privateKey + ) internal pure returns (bytes memory) { + bytes32 hash1 = keccak256( + abi.encode( + quote.sourceDomain, + quote.destinationDomain, + quote.mintRecipient, + quote.amount, + quote.burnToken, + quote.destinationCaller, + quote.maxFee, + quote.minFinalityThreshold + ) + ); + + bytes32 hash2 = keccak256( + abi.encode( + quote.nonce, + quote.deadline, + quote.maxBpsToSponsor, + quote.maxUserSlippageBps, + quote.finalRecipient, + quote.finalToken, + quote.executionMode, + keccak256(quote.actionData) + ) + ); + + bytes32 typedDataHash = keccak256(abi.encode(hash1, hash2)); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); + return abi.encodePacked(r, s, v); + } + + /// @dev Helper function to create a default valid quote + function createDefaultQuote() internal view returns (SponsoredCCTPInterface.SponsoredCCTPQuote memory) { + return + SponsoredCCTPInterface.SponsoredCCTPQuote({ + sourceDomain: SOURCE_DOMAIN, + destinationDomain: DESTINATION_DOMAIN, + mintRecipient: bytes32(uint256(uint160(address(periphery)))), + amount: DEFAULT_AMOUNT, + burnToken: address(usdc).toBytes32(), + destinationCaller: bytes32(0), + maxFee: DEFAULT_MAX_FEE, + minFinalityThreshold: MIN_FINALITY_THRESHOLD, + nonce: keccak256("test-nonce-1"), + deadline: block.timestamp + 1 hours, + maxBpsToSponsor: 100, // 1% + maxUserSlippageBps: 50, // 0.5% + finalRecipient: finalRecipient.toBytes32(), + finalToken: address(usdc).toBytes32(), + executionMode: uint8(SponsoredCCTPInterface.ExecutionMode.DirectToCore), + actionData: bytes("") + }); + } + + /*////////////////////////////////////////////////////////////// + BASIC MESSAGE VALIDATION TESTS + //////////////////////////////////////////////////////////////*/ + + function test_ReceiveMessage_ValidQuote_Success() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // Note: setUp already mocks HyperCore precompiles + // The actual event emitted is SimpleTransferFlowCompleted from HyperCoreFlowExecutor + periphery.receiveMessage(message, attestation, signature); + + // Verify nonce is marked as used + assertTrue(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_InvalidSignature_FallsBackToUnsponsoredFlow() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + + // Sign with wrong private key + uint256 wrongPrivateKey = 0x5678; + bytes memory wrongSignature = signQuote(quote, wrongPrivateKey); + + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // Should not revert, but should process as unsponsored + periphery.receiveMessage(message, attestation, wrongSignature); + + // Nonce should NOT be marked as used since signature was invalid + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_ExpiredDeadline_FallsBackToUnsponsoredFlow() public { + vm.warp(block.timestamp + 2 hours); + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.deadline = block.timestamp - 1 hours; // Expired deadline + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // Should not revert, but should process as unsponsored + periphery.receiveMessage(message, attestation, signature); + + // Nonce should NOT be marked as used + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_DeadlineWithinBuffer_Success() public { + vm.warp(block.timestamp + 1 hours); + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + + // Set deadline to 15 minutes ago (within 30 minute buffer) + quote.deadline = block.timestamp - 15 minutes; + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should be processed as valid since within buffer + assertTrue(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_DeadlineOutsideBuffer_FallsBack() public { + vm.warp(block.timestamp + 1 hours); + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + + // Set deadline to 31 minutes ago (outside 30 minute buffer) + quote.deadline = block.timestamp - 31 minutes; + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should NOT be processed as valid + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_ReplayAttack_SecondAttemptNotSponsored() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // First call - should succeed + periphery.receiveMessage(message, attestation, signature); + assertTrue(periphery.usedNonces(quote.nonce)); + + // Second call with same nonce - should process as unsponsored + // (CCTP prevents actual replay, but this tests nonce checking) + quote.deadline = block.timestamp + 2 hours; // Update deadline + bytes memory newSignature = signQuote(quote, signerPrivateKey); + bytes memory newMessage = createCCTPMessage(quote, FEE_EXECUTED); + + // This would fail at CCTP level in practice, but for testing our logic: + messageTransmitter.setShouldSucceed(true); + periphery.receiveMessage(newMessage, attestation, newSignature); + + // Nonce already used, so not considered valid + assertTrue(periphery.usedNonces(quote.nonce)); + } + + /*////////////////////////////////////////////////////////////// + MESSAGE DECODING TESTS + //////////////////////////////////////////////////////////////*/ + + function test_MessageDecoding_InvalidMintRecipient_KeepsFundsInContract() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + + // Set mint recipient to wrong address + quote.mintRecipient = bytes32(uint256(uint160(address(0x1234)))); + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + uint256 balanceBefore = usdc.balanceOf(address(periphery)); + + // Should not revert, but message validation fails so funds stay in contract + periphery.receiveMessage(message, attestation, signature); + + uint256 balanceAfter = usdc.balanceOf(address(periphery)); + assertEq(balanceAfter, balanceBefore); + } + + function test_MessageDecoding_InvalidFinalRecipient_FailsValidation() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + + // Set invalid final recipient (has upper bits set) + quote.finalRecipient = bytes32(uint256(1) << 200); + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // Should not revert, but should fail validation + periphery.receiveMessage(message, attestation, signature); + + // Nonce should NOT be used since message validation failed + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_MessageDecoding_InvalidFinalToken_FailsValidation() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + + // Set invalid final token (has upper bits set) + quote.finalToken = bytes32(uint256(1) << 200); + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // Should not revert, but should fail validation + periphery.receiveMessage(message, attestation, signature); + + // Nonce should NOT be used + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_MessageDecoding_ExtractsCorrectQuoteData() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.maxBpsToSponsor = 250; // 2.5% + quote.maxUserSlippageBps = 100; // 1% + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + // Verify the message is processed successfully + periphery.receiveMessage(message, attestation, signature); + + // Verify nonce is marked as used, confirming successful processing + assertTrue(periphery.usedNonces(quote.nonce)); + } + + /*////////////////////////////////////////////////////////////// + EXECUTION MODE TESTS + //////////////////////////////////////////////////////////////*/ + + function test_ReceiveMessage_DirectToCore_Success() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.executionMode = uint8(SponsoredCCTPInterface.ExecutionMode.DirectToCore); + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + assertTrue(periphery.usedNonces(quote.nonce)); + } + + struct CompressedCall { + address target; + bytes callData; + } + + /*////////////////////////////////////////////////////////////// + ADMIN FUNCTION TESTS + //////////////////////////////////////////////////////////////*/ + + function test_SetSigner_OnlyAdmin() public { + address newSigner = makeAddr("newSigner"); + + vm.prank(admin); + periphery.setSigner(newSigner); + + assertEq(periphery.signer(), newSigner); + } + + function test_SetSigner_NotAdmin_Reverts() public { + address newSigner = makeAddr("newSigner"); + + vm.prank(user); + vm.expectRevert(); + periphery.setSigner(newSigner); + } + + function test_SetQuoteDeadlineBuffer_OnlyAdmin() public { + uint256 newBuffer = 1 hours; + + vm.prank(admin); + periphery.setQuoteDeadlineBuffer(newBuffer); + + assertEq(periphery.quoteDeadlineBuffer(), newBuffer); + } + + function test_SetQuoteDeadlineBuffer_NotAdmin_Reverts() public { + uint256 newBuffer = 1 hours; + + vm.prank(user); + vm.expectRevert(); + periphery.setQuoteDeadlineBuffer(newBuffer); + } + + function test_SetQuoteDeadlineBuffer_AffectsValidation() public { + // Set buffer to 0 + vm.prank(admin); + periphery.setQuoteDeadlineBuffer(0); + + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.deadline = block.timestamp - 1; // 1 second ago + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should NOT be processed as valid (no buffer) + assertFalse(periphery.usedNonces(quote.nonce)); + } + + /*////////////////////////////////////////////////////////////// + SIGNATURE VALIDATION TESTS + //////////////////////////////////////////////////////////////*/ + + function test_SignatureValidation_DifferentSigner_Fails() public { + // Change signer + address newSigner = makeAddr("newSigner"); + vm.prank(admin); + periphery.setSigner(newSigner); + + // Create quote signed with old signer + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should not be valid with different signer + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_SignatureValidation_ModifiedAmount_Fails() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.amount = 1000e6; + + // Sign quote + bytes memory signature = signQuote(quote, signerPrivateKey); + + // Modify amount in message + quote.amount = 2000e6; + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should fail validation + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_SignatureValidation_ModifiedRecipient_Fails() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + bytes memory signature = signQuote(quote, signerPrivateKey); + + // Modify final recipient + quote.finalRecipient = makeAddr("attacker").toBytes32(); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should fail validation + assertFalse(periphery.usedNonces(quote.nonce)); + } + + function test_SignatureValidation_ModifiedActionData_Fails() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.executionMode = uint8(SponsoredCCTPInterface.ExecutionMode.ArbitraryActionsToCore); + + // Original action data + address[] memory targets = new address[](1); + targets[0] = address(usdc); + bytes[] memory callDatas = new bytes[](1); + callDatas[0] = abi.encodeWithSignature("transfer(address,uint256)", finalRecipient, 100e6); + quote.actionData = abi.encode(targets, callDatas); + + // Sign original quote + bytes memory signature = signQuote(quote, signerPrivateKey); + + // Modify action data + callDatas[0] = abi.encodeWithSignature("transfer(address,uint256)", makeAddr("attacker"), 100e6); + quote.actionData = abi.encode(targets, callDatas); + + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + // Should fail validation (actionData is hashed in signature) + assertFalse(periphery.usedNonces(quote.nonce)); + } + + /*////////////////////////////////////////////////////////////// + EDGE CASE TESTS + //////////////////////////////////////////////////////////////*/ + + function test_ReceiveMessage_ZeroAmount_HandledGracefully() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.amount = 0; + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, 0); + bytes memory attestation = bytes("mock-attestation"); + + // Should not revert + periphery.receiveMessage(message, attestation, signature); + + assertTrue(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_EmptyActionData_DirectToCore() public { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.executionMode = uint8(SponsoredCCTPInterface.ExecutionMode.DirectToCore); + quote.actionData = bytes(""); + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + assertTrue(periphery.usedNonces(quote.nonce)); + } + + function test_ReceiveMessage_MultipleQuotesInSequence() public { + // Test multiple different quotes in sequence + for (uint256 i = 0; i < 5; i++) { + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.nonce = keccak256(abi.encodePacked("test-nonce", i)); + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + assertTrue(periphery.usedNonces(quote.nonce)); + } + } + + function test_View_UsedNonces() public { + bytes32 testNonce = keccak256("test-view-nonce"); + assertFalse(periphery.usedNonces(testNonce)); + + SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = createDefaultQuote(); + quote.nonce = testNonce; + + bytes memory signature = signQuote(quote, signerPrivateKey); + bytes memory message = createCCTPMessage(quote, FEE_EXECUTED); + bytes memory attestation = bytes("mock-attestation"); + + periphery.receiveMessage(message, attestation, signature); + + assertTrue(periphery.usedNonces(testNonce)); + } + + function test_View_ContractReferences() public { + assertEq(address(periphery.cctpMessageTransmitter()), address(messageTransmitter)); + assertEq(periphery.signer(), signer); + assertEq(periphery.quoteDeadlineBuffer(), 30 minutes); + assertEq(address(IHyperCoreFlowExecutor(address(periphery)).donationBox()), address(donationBox)); + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/src/CoreWriterLib.sol b/test/evm/foundry/local/external/hyper-evm-lib/src/CoreWriterLib.sol new file mode 100644 index 000000000..7a4d7a254 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/src/CoreWriterLib.sol @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +import { PrecompileLib } from "./PrecompileLib.sol"; +import { HLConstants } from "./common/HLConstants.sol"; +import { HLConversions } from "./common/HLConversions.sol"; + +import { ICoreWriter } from "./interfaces/ICoreWriter.sol"; + +/** + * @title CoreWriterLib v1.0 + * @author Obsidian (https://x.com/ObsidianAudits) + * @notice A library for interacting with HyperEVM's CoreWriter + * + * @dev Additional functionality for: + * - Bridging assets between EVM and HyperCore + * - Converting decimal representations between EVM and HyperCore amounts + * - Security checks before sending actions to CoreWriter + */ +library CoreWriterLib { + using SafeERC20 for IERC20; + + ICoreWriter constant coreWriter = ICoreWriter(0x3333333333333333333333333333333333333333); + + error CoreWriterLib__StillLockedUntilTimestamp(uint64 lockedUntilTimestamp); + error CoreWriterLib__CannotSelfTransfer(); + error CoreWriterLib__HypeTransferFailed(); + error CoreWriterLib__CoreAmountTooLarge(uint256 amount); + error CoreWriterLib__EvmAmountTooSmall(uint256 amount); + + /*////////////////////////////////////////////////////////////// + EVM <---> Core Bridging + //////////////////////////////////////////////////////////////*/ + + function bridgeToCore(address tokenAddress, uint256 evmAmount) internal { + uint64 tokenIndex = PrecompileLib.getTokenIndex(tokenAddress); + bridgeToCore(tokenIndex, evmAmount); + } + + function bridgeToCore(uint64 token, uint256 evmAmount) internal { + // Check if amount would be 0 after conversion to prevent token loss + uint64 coreAmount = HLConversions.evmToWei(token, evmAmount); + if (coreAmount == 0) revert CoreWriterLib__EvmAmountTooSmall(evmAmount); + address systemAddress = getSystemAddress(token); + if (isHype(token)) { + (bool success, ) = systemAddress.call{ value: evmAmount }(""); + require(success, "HYPE transfer failed"); + } else { + PrecompileLib.TokenInfo memory info = PrecompileLib.tokenInfo(uint32(token)); + address tokenAddress = info.evmContract; + IERC20(tokenAddress).safeTransfer(systemAddress, evmAmount); + } + } + + function bridgeToEvm(address tokenAddress, uint256 evmAmount) internal { + uint64 tokenIndex = PrecompileLib.getTokenIndex(tokenAddress); + bridgeToEvm(tokenIndex, evmAmount, true); + } + + // NOTE: For bridging non-HYPE tokens, the contract must hold some HYPE on core (enough to cover the transfer gas), otherwise spotSend will fail + function bridgeToEvm(uint64 token, uint256 amount, bool isEvmAmount) internal { + address systemAddress = getSystemAddress(token); + + uint64 coreAmount; + if (isEvmAmount) { + coreAmount = HLConversions.evmToWei(token, amount); + if (coreAmount == 0) revert CoreWriterLib__EvmAmountTooSmall(amount); + } else { + if (amount > type(uint64).max) revert CoreWriterLib__CoreAmountTooLarge(amount); + coreAmount = uint64(amount); + } + + spotSend(systemAddress, token, coreAmount); + } + + function spotSend(address to, uint64 token, uint64 amountWei) internal { + // Self-transfers will always fail, so reverting here + require(to != address(this), "Cannot self-transfer"); + + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.SPOT_SEND_ACTION, abi.encode(to, token, amountWei)) + ); + } + + /*////////////////////////////////////////////////////////////// + Bridging Utils + //////////////////////////////////////////////////////////////*/ + + function getSystemAddress(uint64 index) internal view returns (address) { + if (index == HLConstants.hypeTokenIndex()) { + return HLConstants.HYPE_SYSTEM_ADDRESS; + } + return address(HLConstants.BASE_SYSTEM_ADDRESS + index); + } + + function isHype(uint64 index) internal view returns (bool) { + return index == HLConstants.hypeTokenIndex(); + } + + /*////////////////////////////////////////////////////////////// + Staking + //////////////////////////////////////////////////////////////*/ + function delegateToken(address validator, uint64 amountWei, bool undelegate) internal { + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.TOKEN_DELEGATE_ACTION, abi.encode(validator, amountWei, undelegate)) + ); + } + + function depositStake(uint64 amountWei) internal { + coreWriter.sendRawAction(abi.encodePacked(uint8(1), HLConstants.STAKING_DEPOSIT_ACTION, abi.encode(amountWei))); + } + + function withdrawStake(uint64 amountWei) internal { + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.STAKING_WITHDRAW_ACTION, abi.encode(amountWei)) + ); + } + + /*////////////////////////////////////////////////////////////// + Trading + //////////////////////////////////////////////////////////////*/ + + function toMilliseconds(uint64 timestamp) internal pure returns (uint64) { + return timestamp * 1000; + } + + function _canWithdrawFromVault(address vault) internal view returns (bool, uint64) { + PrecompileLib.UserVaultEquity memory vaultEquity = PrecompileLib.userVaultEquity(address(this), vault); + + return ( + toMilliseconds(uint64(block.timestamp)) > vaultEquity.lockedUntilTimestamp, + vaultEquity.lockedUntilTimestamp + ); + } + + function vaultTransfer(address vault, bool isDeposit, uint64 usdAmount) internal { + if (!isDeposit) { + (bool canWithdraw, uint64 lockedUntilTimestamp) = _canWithdrawFromVault(vault); + + if (!canWithdraw) revert CoreWriterLib__StillLockedUntilTimestamp(lockedUntilTimestamp); + } + + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.VAULT_TRANSFER_ACTION, abi.encode(vault, isDeposit, usdAmount)) + ); + } + + function transferUsdClass(uint64 ntl, bool toPerp) internal { + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.USD_CLASS_TRANSFER_ACTION, abi.encode(ntl, toPerp)) + ); + } + + function placeLimitOrder( + uint32 asset, + bool isBuy, + uint64 limitPx, + uint64 sz, + bool reduceOnly, + uint8 encodedTif, + uint128 cloid + ) internal { + coreWriter.sendRawAction( + abi.encodePacked( + uint8(1), + HLConstants.LIMIT_ORDER_ACTION, + abi.encode(asset, isBuy, limitPx, sz, reduceOnly, encodedTif, cloid) + ) + ); + } + + function addApiWallet(address wallet, string memory name) internal { + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.ADD_API_WALLET_ACTION, abi.encode(wallet, name)) + ); + } + + function cancelOrderByOrderId(uint32 asset, uint64 orderId) internal { + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.CANCEL_ORDER_BY_OID_ACTION, abi.encode(asset, orderId)) + ); + } + + function cancelOrderByCloid(uint32 asset, uint128 cloid) internal { + coreWriter.sendRawAction( + abi.encodePacked(uint8(1), HLConstants.CANCEL_ORDER_BY_CLOID_ACTION, abi.encode(asset, cloid)) + ); + } + + function finalizeEvmContract(uint64 token, uint8 encodedVariant, uint64 createNonce) internal { + coreWriter.sendRawAction( + abi.encodePacked( + uint8(1), + HLConstants.FINALIZE_EVM_CONTRACT_ACTION, + abi.encode(token, encodedVariant, createNonce) + ) + ); + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/src/PrecompileLib.sol b/test/evm/foundry/local/external/hyper-evm-lib/src/PrecompileLib.sol new file mode 100644 index 000000000..166df1853 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/src/PrecompileLib.sol @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { ITokenRegistry } from "./interfaces/ITokenRegistry.sol"; +import { HLConstants } from "./common/HLConstants.sol"; + +/** + * @title PrecompileLib v1.0 + * @author Obsidian (https://x.com/ObsidianAudits) + * @notice A library with helper functions for interacting with HyperEVM's precompiles + */ +library PrecompileLib { + // Onchain record of token indices for each linked evm contract + ITokenRegistry constant REGISTRY = ITokenRegistry(0x0b51d1A9098cf8a72C325003F44C194D41d7A85B); + + /*////////////////////////////////////////////////////////////// + Custom Utility Functions + (Overloads accepting token address instead of index) + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Gets TokenInfo for a given token address by looking up its index and fetching from the precompile. + * @dev Overload of tokenInfo(uint64 token) + */ + function tokenInfo(address tokenAddress) internal view returns (TokenInfo memory) { + uint64 index = getTokenIndex(tokenAddress); + return tokenInfo(index); + } + + /** + * @notice Gets SpotInfo for the token/USDC market using the token address. + * @dev Overload of spotInfo(uint64 tokenIndex) + * Finds the spot market where USDC (index 0) is the quote. + */ + function spotInfo(address tokenAddress) internal view returns (SpotInfo memory) { + uint64 tokenIndex = getTokenIndex(tokenAddress); + uint64 spotIndex = getSpotIndex(tokenIndex); + return spotInfo(spotIndex); + } + + /** + * @notice Gets the spot price for the token/USDC market using the token address. + * @dev Overload of spotPx(uint64 spotIndex) + */ + function spotPx(address tokenAddress) internal view returns (uint64) { + uint64 tokenIndex = getTokenIndex(tokenAddress); + uint64 spotIndex = getSpotIndex(tokenIndex); + return spotPx(spotIndex); + } + + /** + * @notice Gets a user's spot balance for a given token address. + * @dev Overload of spotBalance(address user, uint64 token) + */ + function spotBalance(address user, address tokenAddress) internal view returns (SpotBalance memory) { + uint64 tokenIndex = getTokenIndex(tokenAddress); + return spotBalance(user, tokenIndex); + } + + /** + * @notice Gets the index of a token from its address. Reverts if token is not linked to HyperCore. + */ + function getTokenIndex(address tokenAddress) internal view returns (uint64) { + return REGISTRY.getTokenIndex(tokenAddress); + } + + /** + * @notice Gets the spot market index for the token/USDC pair for a token using its address. + * @dev Overload of getSpotIndex(uint64 tokenIndex) + */ + function getSpotIndex(address tokenAddress) internal view returns (uint64) { + uint64 tokenIndex = getTokenIndex(tokenAddress); + return getSpotIndex(tokenIndex); + } + + /** + * @notice Gets the spot market index for a token. + * @dev If only one spot market exists, returns it. Otherwise, finds the spot market with USDC as the quote token. + */ + function getSpotIndex(uint64 tokenIndex) internal view returns (uint64) { + uint64[] memory spots = tokenInfo(tokenIndex).spots; + + if (spots.length == 1) return spots[0]; + + for (uint256 idx = 0; idx < spots.length; idx++) { + SpotInfo memory spot = spotInfo(spots[idx]); + if (spot.tokens[1] == 0) { + // index 0 = USDC + return spots[idx]; + } + } + revert PrecompileLib__SpotIndexNotFound(); + } + + /*////////////////////////////////////////////////////////////// + Using Alternate Quote Token (non USDC) + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Gets the spot market index for a token/quote pair. + * Iterates all spot markets for the token and matches the quote token index. + * @dev Overload of getSpotIndex(uint64 tokenIndex) + */ + function getSpotIndex(uint64 tokenIndex, uint64 quoteTokenIndex) internal view returns (uint64) { + uint64[] memory spots = tokenInfo(tokenIndex).spots; + + for (uint256 idx = 0; idx < spots.length; idx++) { + SpotInfo memory spot = spotInfo(spots[idx]); + if (spot.tokens[1] == quoteTokenIndex) { + return spots[idx]; + } + } + revert PrecompileLib__SpotIndexNotFound(); + } + + /** + * @notice Gets SpotInfo for a token/quote pair using token addresses. + * Looks up both token and quote indices, then finds the spot market. + * @dev Overload of spotInfo(uint64 spotIndex) + */ + function spotInfo(address token, address quoteToken) internal view returns (SpotInfo memory) { + uint64 tokenIndex = getTokenIndex(token); + uint64 quoteTokenIndex = getTokenIndex(quoteToken); + uint64 spotIndex = getSpotIndex(tokenIndex, quoteTokenIndex); + return spotInfo(spotIndex); + } + + /** + * @notice Gets the spot price for a token/quote pair using token addresses. + * Looks up both token and quote indices, then finds the spot market. + * @dev Overload of spotPx(uint64 spotIndex) + */ + function spotPx(address token, address quoteToken) internal view returns (uint64) { + uint64 tokenIndex = getTokenIndex(token); + uint64 quoteTokenIndex = getTokenIndex(quoteToken); + uint64 spotIndex = getSpotIndex(tokenIndex, quoteTokenIndex); + return spotPx(spotIndex); + } + + /*////////////////////////////////////////////////////////////// + Price decimals normalization + //////////////////////////////////////////////////////////////*/ + + // returns spot price as a fixed-point integer with 8 decimals + function normalizedSpotPx(uint64 spotIndex) internal view returns (uint256) { + SpotInfo memory info = spotInfo(spotIndex); + uint8 baseSzDecimals = tokenInfo(info.tokens[0]).szDecimals; + return spotPx(spotIndex) * 10 ** baseSzDecimals; + } + + // returns mark price as a fixed-point integer with 6 decimals + function normalizedMarkPx(uint32 perpIndex) internal view returns (uint256) { + PerpAssetInfo memory info = perpAssetInfo(perpIndex); + return markPx(perpIndex) * 10 ** info.szDecimals; + } + + // returns perp oracle price as a fixed-point integer with 6 decimals + function normalizedOraclePx(uint32 perpIndex) internal view returns (uint256) { + PerpAssetInfo memory info = perpAssetInfo(perpIndex); + return oraclePx(perpIndex) * 10 ** info.szDecimals; + } + + /*////////////////////////////////////////////////////////////// + Precompile Calls + //////////////////////////////////////////////////////////////*/ + + function position(address user, uint16 perp) internal view returns (Position memory) { + (bool success, bytes memory result) = HLConstants.POSITION_PRECOMPILE_ADDRESS.staticcall( + abi.encode(user, perp) + ); + if (!success) revert PrecompileLib__PositionPrecompileFailed(); + return abi.decode(result, (Position)); + } + + function spotBalance(address user, uint64 token) internal view returns (SpotBalance memory) { + (bool success, bytes memory result) = HLConstants.SPOT_BALANCE_PRECOMPILE_ADDRESS.staticcall( + abi.encode(user, token) + ); + if (!success) revert PrecompileLib__SpotBalancePrecompileFailed(); + return abi.decode(result, (SpotBalance)); + } + + function userVaultEquity(address user, address vault) internal view returns (UserVaultEquity memory) { + (bool success, bytes memory result) = HLConstants.VAULT_EQUITY_PRECOMPILE_ADDRESS.staticcall( + abi.encode(user, vault) + ); + if (!success) revert PrecompileLib__VaultEquityPrecompileFailed(); + return abi.decode(result, (UserVaultEquity)); + } + + function withdrawable(address user) internal view returns (uint64) { + (bool success, bytes memory result) = HLConstants.WITHDRAWABLE_PRECOMPILE_ADDRESS.staticcall(abi.encode(user)); + if (!success) revert PrecompileLib__WithdrawablePrecompileFailed(); + return abi.decode(result, (Withdrawable)).withdrawable; + } + + function delegations(address user) internal view returns (Delegation[] memory) { + (bool success, bytes memory result) = HLConstants.DELEGATIONS_PRECOMPILE_ADDRESS.staticcall(abi.encode(user)); + if (!success) revert PrecompileLib__DelegationsPrecompileFailed(); + return abi.decode(result, (Delegation[])); + } + + function delegatorSummary(address user) internal view returns (DelegatorSummary memory) { + (bool success, bytes memory result) = HLConstants.DELEGATOR_SUMMARY_PRECOMPILE_ADDRESS.staticcall( + abi.encode(user) + ); + if (!success) revert PrecompileLib__DelegatorSummaryPrecompileFailed(); + return abi.decode(result, (DelegatorSummary)); + } + + function markPx(uint32 perpIndex) internal view returns (uint64) { + (bool success, bytes memory result) = HLConstants.MARK_PX_PRECOMPILE_ADDRESS.staticcall(abi.encode(perpIndex)); + if (!success) revert PrecompileLib__MarkPxPrecompileFailed(); + return abi.decode(result, (uint64)); + } + + function oraclePx(uint32 perpIndex) internal view returns (uint64) { + (bool success, bytes memory result) = HLConstants.ORACLE_PX_PRECOMPILE_ADDRESS.staticcall( + abi.encode(perpIndex) + ); + if (!success) revert PrecompileLib__OraclePxPrecompileFailed(); + return abi.decode(result, (uint64)); + } + + function spotPx(uint64 spotIndex) internal view returns (uint64) { + (bool success, bytes memory result) = HLConstants.SPOT_PX_PRECOMPILE_ADDRESS.staticcall(abi.encode(spotIndex)); + if (!success) revert PrecompileLib__SpotPxPrecompileFailed(); + return abi.decode(result, (uint64)); + } + + function perpAssetInfo(uint32 perp) internal view returns (PerpAssetInfo memory) { + (bool success, bytes memory result) = HLConstants.PERP_ASSET_INFO_PRECOMPILE_ADDRESS.staticcall( + abi.encode(perp) + ); + if (!success) revert PrecompileLib__PerpAssetInfoPrecompileFailed(); + return abi.decode(result, (PerpAssetInfo)); + } + + function spotInfo(uint64 spotIndex) internal view returns (SpotInfo memory) { + (bool success, bytes memory result) = HLConstants.SPOT_INFO_PRECOMPILE_ADDRESS.staticcall( + abi.encode(spotIndex) + ); + if (!success) revert PrecompileLib__SpotInfoPrecompileFailed(); + return abi.decode(result, (SpotInfo)); + } + + function tokenInfo(uint64 token) internal view returns (TokenInfo memory) { + (bool success, bytes memory result) = HLConstants.TOKEN_INFO_PRECOMPILE_ADDRESS.staticcall(abi.encode(token)); + if (!success) revert PrecompileLib__TokenInfoPrecompileFailed(); + return abi.decode(result, (TokenInfo)); + } + + function tokenSupply(uint64 token) internal view returns (TokenSupply memory) { + (bool success, bytes memory result) = HLConstants.TOKEN_SUPPLY_PRECOMPILE_ADDRESS.staticcall(abi.encode(token)); + if (!success) revert PrecompileLib__TokenSupplyPrecompileFailed(); + return abi.decode(result, (TokenSupply)); + } + + function l1BlockNumber() internal view returns (uint64) { + (bool success, bytes memory result) = HLConstants.L1_BLOCK_NUMBER_PRECOMPILE_ADDRESS.staticcall(abi.encode()); + if (!success) revert PrecompileLib__L1BlockNumberPrecompileFailed(); + return abi.decode(result, (uint64)); + } + + function bbo(uint64 asset) internal view returns (Bbo memory) { + (bool success, bytes memory result) = HLConstants.BBO_PRECOMPILE_ADDRESS.staticcall(abi.encode(asset)); + if (!success) revert PrecompileLib__BboPrecompileFailed(); + return abi.decode(result, (Bbo)); + } + + function accountMarginSummary( + uint32 perpDexIndex, + address user + ) internal view returns (AccountMarginSummary memory) { + (bool success, bytes memory result) = HLConstants.ACCOUNT_MARGIN_SUMMARY_PRECOMPILE_ADDRESS.staticcall( + abi.encode(perpDexIndex, user) + ); + if (!success) revert PrecompileLib__AccountMarginSummaryPrecompileFailed(); + return abi.decode(result, (AccountMarginSummary)); + } + + function coreUserExists(address user) internal view returns (bool) { + (bool success, bytes memory result) = HLConstants.CORE_USER_EXISTS_PRECOMPILE_ADDRESS.staticcall( + abi.encode(user) + ); + if (!success) revert PrecompileLib__CoreUserExistsPrecompileFailed(); + return abi.decode(result, (CoreUserExists)).exists; + } + + /*////////////////////////////////////////////////////////////// + Structs + //////////////////////////////////////////////////////////////*/ + struct Position { + int64 szi; + uint64 entryNtl; + int64 isolatedRawUsd; + uint32 leverage; + bool isIsolated; + } + + struct SpotBalance { + uint64 total; + uint64 hold; + uint64 entryNtl; + } + + struct UserVaultEquity { + uint64 equity; + uint64 lockedUntilTimestamp; + } + + struct Withdrawable { + uint64 withdrawable; + } + + struct Delegation { + address validator; + uint64 amount; + uint64 lockedUntilTimestamp; + } + + struct DelegatorSummary { + uint64 delegated; + uint64 undelegated; + uint64 totalPendingWithdrawal; + uint64 nPendingWithdrawals; + } + + struct PerpAssetInfo { + string coin; + uint32 marginTableId; + uint8 szDecimals; + uint8 maxLeverage; + bool onlyIsolated; + } + + struct SpotInfo { + string name; + uint64[2] tokens; + } + + struct TokenInfo { + string name; + uint64[] spots; + uint64 deployerTradingFeeShare; + address deployer; + address evmContract; + uint8 szDecimals; + uint8 weiDecimals; + int8 evmExtraWeiDecimals; + } + + struct UserBalance { + address user; + uint64 balance; + } + + struct TokenSupply { + uint64 maxSupply; + uint64 totalSupply; + uint64 circulatingSupply; + uint64 futureEmissions; + UserBalance[] nonCirculatingUserBalances; + } + + struct Bbo { + uint64 bid; + uint64 ask; + } + + struct AccountMarginSummary { + int64 accountValue; + uint64 marginUsed; + uint64 ntlPos; + int64 rawUsd; + } + + struct CoreUserExists { + bool exists; + } + + error PrecompileLib__PositionPrecompileFailed(); + error PrecompileLib__SpotBalancePrecompileFailed(); + error PrecompileLib__VaultEquityPrecompileFailed(); + error PrecompileLib__WithdrawablePrecompileFailed(); + error PrecompileLib__DelegationsPrecompileFailed(); + error PrecompileLib__DelegatorSummaryPrecompileFailed(); + error PrecompileLib__MarkPxPrecompileFailed(); + error PrecompileLib__OraclePxPrecompileFailed(); + error PrecompileLib__SpotPxPrecompileFailed(); + error PrecompileLib__PerpAssetInfoPrecompileFailed(); + error PrecompileLib__SpotInfoPrecompileFailed(); + error PrecompileLib__TokenInfoPrecompileFailed(); + error PrecompileLib__TokenSupplyPrecompileFailed(); + error PrecompileLib__L1BlockNumberPrecompileFailed(); + error PrecompileLib__BboPrecompileFailed(); + error PrecompileLib__AccountMarginSummaryPrecompileFailed(); + error PrecompileLib__CoreUserExistsPrecompileFailed(); + error PrecompileLib__SpotIndexNotFound(); +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConstants.sol b/test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConstants.sol new file mode 100644 index 000000000..cbccb2840 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConstants.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { ICoreWriter } from "../interfaces/ICoreWriter.sol"; + +library HLConstants { + /*////////////////////////////////////////////////////////////// + Addresses + //////////////////////////////////////////////////////////////*/ + + address constant POSITION_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000800; + address constant SPOT_BALANCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; + address constant VAULT_EQUITY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000802; + address constant WITHDRAWABLE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000803; + address constant DELEGATIONS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000804; + address constant DELEGATOR_SUMMARY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000805; + address constant MARK_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000806; + address constant ORACLE_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000807; + address constant SPOT_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000808; + address constant L1_BLOCK_NUMBER_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000809; + address constant PERP_ASSET_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080a; + address constant SPOT_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080b; + address constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C; + address constant TOKEN_SUPPLY_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080D; + address constant BBO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080e; + address constant ACCOUNT_MARGIN_SUMMARY_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080F; + address constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; + + uint160 constant BASE_SYSTEM_ADDRESS = uint160(0x2000000000000000000000000000000000000000); + address constant HYPE_SYSTEM_ADDRESS = 0x2222222222222222222222222222222222222222; + + uint8 constant HYPE_EVM_EXTRA_DECIMALS = 10; + + /*////////////////////////////////////////////////////////////// + HYPE Token Index + //////////////////////////////////////////////////////////////*/ + function hypeTokenIndex() internal view returns (uint64) { + return block.chainid == 998 ? 1105 : 150; + } + + function isHype(uint64 index) internal view returns (bool) { + return index == hypeTokenIndex(); + } + + /*////////////////////////////////////////////////////////////// + CoreWriter Actions + //////////////////////////////////////////////////////////////*/ + + uint24 constant LIMIT_ORDER_ACTION = 1; + uint24 constant VAULT_TRANSFER_ACTION = 2; + + uint24 constant TOKEN_DELEGATE_ACTION = 3; + uint24 constant STAKING_DEPOSIT_ACTION = 4; + uint24 constant STAKING_WITHDRAW_ACTION = 5; + + uint24 constant SPOT_SEND_ACTION = 6; + uint24 constant USD_CLASS_TRANSFER_ACTION = 7; + + uint24 constant FINALIZE_EVM_CONTRACT_ACTION = 8; + uint24 constant ADD_API_WALLET_ACTION = 9; + uint24 constant CANCEL_ORDER_BY_OID_ACTION = 10; + uint24 constant CANCEL_ORDER_BY_CLOID_ACTION = 11; + + /*////////////////////////////////////////////////////////////// + Limit Order Time in Force + //////////////////////////////////////////////////////////////*/ + + uint8 public constant LIMIT_ORDER_TIF_ALO = 1; + uint8 public constant LIMIT_ORDER_TIF_GTC = 2; + uint8 public constant LIMIT_ORDER_TIF_IOC = 3; +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConversions.sol b/test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConversions.sol new file mode 100644 index 000000000..b473e5515 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/src/common/HLConversions.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { HLConstants } from "./HLConstants.sol"; +import { PrecompileLib } from "../PrecompileLib.sol"; +import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; + +library HLConversions { + error HLConversions__InvalidToken(uint64 token); + + /** + * @dev Converts an EVM amount to a Core (wei) amount, handling both positive and negative extra decimals + * Note: If evmExtraWeiDecimals > 0, and evmAmount < 10**evmExtraWeiDecimals, the result will be 0 + */ + function evmToWei(uint64 token, uint256 evmAmount) internal view returns (uint64) { + PrecompileLib.TokenInfo memory info = PrecompileLib.tokenInfo(uint32(token)); + + if (info.evmContract != address(0)) { + if (info.evmExtraWeiDecimals > 0) { + uint256 amount = evmAmount / (10 ** uint8(info.evmExtraWeiDecimals)); + return SafeCast.toUint64(amount); + } else if (info.evmExtraWeiDecimals < 0) { + uint256 amount = evmAmount * (10 ** uint8(-info.evmExtraWeiDecimals)); + return SafeCast.toUint64(amount); + } + } else if (HLConstants.isHype(token)) { + return SafeCast.toUint64(evmAmount / (10 ** HLConstants.HYPE_EVM_EXTRA_DECIMALS)); + } + + revert HLConversions__InvalidToken(token); + } + + function weiToEvm(uint64 token, uint64 amountWei) internal view returns (uint256) { + PrecompileLib.TokenInfo memory info = PrecompileLib.tokenInfo(uint32(token)); + if (info.evmContract != address(0)) { + if (info.evmExtraWeiDecimals > 0) { + return (uint256(amountWei) * (10 ** uint8(info.evmExtraWeiDecimals))); + } else if (info.evmExtraWeiDecimals < 0) { + return amountWei / (10 ** uint8(-info.evmExtraWeiDecimals)); + } + } else if (HLConstants.isHype(token)) { + return (uint256(amountWei) * (10 ** HLConstants.HYPE_EVM_EXTRA_DECIMALS)); + } + + revert HLConversions__InvalidToken(token); + } + + function szToWei(uint64 token, uint64 sz) internal view returns (uint64) { + PrecompileLib.TokenInfo memory info = PrecompileLib.tokenInfo(uint32(token)); + return sz * uint64(10 ** (info.weiDecimals - info.szDecimals)); + } + + function weiToSz(uint64 token, uint64 amountWei) internal view returns (uint64) { + PrecompileLib.TokenInfo memory info = PrecompileLib.tokenInfo(uint32(token)); + return amountWei / uint64(10 ** (info.weiDecimals - info.szDecimals)); + } + + // for USDC between spot and perp + function weiToPerp(uint64 amountWei) internal pure returns (uint64) { + return amountWei / 10 ** 2; + } + + function perpToWei(uint64 perpAmount) internal pure returns (uint64) { + return perpAmount * 10 ** 2; + } + + function spotToAssetId(uint64 spot) internal pure returns (uint64) { + return spot + 10000; + } + + function assetToSpotId(uint64 asset) internal pure returns (uint64) { + return asset - 10000; + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ICoreWriter.sol b/test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ICoreWriter.sol new file mode 100644 index 000000000..3dfb730cc --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ICoreWriter.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface ICoreWriter { + function sendRawAction(bytes calldata data) external; +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ITokenRegistry.sol b/test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ITokenRegistry.sol new file mode 100644 index 000000000..e4fee44db --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/src/interfaces/ITokenRegistry.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface ITokenRegistry { + function getTokenIndex(address evmContract) external view returns (uint32 index); +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/BaseSimulatorTest.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/BaseSimulatorTest.sol new file mode 100644 index 000000000..ad4bd64e7 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/BaseSimulatorTest.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; +import { PrecompileLib } from "../src/PrecompileLib.sol"; +import { CoreWriterLib } from "../src/CoreWriterLib.sol"; +import { HLConversions } from "../src/common/HLConversions.sol"; +import { HLConstants } from "../src/common/HLConstants.sol"; +import { HyperCore } from "./simulation/HyperCore.sol"; +import { CoreSimulatorLib } from "./simulation/CoreSimulatorLib.sol"; + +/** + * @title BaseSimulatorTest + * @notice Base test contract that sets up the HyperCore simulation + */ +abstract contract BaseSimulatorTest is Test { + using PrecompileLib for address; + using HLConversions for *; + + HyperCore public hyperCore; + + // Common token addresses + address public constant USDT0 = 0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb; + address public constant uBTC = 0x9FDBdA0A5e284c32744D2f17Ee5c74B284993463; + address public constant uETH = 0xBe6727B535545C67d5cAa73dEa54865B92CF7907; + address public constant uSOL = 0x068f321Fa8Fb9f0D135f290Ef6a3e2813e1c8A29; + + // Common token indices + uint64 public constant USDC_TOKEN = 0; + uint64 public constant HYPE_TOKEN = 150; + + address user = makeAddr("user"); + + function setUp() public virtual { + string memory hyperliquidRpc = "https://rpc.hyperliquid.xyz/evm"; + vm.createSelectFork(hyperliquidRpc); + + hyperCore = CoreSimulatorLib.init(); + + hyperCore.forceAccountActivation(user); + hyperCore.forceSpot(user, USDC_TOKEN, 1000e8); + hyperCore.forcePerpBalance(user, 1000e6); + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreSimulatorLib.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreSimulatorLib.sol new file mode 100644 index 000000000..ce380261d --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreSimulatorLib.sol @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Vm } from "forge-std/Vm.sol"; + +import { HyperCore } from "./HyperCore.sol"; +import { CoreWriterSim } from "./CoreWriterSim.sol"; +import { PrecompileSim } from "./PrecompileSim.sol"; + +import { HLConstants } from "../../src/PrecompileLib.sol"; + +Vm constant vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); +CoreWriterSim constant coreWriter = CoreWriterSim(0x3333333333333333333333333333333333333333); + +contract HypeSystemContract { + receive() external payable { + coreWriter.nativeTransferCallback{ value: msg.value }(msg.sender, msg.sender, msg.value); + } +} + +/** + * @title CoreSimulatorLib + * @dev A library used to simulate HyperCore functionality in foundry tests + */ +library CoreSimulatorLib { + uint256 constant NUM_PRECOMPILES = 17; + + HyperCore constant hyperCore = HyperCore(payable(0x9999999999999999999999999999999999999999)); + + // ERC20 Transfer event signature + bytes32 constant TRANSFER_EVENT_SIG = keccak256("Transfer(address,address,uint256)"); + + function init() internal returns (HyperCore) { + vm.pauseGasMetering(); + + HyperCore coreImpl = new HyperCore(); + + vm.etch(address(hyperCore), address(coreImpl).code); + vm.etch(address(coreWriter), type(CoreWriterSim).runtimeCode); + + // Initialize precompiles + for (uint160 i = 0; i < NUM_PRECOMPILES; i++) { + address precompileAddress = address(uint160(0x0000000000000000000000000000000000000800) + i); + vm.etch(precompileAddress, type(PrecompileSim).runtimeCode); + vm.allowCheatcodes(precompileAddress); + } + + // System addresses + address hypeSystemAddress = address(0x2222222222222222222222222222222222222222); + vm.etch(hypeSystemAddress, type(HypeSystemContract).runtimeCode); + + // Start recording logs for token transfer tracking + vm.recordLogs(); + + vm.allowCheatcodes(address(hyperCore)); + vm.allowCheatcodes(address(coreWriter)); + + vm.resumeGasMetering(); + + return hyperCore; + } + + function nextBlock() internal { + // Get all recorded logs + Vm.Log[] memory entries = vm.getRecordedLogs(); + + // Process any ERC20 transfers to system addresses (EVM->Core transfers are processed before CoreWriter actions) + for (uint256 i = 0; i < entries.length; i++) { + Vm.Log memory entry = entries[i]; + + // Check if it's a Transfer event + if (entry.topics[0] == TRANSFER_EVENT_SIG) { + address from = address(uint160(uint256(entry.topics[1]))); + address to = address(uint160(uint256(entry.topics[2]))); + uint256 amount = abi.decode(entry.data, (uint256)); + + // Check if destination is a system address + if (isSystemAddress(to)) { + uint64 tokenIndex = getTokenIndexFromSystemAddress(to); + + // Call tokenTransferCallback on HyperCoreWrite + hyperCore.executeTokenTransfer(address(0), tokenIndex, from, amount); + } + } + } + + // Clear recorded logs for next block + vm.recordLogs(); + + // Advance block + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + + // liquidate any positions that are liquidatable + hyperCore.liquidatePositions(); + + // Process any pending actions + coreWriter.executeQueuedActions(); + + // Process pending orders + hyperCore.processPendingOrders(); + } + + ////// TESTING CONFIG SETTERS ///////// + + function setRevertOnFailure(bool _revertOnFailure) internal { + coreWriter.setRevertOnFailure(_revertOnFailure); + } + + // cheatcodes // + function forceAccountActivation(address account) internal { + hyperCore.forceAccountActivation(account); + } + + function forceSpot(address account, uint64 token, uint64 _wei) internal { + hyperCore.forceSpot(account, token, _wei); + } + + function forceTokenInfo( + uint64 index, + string memory name, + address evmContract, + uint8 szDecimals, + uint8 weiDecimals, + int8 evmExtraWeiDecimals + ) internal { + hyperCore.forceTokenInfo(index, name, evmContract, szDecimals, weiDecimals, evmExtraWeiDecimals); + } + + function forcePerpBalance(address account, uint64 usd) internal { + hyperCore.forcePerpBalance(account, usd); + } + + function forceStaking(address account, uint64 _wei) internal { + hyperCore.forceStaking(account, _wei); + } + + function forceDelegation(address account, address validator, uint64 amount, uint64 lockedUntilTimestamp) internal { + hyperCore.forceDelegation(account, validator, amount, lockedUntilTimestamp); + } + + function forceVaultEquity(address account, address vault, uint64 usd, uint64 lockedUntilTimestamp) internal { + hyperCore.forceVaultEquity(account, vault, usd, lockedUntilTimestamp); + } + + function setMarkPx(uint32 perp, uint64 markPx) internal { + hyperCore.setMarkPx(perp, markPx); + } + + function setMarkPx(uint32 perp, uint64 priceDiffBps, bool isIncrease) internal { + hyperCore.setMarkPx(perp, priceDiffBps, isIncrease); + } + + function setSpotPx(uint32 spotMarketId, uint64 spotPx) internal { + hyperCore.setSpotPx(spotMarketId, spotPx); + } + + function setSpotPx(uint32 spotMarketId, uint64 priceDiffBps, bool isIncrease) internal { + hyperCore.setSpotPx(spotMarketId, priceDiffBps, isIncrease); + } + + function setVaultMultiplier(address vault, uint64 multiplier) internal { + hyperCore.setVaultMultiplier(vault, multiplier); + } + + ///// VIEW AND PURE ///////// + + function isSystemAddress(address addr) internal view returns (bool) { + // Check if it's the HYPE system address + if (addr == address(0x2222222222222222222222222222222222222222)) { + return true; + } + + // Check if it's a token system address (0x2000...0000 + index) + uint160 baseAddr = uint160(0x2000000000000000000000000000000000000000); + uint160 addrInt = uint160(addr); + + if (addrInt >= baseAddr && addrInt < baseAddr + 10000) { + uint64 tokenIndex = uint64(addrInt - baseAddr); + + return tokenExists(tokenIndex); + } + + return false; + } + + function getTokenIndexFromSystemAddress(address systemAddr) internal pure returns (uint64) { + if (systemAddr == address(0x2222222222222222222222222222222222222222)) { + return 150; // HYPE token index + } + return uint64(uint160(systemAddr) - uint160(0x2000000000000000000000000000000000000000)); + } + + function tokenExists(uint64 token) internal view returns (bool) { + (bool success, ) = HLConstants.TOKEN_INFO_PRECOMPILE_ADDRESS.staticcall(abi.encode(token)); + return success; + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreWriterSim.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreWriterSim.sol new file mode 100644 index 000000000..2c3e014ef --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/CoreWriterSim.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { Heap } from "@openzeppelin/contracts/utils/structs/Heap.sol"; +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; +import { HyperCore, CoreExecution } from "./HyperCore.sol"; + +contract CoreWriterSim { + using Address for address; + using Heap for Heap.Uint256Heap; + + uint128 private _sequence; + + Heap.Uint256Heap private _actionQueue; + + struct Action { + uint256 timestamp; + bytes data; + uint256 value; + } + + mapping(uint256 id => Action) _actions; + + event RawAction(address indexed user, bytes data); + + HyperCore constant _hyperCore = HyperCore(payable(0x9999999999999999999999999999999999999999)); + + /////// testing config + ///////////////////////// + bool revertOnFailure; + + function setRevertOnFailure(bool _revertOnFailure) public { + revertOnFailure = _revertOnFailure; + } + + function enqueueAction(bytes memory data, uint256 value) public { + enqueueAction(block.timestamp, data, value); + } + + function enqueueAction(uint256 timestamp, bytes memory data, uint256 value) public { + uint256 uniqueId = (uint256(timestamp) << 128) | uint256(_sequence++); + + _actions[uniqueId] = Action(timestamp, data, value); + _actionQueue.insert(uniqueId); + } + + function executeQueuedActions() external { + while (_actionQueue.length() > 0) { + Action memory action = _actions[_actionQueue.peek()]; + + // the action queue is a priority queue so the timestamp takes precedence in the + // ordering which means we can safely stop processing if the actions are delayed + if (action.timestamp > block.timestamp) { + break; + } + + (bool success, ) = address(_hyperCore).call{ value: action.value }(action.data); + + if (revertOnFailure && !success) { + revert("CoreWriter action failed: Reverting due to revertOnFailure flag"); + } + + _actionQueue.pop(); + } + + _hyperCore.processStakingWithdrawals(); + } + + function tokenTransferCallback(uint64 token, address from, uint256 value) public { + // there's a special case when transferring to the L1 via the system address which + // is that the balance isn't reflected on the L1 until after the EVM block has finished + // and the subsequent EVM block has been processed, this means that the balance can be + // in limbo for the user + tokenTransferCallback(msg.sender, token, from, value); + } + + function tokenTransferCallback(address sender, uint64 token, address from, uint256 value) public { + enqueueAction(abi.encodeCall(CoreExecution.executeTokenTransfer, (sender, token, from, value)), 0); + } + + function nativeTransferCallback(address sender, address from, uint256 value) public payable { + enqueueAction(abi.encodeCall(CoreExecution.executeNativeTransfer, (sender, from, value)), value); + } + + function sendRawAction(bytes calldata data) external { + uint8 version = uint8(data[0]); + require(version == 1); + + uint24 kind = (uint24(uint8(data[1])) << 16) | (uint24(uint8(data[2])) << 8) | (uint24(uint8(data[3]))); + + bytes memory call = abi.encodeCall(HyperCore.executeRawAction, (msg.sender, kind, data[4:])); + + enqueueAction(block.timestamp, call, 0); + + emit RawAction(msg.sender, data); + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/HyperCore.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/HyperCore.sol new file mode 100644 index 000000000..bbeaf0b0c --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/HyperCore.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { CoreExecution } from "./hyper-core/CoreExecution.sol"; +import { DoubleEndedQueue } from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol"; +import { HLConstants } from "../../src/PrecompileLib.sol"; + +contract HyperCore is CoreExecution { + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + + function executeRawAction(address sender, uint24 kind, bytes calldata data) public payable { + if (kind == HLConstants.LIMIT_ORDER_ACTION) { + LimitOrderAction memory action = abi.decode(data, (LimitOrderAction)); + + // for perps (check that the ID is not a spot asset ID) + if (action.asset < 1e4 || action.asset >= 1e5) { + executePerpLimitOrder(sender, action); + } else { + executeSpotLimitOrder(sender, action); + } + return; + } + + if (kind == HLConstants.VAULT_TRANSFER_ACTION) { + executeVaultTransfer(sender, abi.decode(data, (VaultTransferAction))); + return; + } + + if (kind == HLConstants.TOKEN_DELEGATE_ACTION) { + executeTokenDelegate(sender, abi.decode(data, (TokenDelegateAction))); + return; + } + + if (kind == HLConstants.STAKING_DEPOSIT_ACTION) { + executeStakingDeposit(sender, abi.decode(data, (StakingDepositAction))); + return; + } + + if (kind == HLConstants.STAKING_WITHDRAW_ACTION) { + executeStakingWithdraw(sender, abi.decode(data, (StakingWithdrawAction))); + return; + } + + if (kind == HLConstants.SPOT_SEND_ACTION) { + executeSpotSend(sender, abi.decode(data, (SpotSendAction))); + return; + } + + if (kind == HLConstants.USD_CLASS_TRANSFER_ACTION) { + executeUsdClassTransfer(sender, abi.decode(data, (UsdClassTransferAction))); + return; + } + } + + /// @dev unstaking takes 7 days and after which it will automatically appear in the users + /// spot balance so we need to check this at the end of each operation to simulate that. + function processStakingWithdrawals() public { + while (_withdrawQueue.length() > 0) { + WithdrawRequest memory request = deserializeWithdrawRequest(_withdrawQueue.front()); + + if (request.lockedUntilTimestamp > block.timestamp) { + break; + } + + _withdrawQueue.popFront(); + + _accounts[request.account].spot[HLConstants.hypeTokenIndex()] += request.amount; + } + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/PrecompileSim.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/PrecompileSim.sol new file mode 100644 index 000000000..c17e0188e --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/PrecompileSim.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { HyperCore } from "./HyperCore.sol"; + +import { Vm } from "forge-std/Vm.sol"; + +/// @dev this contract is deployed for each different precompile address such that the fallback can be executed for each +contract PrecompileSim { + Vm internal constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + HyperCore constant _hyperCore = HyperCore(payable(0x9999999999999999999999999999999999999999)); + + address constant POSITION_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000800; + address constant SPOT_BALANCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; + address constant VAULT_EQUITY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000802; + address constant WITHDRAWABLE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000803; + address constant DELEGATIONS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000804; + address constant DELEGATOR_SUMMARY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000805; + address constant MARK_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000806; + address constant ORACLE_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000807; + address constant SPOT_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000808; + address constant L1_BLOCK_NUMBER_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000809; + address constant PERP_ASSET_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080a; + address constant SPOT_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080b; + address constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C; + address constant TOKEN_SUPPLY_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080D; + address constant BBO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080e; + address constant ACCOUNT_MARGIN_SUMMARY_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080F; + address constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; + + receive() external payable {} + + fallback(bytes calldata data) external returns (bytes memory) { + if (address(this) == SPOT_BALANCE_PRECOMPILE_ADDRESS) { + (address user, uint64 token) = abi.decode(data, (address, uint64)); + return abi.encode(_hyperCore.readSpotBalance(user, token)); + } + + if (address(this) == VAULT_EQUITY_PRECOMPILE_ADDRESS) { + (address user, address vault) = abi.decode(data, (address, address)); + return abi.encode(_hyperCore.readUserVaultEquity(user, vault)); + } + + if (address(this) == WITHDRAWABLE_PRECOMPILE_ADDRESS) { + address user = abi.decode(data, (address)); + return abi.encode(_hyperCore.readWithdrawable(user)); + } + + if (address(this) == DELEGATIONS_PRECOMPILE_ADDRESS) { + address user = abi.decode(data, (address)); + return abi.encode(_hyperCore.readDelegations(user)); + } + + if (address(this) == DELEGATOR_SUMMARY_PRECOMPILE_ADDRESS) { + address user = abi.decode(data, (address)); + return abi.encode(_hyperCore.readDelegatorSummary(user)); + } + + if (address(this) == POSITION_PRECOMPILE_ADDRESS) { + (address user, uint16 perp) = abi.decode(data, (address, uint16)); + return abi.encode(_hyperCore.readPosition(user, perp)); + } + + if (address(this) == CORE_USER_EXISTS_PRECOMPILE_ADDRESS) { + address user = abi.decode(data, (address)); + return abi.encode(_hyperCore.coreUserExists(user)); + } + + if (address(this) == MARK_PX_PRECOMPILE_ADDRESS) { + uint32 perp = abi.decode(data, (uint32)); + return abi.encode(_hyperCore.readMarkPx(perp)); + } + + if (address(this) == ACCOUNT_MARGIN_SUMMARY_PRECOMPILE_ADDRESS) { + address user = abi.decode(data, (address)); + return abi.encode(_hyperCore.readAccountMarginSummary(user)); + } + + if (address(this) == TOKEN_INFO_PRECOMPILE_ADDRESS) { + uint64 index = abi.decode(data, (uint64)); + return abi.encode(_hyperCore.readTokenInfo(index)); + } + + return _makeRpcCall(address(this), data); + } + + function _makeRpcCall(address target, bytes memory params) internal returns (bytes memory) { + // Construct the JSON-RPC payload + string memory jsonPayload = string.concat( + '[{"to":"', + vm.toString(target), + '","data":"', + vm.toString(params), + '"},"latest"]' + ); + + // Make the RPC call + return vm.rpc("eth_call", jsonPayload); + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreExecution.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreExecution.sol new file mode 100644 index 000000000..3ede903af --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreExecution.sol @@ -0,0 +1,575 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { DoubleEndedQueue } from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol"; +import { Heap } from "@openzeppelin/contracts/utils/structs/Heap.sol"; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +import { PrecompileLib } from "../../../src/PrecompileLib.sol"; +import { CoreWriterLib } from "../../../src/CoreWriterLib.sol"; +import { HLConversions } from "../../../src/common/HLConversions.sol"; + +import { RealL1Read } from "../../utils/RealL1Read.sol"; +import { CoreView } from "./CoreView.sol"; + +contract CoreExecution is CoreView { + using SafeCast for uint256; + using EnumerableSet for EnumerableSet.AddressSet; + using EnumerableSet for EnumerableSet.Bytes32Set; + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + using Heap for Heap.Uint256Heap; + using SafeERC20 for IERC20; + using RealL1Read for *; + + using EnumerableSet for EnumerableSet.UintSet; + + EnumerableSet.Bytes32Set private _openPerpPositions; + + // Maps user address to a set of perp indices they have active positions in + mapping(address => EnumerableSet.UintSet) private _userPerpPositions; + + uint16 constant MAX_PERP_INDEX = 256; // Adjust based on expected number of perp markets + uint64 constant MM_BPS = 125; // 1.25% maintenance margin fraction (adjust as needed, e.g., for 40x max leverage) + + function _getKey(address user, uint16 perpIndex) internal pure returns (bytes32) { + return bytes32((uint256(uint160(user)) << 16) | uint256(perpIndex)); + } + + function executeTokenTransfer( + address, + uint64 token, + address from, + uint256 value + ) public payable initAccountWithToken(from, token) { + if (_accounts[from].activated) { + _accounts[from].spot[token] += toWei(value, _tokens[token].evmExtraWeiDecimals); + } else { + _latentSpotBalance[from][token] += toWei(value, _tokens[token].evmExtraWeiDecimals); + } + } + + function executeNativeTransfer( + address, + address from, + uint256 value + ) public payable initAccountWithToken(from, HYPE_TOKEN_INDEX) { + if (_accounts[from].activated) { + _accounts[from].spot[HYPE_TOKEN_INDEX] += (value / 1e10).toUint64(); + } else { + _latentSpotBalance[from][HYPE_TOKEN_INDEX] += (value / 1e10).toUint64(); + } + } + + function executePerpLimitOrder( + address sender, + LimitOrderAction memory action + ) public initAccountWithPerp(sender, uint16(action.asset)) { + uint16 perpIndex = uint16(action.asset); + PrecompileLib.Position memory position = _accounts[sender].positions[perpIndex]; + + bool isolated = position.isIsolated; + + uint256 markPx = PrecompileLib.markPx(perpIndex); + + if (!isolated) { + if (action.isBuy) { + if (markPx <= action.limitPx) { + _executePerpLong(sender, action, markPx); + } + } else { + if (markPx >= action.limitPx) { + _executePerpShort(sender, action, markPx); + } + } + } + } + + function _executePerpLong(address sender, LimitOrderAction memory action, uint256 markPx) internal { + uint16 perpIndex = uint16(action.asset); + int64 szi = _accounts[sender].positions[perpIndex].szi; + uint32 leverage = _accounts[sender].positions[perpIndex].leverage; + + uint64 _markPx = markPx.toUint64(); + + // Add require checks for safety (e.g., leverage > 0, action.sz > 0, etc.) + require(leverage > 0, "Invalid leverage"); + require(action.sz > 0, "Invalid size"); + require(markPx > 0, "Invalid price"); + int64 newSzi = szi + int64(action.sz); + + if (szi >= 0) { + // No PnL realization for same-direction increase + // Update position size (more positive for long) + _accounts[sender].positions[perpIndex].szi += int64(action.sz); + + // Additive update to entryNtl to preserve weighted average + // New entryNtl = old_entryNtl + (action.sz * markPx) + _accounts[sender].positions[perpIndex].entryNtl += uint64(action.sz) * uint64(markPx); + + // Deduct additional margin for the added size only + // To minimize integer truncation: (action.sz * markPx) / leverage + _accounts[sender].perpBalance -= (uint64(action.sz) * uint64(markPx)) / leverage; + + _accounts[sender].margin[perpIndex] += (uint64(action.sz) * uint64(markPx)) / leverage; + } else { + if (newSzi <= 0) { + uint64 avgEntryPrice = _accounts[sender].positions[perpIndex].entryNtl / uint64(-szi); + int64 pnl = int64(action.sz) * (int64(avgEntryPrice) - int64(_markPx)); + + uint64 closedMargin = ((uint64(action.sz) * _accounts[sender].positions[perpIndex].entryNtl) / + uint64(-szi)) / leverage; + _accounts[sender].perpBalance += closedMargin; + + _accounts[sender].perpBalance = pnl > 0 + ? _accounts[sender].perpBalance + uint64(pnl) + : _accounts[sender].perpBalance - uint64(-pnl); + _accounts[sender].margin[perpIndex] -= closedMargin; + + _accounts[sender].positions[perpIndex].szi = newSzi; + _accounts[sender].positions[perpIndex].entryNtl = uint64(-newSzi) * avgEntryPrice; + } else { + uint64 avgEntryPrice = _accounts[sender].positions[perpIndex].entryNtl / uint64(-szi); + int64 pnl = int64(-szi) * (int64(avgEntryPrice) - int64(_markPx)); + _accounts[sender].perpBalance = pnl > 0 + ? _accounts[sender].perpBalance + uint64(pnl) + : _accounts[sender].perpBalance - uint64(-pnl); + uint64 oldMargin = _accounts[sender].positions[perpIndex].entryNtl / leverage; + _accounts[sender].perpBalance += oldMargin; + uint64 newLongSize = uint64(newSzi); + uint64 newMargin = (newLongSize * _markPx) / leverage; + _accounts[sender].perpBalance -= newMargin; + _accounts[sender].margin[perpIndex] += newMargin; + _accounts[sender].positions[perpIndex].szi = newSzi; + _accounts[sender].positions[perpIndex].entryNtl = newLongSize * _markPx; + } + } + + bytes32 key = _getKey(sender, perpIndex); + if (szi == 0 && newSzi != 0) { + _openPerpPositions.add(key); + _userPerpPositions[sender].add(perpIndex); + } else if (szi != 0 && newSzi == 0) { + _openPerpPositions.remove(key); + _userPerpPositions[sender].remove(perpIndex); + } + } + + function _executePerpShort(address sender, LimitOrderAction memory action, uint256 markPx) internal { + uint16 perpIndex = uint16(action.asset); + int64 szi = _accounts[sender].positions[perpIndex].szi; + uint32 leverage = _accounts[sender].positions[perpIndex].leverage; + + uint64 _markPx = markPx.toUint64(); + + // Add require checks for safety (e.g., leverage > 0, action.sz > 0, etc.) + require(leverage > 0, "Invalid leverage"); + require(action.sz > 0, "Invalid size"); + require(markPx > 0, "Invalid price"); + int64 newSzi = szi - int64(action.sz); + + if (szi <= 0) { + // No PnL realization for same-direction increase + // Update position size (more negative for short) + _accounts[sender].positions[perpIndex].szi -= int64(action.sz); + + // Additive update to entryNtl to preserve weighted average + // New entryNtl = old_entryNtl + (action.sz * markPx) + _accounts[sender].positions[perpIndex].entryNtl += uint64(action.sz) * uint64(markPx); + + // Deduct additional margin for the added size only + // To minimize integer truncation: (action.sz * markPx) / leverage + _accounts[sender].perpBalance -= (uint64(action.sz) * uint64(markPx)) / leverage; + + _accounts[sender].margin[perpIndex] += (uint64(action.sz) * uint64(markPx)) / leverage; + } else { + if (newSzi >= 0) { + uint64 avgEntryPrice = _accounts[sender].positions[perpIndex].entryNtl / uint64(szi); + int64 pnl = int64(action.sz) * (int64(_markPx) - int64(avgEntryPrice)); + uint64 closedMargin = ((uint64(action.sz) * _accounts[sender].positions[perpIndex].entryNtl) / + uint64(szi)) / leverage; + _accounts[sender].perpBalance += closedMargin; + + _accounts[sender].perpBalance = pnl > 0 + ? _accounts[sender].perpBalance + uint64(pnl) + : _accounts[sender].perpBalance - uint64(-pnl); + + _accounts[sender].margin[perpIndex] -= closedMargin; + _accounts[sender].positions[perpIndex].szi = newSzi; + _accounts[sender].positions[perpIndex].entryNtl = uint64(newSzi) * avgEntryPrice; + } else { + uint64 avgEntryPrice = _accounts[sender].positions[perpIndex].entryNtl / uint64(szi); + int64 pnl = int64(szi) * (int64(_markPx) - int64(avgEntryPrice)); + _accounts[sender].perpBalance = pnl > 0 + ? _accounts[sender].perpBalance + uint64(pnl) + : _accounts[sender].perpBalance - uint64(-pnl); + uint64 oldMargin = _accounts[sender].positions[perpIndex].entryNtl / leverage; + _accounts[sender].perpBalance += oldMargin; + uint64 newShortSize = uint64(-newSzi); + uint64 newMargin = (newShortSize * _markPx) / leverage; + _accounts[sender].perpBalance -= newMargin; + _accounts[sender].margin[perpIndex] += newMargin; + _accounts[sender].positions[perpIndex].szi = newSzi; + _accounts[sender].positions[perpIndex].entryNtl = newShortSize * _markPx; + } + } + + bytes32 key = _getKey(sender, perpIndex); + if (szi == 0 && newSzi != 0) { + _openPerpPositions.add(key); + _userPerpPositions[sender].add(perpIndex); + } else if (szi != 0 && newSzi == 0) { + _openPerpPositions.remove(key); + _userPerpPositions[sender].remove(perpIndex); + } + + // Optional: Add margin sufficiency check after updates + // e.g., require(_accounts[sender].perpBalance >= someMaintenanceMargin, "Insufficient margin"); + } + + // basic simulation of spot trading, not accounting for orderbook depth, or fees + function executeSpotLimitOrder( + address sender, + LimitOrderAction memory action + ) public initAccountWithSpotMarket(sender, uint32(HLConversions.assetToSpotId(action.asset))) { + PrecompileLib.SpotInfo memory spotInfo = RealL1Read.spotInfo(uint32(HLConversions.assetToSpotId(action.asset))); + + PrecompileLib.TokenInfo memory baseToken = _tokens[spotInfo.tokens[0]]; + + uint64 spotPx = readSpotPx(uint32(HLConversions.assetToSpotId(action.asset))); + + uint64 fromToken; + uint64 toToken; + + if (isActionExecutable(action, spotPx)) { + if (action.isBuy) { + fromToken = spotInfo.tokens[1]; + toToken = spotInfo.tokens[0]; + + uint64 amountIn = action.sz * spotPx; + uint64 amountOut = action.sz * (10 ** (baseToken.weiDecimals - baseToken.szDecimals)).toUint64(); + + if (_accounts[sender].spot[fromToken] >= amountIn) { + _accounts[sender].spot[fromToken] -= amountIn; + _accounts[sender].spot[toToken] += amountOut; + } else { + revert("insufficient balance"); + } + } else { + fromToken = spotInfo.tokens[0]; + toToken = spotInfo.tokens[1]; + + uint64 amountIn = action.sz * (10 ** (baseToken.weiDecimals - baseToken.szDecimals)).toUint64(); + + uint64 amountOut = action.sz * spotPx; + + if (_accounts[sender].spot[fromToken] >= amountIn) { + _accounts[sender].spot[fromToken] -= amountIn; + _accounts[sender].spot[toToken] += amountOut; + } else { + revert("insufficient balance"); + } + } + } else { + _pendingOrders.push(PendingOrder({ sender: sender, action: action })); + } + } + + function executeSpotSend( + address sender, + SpotSendAction memory action + ) + public + whenActivated(sender) + initAccountWithToken(sender, action.token) + initAccountWithToken(action.destination, action.token) + { + if (action._wei > _accounts[sender].spot[action.token]) { + revert("insufficient balance"); + } + + // handle account activation case + if (_accounts[action.destination].activated == false) { + _chargeUSDCFee(sender); + + _accounts[action.destination].activated = true; + + _accounts[sender].spot[action.token] -= action._wei; + _accounts[action.destination].spot[action.token] += _latentSpotBalance[sender][action.token] + action._wei; + + // this will no longer be needed + _latentSpotBalance[sender][action.token] = 0; + + // officially init the destination account + _initializedAccounts[action.destination] = true; + _initializedSpotBalance[action.destination][action.token] = true; + return; + } + + address systemAddress = CoreWriterLib.getSystemAddress(action.token); + + _accounts[sender].spot[action.token] -= action._wei; + + if (action.destination != systemAddress) { + _accounts[action.destination].spot[action.token] += action._wei; + } else { + uint256 transferAmount; + if (action.token == HYPE_TOKEN_INDEX) { + transferAmount = action._wei * 1e10; + deal(systemAddress, systemAddress.balance + transferAmount); + vm.prank(systemAddress); + (bool success, ) = address(sender).call{ value: transferAmount, gas: 30000 }(""); + if (!success) { + revert("transfer failed"); + } + return; + } + + address evmContract = _tokens[action.token].evmContract; + transferAmount = fromWei(action._wei, _tokens[action.token].evmExtraWeiDecimals); + deal(evmContract, systemAddress, IERC20(evmContract).balanceOf(systemAddress) + transferAmount); + vm.prank(systemAddress); + IERC20(evmContract).safeTransfer(action.destination, transferAmount); + } + } + + function _chargeUSDCFee(address sender) internal { + if (_accounts[sender].spot[USDC_TOKEN_INDEX] >= 1e8) { + _accounts[sender].spot[USDC_TOKEN_INDEX] -= 1e8; + } else if (_accounts[sender].perpBalance >= 1e8) { + _accounts[sender].perpBalance -= 1e8; + } else { + revert("insufficient USDC balance for fee"); + } + } + + function executeUsdClassTransfer( + address sender, + UsdClassTransferAction memory action + ) public whenActivated(sender) { + if (action.toPerp) { + if (fromPerp(action.ntl) <= _accounts[sender].spot[USDC_TOKEN_INDEX]) { + _accounts[sender].perpBalance += action.ntl; + _accounts[sender].spot[USDC_TOKEN_INDEX] -= fromPerp(action.ntl); + } + } else { + if (action.ntl <= _accounts[sender].perpBalance) { + _accounts[sender].perpBalance -= action.ntl; + _accounts[sender].spot[USDC_TOKEN_INDEX] += fromPerp(action.ntl); + } + } + } + + function executeVaultTransfer( + address sender, + VaultTransferAction memory action + ) public whenActivated(sender) initAccountWithVault(sender, action.vault) { + // first update their vault equity + _accounts[sender].vaultEquity[action.vault].equity = readUserVaultEquity(sender, action.vault).equity; + + if (action.isDeposit) { + if (action.usd <= _accounts[sender].perpBalance) { + _accounts[sender].vaultEquity[action.vault].equity += action.usd; + _accounts[sender].vaultEquity[action.vault].lockedUntilTimestamp = uint64( + (block.timestamp + 86400) * 1000 + ); + _accounts[sender].perpBalance -= action.usd; + _vaultEquity[action.vault] += action.usd; + } else { + revert("insufficient balance"); + } + } else { + PrecompileLib.UserVaultEquity storage userVaultEquity = _accounts[sender].vaultEquity[action.vault]; + + // a zero amount means withdraw the entire amount + action.usd = action.usd == 0 ? userVaultEquity.equity : action.usd; + + // the vaults have a minimum withdraw of 1 / 100,000,000 + if (action.usd < _vaultEquity[action.vault] / 1e8) { + revert("does not meet minimum withdraw"); + } + + if ( + action.usd <= userVaultEquity.equity && userVaultEquity.lockedUntilTimestamp / 1000 <= block.timestamp + ) { + userVaultEquity.equity -= action.usd; + _accounts[sender].perpBalance += action.usd; + } else { + revert("equity too low, or locked"); + } + } + } + + function executeStakingDeposit(address sender, StakingDepositAction memory action) public whenActivated(sender) { + if (action._wei <= _accounts[sender].spot[HYPE_TOKEN_INDEX]) { + _accounts[sender].spot[HYPE_TOKEN_INDEX] -= action._wei; + _accounts[sender].staking += action._wei; + } + } + + function executeStakingWithdraw(address sender, StakingWithdrawAction memory action) public whenActivated(sender) { + if (action._wei <= _accounts[sender].staking) { + _accounts[sender].staking -= action._wei; + + WithdrawRequest memory withrawRequest = WithdrawRequest({ + account: sender, + amount: action._wei, + lockedUntilTimestamp: uint32(block.timestamp + 7 days) + }); + + _withdrawQueue.pushBack(serializeWithdrawRequest(withrawRequest)); + } + } + + function executeTokenDelegate(address sender, TokenDelegateAction memory action) public whenActivated(sender) { + require(_validators.contains(action.validator)); + + if (action.isUndelegate) { + PrecompileLib.Delegation storage delegation = _accounts[sender].delegations[action.validator]; + if (action._wei <= delegation.amount && block.timestamp * 1000 > delegation.lockedUntilTimestamp) { + _accounts[sender].staking += action._wei; + delegation.amount -= action._wei; + } + } else { + if (action._wei <= _accounts[sender].staking) { + _accounts[sender].staking -= action._wei; + _accounts[sender].delegations[action.validator].amount += action._wei; + _accounts[sender].delegations[action.validator].lockedUntilTimestamp = ((block.timestamp + 84600) * + 1000).toUint64(); + } + } + } + + function setMarkPx(uint32 perp, uint64 priceDiffBps, bool isIncrease) public { + uint64 basePrice = readMarkPx(perp); + if (isIncrease) { + _perpMarkPrice[perp] = (basePrice * (10000 + priceDiffBps)) / 10000; + } else { + _perpMarkPrice[perp] = (basePrice * (10000 - priceDiffBps)) / 10000; + } + } + + function setMarkPx(uint32 perp, uint64 markPx) public { + _perpMarkPrice[perp] = markPx; + } + + function setSpotPx(uint32 spotMarketId, uint64 priceDiffBps, bool isIncrease) public { + uint64 basePrice = readSpotPx(spotMarketId); + if (isIncrease) { + _spotPrice[spotMarketId] = (basePrice * (10000 + priceDiffBps)) / 10000; + } else { + _spotPrice[spotMarketId] = (basePrice * (10000 - priceDiffBps)) / 10000; + } + } + + function setSpotPx(uint32 spotMarketId, uint64 spotPx) public { + _spotPrice[spotMarketId] = spotPx; + } + + function isActionExecutable(LimitOrderAction memory action, uint64 px) internal pure returns (bool) { + bool executable = action.isBuy ? action.limitPx >= px : action.limitPx <= px; + return executable; + } + + function setVaultMultiplier(address vault, uint64 multiplier) public { + _vaultMultiplier[vault] = multiplier; + } + + function processPendingOrders() public { + for (uint256 i = _pendingOrders.length; i > 0; i--) { + PendingOrder memory order = _pendingOrders[i - 1]; + uint32 spotMarketId = order.action.asset - 1e4; + uint64 spotPx = readSpotPx(spotMarketId); + + if (isActionExecutable(order.action, spotPx)) { + executeSpotLimitOrder(order.sender, order.action); + + // Remove executed order by swapping with last and popping + _pendingOrders[i - 1] = _pendingOrders[_pendingOrders.length - 1]; + _pendingOrders.pop(); + } + } + } + + ////////// PERP LIQUIDATIONS //////////////////// + function isLiquidatable(address user) public returns (bool) { + uint64 totalNotional = 0; + int64 totalUPnL = 0; + uint64 totalLocked = 0; + uint64 mmReq = 0; + + uint256 len = _userPerpPositions[user].length(); + + for (uint256 i = len; i > 0; i--) { + uint16 perpIndex = uint16(_userPerpPositions[user].at(i - 1)); + PrecompileLib.Position memory pos = _accounts[user].positions[perpIndex]; + if (pos.szi != 0) { + uint64 markPx = readMarkPx(perpIndex); + int64 szi = pos.szi; + uint64 avgEntry = pos.entryNtl / abs(szi); + int64 uPnL = szi * (int64(markPx) - int64(avgEntry)); + totalUPnL += uPnL; + totalLocked += _accounts[user].margin[perpIndex]; + + uint64 positionNotional = abs(szi) * markPx; + totalNotional += positionNotional; + + // Per-perp maintenance margin requirement based on max leverage + uint32 maxLev = _getMaxLeverage(perpIndex); + uint64 mmBps = 5000 / maxLev; // 5000 / maxLev gives bps for mm_fraction = 0.5 / maxLev + mmReq += (positionNotional * mmBps) / 10000; + } + } + + if (totalNotional == 0) { + return false; + } + + int64 equity = int64(_accounts[user].perpBalance) + int64(totalLocked) + totalUPnL; + + return equity < int64(mmReq); + } + + function abs(int64 value) internal pure returns (uint64) { + return value > 0 ? uint64(value) : uint64(-value); + } + + function _getMaxLeverage(uint16 perpIndex) public view returns (uint32) { + return _maxLeverage[perpIndex]; + } + + // simplified liquidation, nukes all positions and resets the perp balance + // for future: make this more realistic + function _liquidateUser(address user) public { + uint256 len = _userPerpPositions[user].length(); + for (uint256 i = len; i > 0; i--) { + uint16 perpIndex = uint16(_userPerpPositions[user].at(i - 1)); + + bytes32 key = _getKey(user, perpIndex); + _openPerpPositions.remove(key); + _accounts[user].positions[perpIndex].szi = 0; + _accounts[user].positions[perpIndex].entryNtl = 0; + _accounts[user].margin[perpIndex] = 0; + _userPerpPositions[user].remove(perpIndex); + } + + _accounts[user].perpBalance = 0; + } + + function liquidatePositions() public { + uint256 len = _openPerpPositions.length(); + + if (len == 0) return; + + for (uint256 i = len; i > 0; i--) { + bytes32 key = _openPerpPositions.at(i - 1); + address user = address(uint160(uint256(key) >> 16)); + if (isLiquidatable(user)) { + _liquidateUser(user); + } + } + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreState.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreState.sol new file mode 100644 index 000000000..5cdbcd0b6 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreState.sol @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { DoubleEndedQueue } from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol"; +import { Heap } from "@openzeppelin/contracts/utils/structs/Heap.sol"; + +import { PrecompileLib } from "../../../src/PrecompileLib.sol"; +import { HLConstants } from "../../../src/CoreWriterLib.sol"; + +import { RealL1Read } from "../../utils/RealL1Read.sol"; +import { StdCheats, Vm } from "forge-std/StdCheats.sol"; + +/// Modified from https://github.com/ambitlabsxyz/hypercore +contract CoreState is StdCheats { + using SafeCast for uint256; + using EnumerableSet for EnumerableSet.AddressSet; + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + using Heap for Heap.Uint256Heap; + + using RealL1Read for *; + + Vm internal constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + uint64 public immutable HYPE_TOKEN_INDEX; + uint64 public constant USDC_TOKEN_INDEX = 0; + + constructor() { + HYPE_TOKEN_INDEX = HLConstants.hypeTokenIndex(); + } + + struct WithdrawRequest { + address account; + uint64 amount; + uint32 lockedUntilTimestamp; + } + + struct AccountData { + bool activated; + mapping(uint64 token => uint64 balance) spot; + mapping(address vault => PrecompileLib.UserVaultEquity) vaultEquity; + uint64 staking; + mapping(address validator => PrecompileLib.Delegation) delegations; + uint64 perpBalance; + mapping(uint16 perpIndex => PrecompileLib.Position) positions; + mapping(uint16 perpIndex => uint64 margin) margin; + } + + struct PendingOrder { + address sender; + LimitOrderAction action; + } + + // registered token info + mapping(uint64 token => PrecompileLib.TokenInfo) internal _tokens; + + mapping(address account => AccountData) internal _accounts; + + mapping(address account => bool initialized) internal _initializedAccounts; + mapping(address account => mapping(uint64 token => bool initialized)) internal _initializedSpotBalance; + mapping(address account => mapping(address vault => bool initialized)) internal _initializedVaults; + + mapping(address account => mapping(uint32 perpIndex => bool initialized)) internal _initializedPerpPosition; + mapping(uint16 perpIndex => uint32 maxLeverage) internal _maxLeverage; + + mapping(address account => mapping(uint64 token => uint64 latentBalance)) internal _latentSpotBalance; + + mapping(uint32 perpIndex => uint64 markPrice) internal _perpMarkPrice; + mapping(uint32 spotMarketId => uint64 spotPrice) internal _spotPrice; + + mapping(address vault => uint64) internal _vaultEquity; + + DoubleEndedQueue.Bytes32Deque internal _withdrawQueue; + + PendingOrder[] internal _pendingOrders; + + EnumerableSet.AddressSet internal _validators; + + mapping(address vault => uint64) internal _vaultMultiplier; + + ///////////////////////// + /// STATE INITIALIZERS/// + ///////////////////////// + + modifier initAccountWithToken(address _account, uint64 token) { + if (!_initializedSpotBalance[_account][token]) { + registerTokenInfo(token); + _initializeAccountWithToken(_account, token); + } + _; + } + + modifier initAccountWithSpotMarket(address _account, uint32 spotMarketId) { + uint64 baseToken = PrecompileLib.spotInfo(spotMarketId).tokens[0]; + uint64 quoteToken = PrecompileLib.spotInfo(spotMarketId).tokens[1]; + + if (!_initializedSpotBalance[_account][baseToken]) { + registerTokenInfo(baseToken); + _initializeAccountWithToken(_account, baseToken); + } + + if (!_initializedSpotBalance[_account][quoteToken]) { + registerTokenInfo(quoteToken); + _initializeAccountWithToken(_account, quoteToken); + } + + _; + } + + modifier initAccountWithVault(address _account, address _vault) { + if (!_initializedVaults[_account][_vault]) { + _initializeAccount(_account); + _initializeAccountWithVault(_account, _vault); + } + _; + } + + modifier initAccountWithPerp(address _account, uint16 perp) { + if (_maxLeverage[perp] == 0) { + _maxLeverage[perp] = RealL1Read.position(address(1), perp).leverage; + } + + if (_initializedPerpPosition[_account][perp] == false) { + _initializeAccount(_account); + _initializeAccountWithPerp(_account, perp); + } + _; + } + + modifier initAccount(address _account) { + if (!_initializedAccounts[_account]) { + _initializeAccount(_account); + } + _; + } + + function _initializeAccountWithToken(address _account, uint64 token) internal { + _initializeAccount(_account); + + if (_accounts[_account].activated == false) { + return; + } + + _initializedSpotBalance[_account][token] = true; + _accounts[_account].spot[token] = RealL1Read.spotBalance(_account, token).total; + } + + function _initializeAccountWithVault(address _account, address _vault) internal { + _initializedVaults[_account][_vault] = true; + _accounts[_account].vaultEquity[_vault] = RealL1Read.userVaultEquity(_account, _vault); + } + + function _initializeAccountWithPerp(address _account, uint16 perp) internal { + _initializedPerpPosition[_account][perp] = true; + _accounts[_account].positions[perp] = RealL1Read.position(_account, perp); + } + + function _initializeAccount(address _account) internal { + bool initialized = _initializedAccounts[_account]; + + if (initialized) { + return; + } + + AccountData storage account = _accounts[_account]; + + // check if the acc is created on Core + RealL1Read.CoreUserExists memory coreUserExists = RealL1Read.coreUserExists(_account); + if (!coreUserExists.exists) { + return; + } + + _initializedAccounts[_account] = true; + account.activated = true; + + // setting perp balance + account.perpBalance = RealL1Read.withdrawable(_account).withdrawable; + + // setting staking balance + PrecompileLib.DelegatorSummary memory summary = RealL1Read.delegatorSummary(_account); + account.staking = summary.undelegated; + // note: no way to track the pending withdrawals, and have a way to credit them later + + // set delegations + PrecompileLib.Delegation[] memory delegations = RealL1Read.delegations(_account); + for (uint256 i = 0; i < delegations.length; i++) { + account.delegations[delegations[i].validator] = delegations[i]; + } + } + + modifier whenActivated(address sender) { + if (_accounts[sender].activated == false) { + return; + } + _; + } + + function registerTokenInfo(uint64 index) public { + // if the token is already registered, return early + if (bytes(_tokens[index].name).length > 0) { + return; + } + + PrecompileLib.TokenInfo memory tokenInfo = RealL1Read.tokenInfo(uint32(index)); + + // this means that the precompile call failed + if (tokenInfo.evmContract == RealL1Read.INVALID_ADDRESS) return; + _tokens[index] = tokenInfo; + } + + function forceTokenInfo( + uint64 index, + string memory name, + address evmContract, + uint8 szDecimals, + uint8 weiDecimals, + int8 evmExtraWeiDecimals + ) public { + PrecompileLib.TokenInfo memory tokenInfo = PrecompileLib.TokenInfo({ + name: name, + spots: new uint64[](0), + deployerTradingFeeShare: 0, + deployer: address(0), + evmContract: evmContract, + szDecimals: szDecimals, + weiDecimals: weiDecimals, + evmExtraWeiDecimals: evmExtraWeiDecimals + }); + _tokens[index] = tokenInfo; + } + + function registerValidator(address validator) public { + _validators.add(validator); + } + + /// @dev account creation can be forced when there isnt a reliance on testing that workflow. + function forceAccountActivation(address account) public { + _accounts[account].activated = true; + } + + function forceSpot(address account, uint64 token, uint64 _wei) public payable { + if (_accounts[account].activated == false) { + forceAccountActivation(account); + } + + if (_initializedSpotBalance[account][token] == false) { + registerTokenInfo(token); + _initializeAccountWithToken(account, token); + } + + _accounts[account].spot[token] = _wei; + } + + function forcePerpBalance(address account, uint64 usd) public payable { + if (_accounts[account].activated == false) { + forceAccountActivation(account); + } + if (_initializedAccounts[account] == false) { + _initializeAccount(account); + } + + _accounts[account].perpBalance = usd; + } + + function forceStaking(address account, uint64 _wei) public payable { + forceAccountActivation(account); + _accounts[account].staking = _wei; + } + + function forceDelegation(address account, address validator, uint64 amount, uint64 lockedUntilTimestamp) public { + forceAccountActivation(account); + _accounts[account].delegations[validator] = PrecompileLib.Delegation({ + validator: validator, + amount: amount, + lockedUntilTimestamp: lockedUntilTimestamp + }); + } + + function forceVaultEquity(address account, address vault, uint64 usd, uint64 lockedUntilTimestamp) public payable { + forceAccountActivation(account); + + _vaultEquity[vault] -= _accounts[account].vaultEquity[vault].equity; + _vaultEquity[vault] += usd; + + _accounts[account].vaultEquity[vault].equity = usd; + _accounts[account].vaultEquity[vault].lockedUntilTimestamp = lockedUntilTimestamp > 0 + ? lockedUntilTimestamp + : uint64((block.timestamp + 3600) * 1000); + } + + //////// conversions //////// + + function toWei(uint256 amount, int8 evmExtraWeiDecimals) internal pure returns (uint64) { + uint256 _wei = evmExtraWeiDecimals == 0 + ? amount + : evmExtraWeiDecimals > 0 + ? amount / 10 ** uint8(evmExtraWeiDecimals) + : amount * 10 ** uint8(-evmExtraWeiDecimals); + + return _wei.toUint64(); + } + + function fromWei(uint64 _wei, int8 evmExtraWeiDecimals) internal pure returns (uint256) { + return + evmExtraWeiDecimals == 0 + ? _wei + : evmExtraWeiDecimals > 0 + ? _wei * 10 ** uint8(evmExtraWeiDecimals) + : _wei / 10 ** uint8(-evmExtraWeiDecimals); + } + + function fromPerp(uint64 usd) internal pure returns (uint64) { + return usd * 1e2; + } + + // converting a withdraw request into a bytes32 + function serializeWithdrawRequest(CoreState.WithdrawRequest memory request) internal pure returns (bytes32) { + return + bytes32( + (uint256(uint160(request.account)) << 96) | + (uint256(request.amount) << 32) | + uint40(request.lockedUntilTimestamp) + ); + } + + function deserializeWithdrawRequest(bytes32 data) internal pure returns (CoreState.WithdrawRequest memory request) { + request.account = address(uint160(uint256(data) >> 96)); + request.amount = uint64(uint256(data) >> 32); + request.lockedUntilTimestamp = uint32(uint256(data)); + } + + struct LimitOrderAction { + uint32 asset; + bool isBuy; + uint64 limitPx; + uint64 sz; + bool reduceOnly; + uint8 encodedTif; + uint128 cloid; + } + + struct VaultTransferAction { + address vault; + bool isDeposit; + uint64 usd; + } + + struct TokenDelegateAction { + address validator; + uint64 _wei; + bool isUndelegate; + } + + struct StakingDepositAction { + uint64 _wei; + } + + struct StakingWithdrawAction { + uint64 _wei; + } + + struct SpotSendAction { + address destination; + uint64 token; + uint64 _wei; + } + + struct UsdClassTransferAction { + uint64 ntl; + bool toPerp; + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreView.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreView.sol new file mode 100644 index 000000000..571948642 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/simulation/hyper-core/CoreView.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { DoubleEndedQueue } from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol"; +import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; + +import { PrecompileLib } from "../../../src/PrecompileLib.sol"; +import { RealL1Read } from "../../utils/RealL1Read.sol"; + +import { CoreState } from "./CoreState.sol"; + +/// Modified from https://github.com/ambitlabsxyz/hypercore +contract CoreView is CoreState { + using EnumerableSet for EnumerableSet.AddressSet; + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + using SafeCast for uint256; + + function tokenExists(uint32 token) public view returns (bool) { + return bytes(_tokens[token].name).length > 0; + } + + function readMarkPx(uint32 perp) public returns (uint64) { + if (_perpMarkPrice[perp] == 0) { + return RealL1Read.markPx(perp); + } + + return _perpMarkPrice[perp]; + } + + function readSpotPx(uint32 spotMarketId) public view returns (uint64) { + if (_spotPrice[spotMarketId] == 0) { + return PrecompileLib.spotPx(spotMarketId); + } + + return _spotPrice[spotMarketId]; + } + + function readSpotBalance(address account, uint64 token) public returns (PrecompileLib.SpotBalance memory) { + if (_initializedSpotBalance[account][token] == false) { + return RealL1Read.spotBalance(account, token); + } + + return PrecompileLib.SpotBalance({ total: _accounts[account].spot[token], entryNtl: 0, hold: 0 }); + } + + // Even if the HyperCore account is not created, the precompile returns 0 (it does not revert) + function readWithdrawable(address account) public returns (PrecompileLib.Withdrawable memory) { + if (_accounts[account].activated == false) { + return RealL1Read.withdrawable(account); + } + + return PrecompileLib.Withdrawable({ withdrawable: _accounts[account].perpBalance }); + } + + function readUserVaultEquity( + address user, + address vault + ) public view returns (PrecompileLib.UserVaultEquity memory) { + PrecompileLib.UserVaultEquity memory equity = _accounts[user].vaultEquity[vault]; + uint64 multiplier = _vaultMultiplier[vault]; + if (multiplier != 0) equity.equity = uint64((uint256(equity.equity) * multiplier) / 1e18); + return equity; + } + + function readDelegation( + address user, + address validator + ) public view returns (PrecompileLib.Delegation memory delegation) { + delegation.validator = validator; + delegation.amount = _accounts[user].delegations[validator].amount; + delegation.lockedUntilTimestamp = _accounts[user].delegations[validator].lockedUntilTimestamp; + } + + function readDelegations(address user) public view returns (PrecompileLib.Delegation[] memory userDelegations) { + address[] memory validators = _validators.values(); + + userDelegations = new PrecompileLib.Delegation[](validators.length); + for (uint256 i; i < userDelegations.length; i++) { + userDelegations[i].validator = validators[i]; + + PrecompileLib.Delegation memory delegation = _accounts[user].delegations[validators[i]]; + userDelegations[i].amount = delegation.amount; + userDelegations[i].lockedUntilTimestamp = delegation.lockedUntilTimestamp; + } + } + + function readDelegatorSummary(address user) public view returns (PrecompileLib.DelegatorSummary memory summary) { + address[] memory validators = _validators.values(); + + for (uint256 i; i < validators.length; i++) { + PrecompileLib.Delegation memory delegation = _accounts[user].delegations[validators[i]]; + summary.delegated += delegation.amount; + } + + summary.undelegated = _accounts[user].staking; + + for (uint256 i; i < _withdrawQueue.length(); i++) { + WithdrawRequest memory request = deserializeWithdrawRequest(_withdrawQueue.at(i)); + if (request.account == user) { + summary.nPendingWithdrawals++; + summary.totalPendingWithdrawal += request.amount; + } + } + } + + function readPosition(address user, uint16 perp) public view returns (PrecompileLib.Position memory) { + return _accounts[user].positions[perp]; + } + + function coreUserExists(address account) public returns (bool) { + if (_accounts[account].activated == false) { + return RealL1Read.coreUserExists(account).exists; + } + + return _accounts[account].activated; + } + + function readAccountMarginSummary(address user) public view returns (PrecompileLib.AccountMarginSummary memory) { + // 1. maintain an enumerable set for the perps that a user is in + // 2. iterate over their positions and calculate position value, add them up (value = abs(sz * markPx)) + return PrecompileLib.accountMarginSummary(0, user); + } + + function readTokenInfo(uint64 index) public view returns (PrecompileLib.TokenInfo memory) { + return _tokens[index]; + } +} diff --git a/test/evm/foundry/local/external/hyper-evm-lib/test/utils/RealL1Read.sol b/test/evm/foundry/local/external/hyper-evm-lib/test/utils/RealL1Read.sol new file mode 100644 index 000000000..84f9b0387 --- /dev/null +++ b/test/evm/foundry/local/external/hyper-evm-lib/test/utils/RealL1Read.sol @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Vm } from "forge-std/Vm.sol"; +import { PrecompileLib } from "../../src/PrecompileLib.sol"; +import { console } from "forge-std/console.sol"; + +// Makes RPC calls to get real precompile data (independent of the test environment) +library RealL1Read { + struct Position { + int64 szi; + uint64 entryNtl; + int64 isolatedRawUsd; + uint32 leverage; + bool isIsolated; + } + + struct SpotBalance { + uint64 total; + uint64 hold; + uint64 entryNtl; + } + + struct UserVaultEquity { + uint64 equity; + uint64 lockedUntilTimestamp; + } + + struct Withdrawable { + uint64 withdrawable; + } + + struct Delegation { + address validator; + uint64 amount; + uint64 lockedUntilTimestamp; + } + + struct DelegatorSummary { + uint64 delegated; + uint64 undelegated; + uint64 totalPendingWithdrawal; + uint64 nPendingWithdrawals; + } + + struct PerpAssetInfo { + string coin; + uint32 marginTableId; + uint8 szDecimals; + uint8 maxLeverage; + bool onlyIsolated; + } + + struct SpotInfo { + string name; + uint64[2] tokens; + } + + struct TokenInfo { + string name; + uint64[] spots; + uint64 deployerTradingFeeShare; + address deployer; + address evmContract; + uint8 szDecimals; + uint8 weiDecimals; + int8 evmExtraWeiDecimals; + } + + struct UserBalance { + address user; + uint64 balance; + } + + struct TokenSupply { + uint64 maxSupply; + uint64 totalSupply; + uint64 circulatingSupply; + uint64 futureEmissions; + UserBalance[] nonCirculatingUserBalances; + } + + struct Bbo { + uint64 bid; + uint64 ask; + } + + struct AccountMarginSummary { + int64 accountValue; + uint64 marginUsed; + uint64 ntlPos; + int64 rawUsd; + } + + struct CoreUserExists { + bool exists; + } + + Vm constant vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); + + address constant POSITION_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000800; + address constant SPOT_BALANCE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; + address constant VAULT_EQUITY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000802; + address constant WITHDRAWABLE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000803; + address constant DELEGATIONS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000804; + address constant DELEGATOR_SUMMARY_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000805; + address constant MARK_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000806; + address constant ORACLE_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000807; + address constant SPOT_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000808; + address constant L1_BLOCK_NUMBER_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000809; + address constant PERP_ASSET_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080a; + address constant SPOT_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080b; + address constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C; + address constant TOKEN_SUPPLY_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080D; + address constant BBO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080e; + address constant ACCOUNT_MARGIN_SUMMARY_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080F; + address constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; + + address constant INVALID_ADDRESS = address(1); + + function _makeRpcCall(address target, bytes memory params) internal returns (bytes memory) { + // Construct the JSON-RPC payload + string memory jsonPayload = string.concat( + '[{"to":"', + vm.toString(target), + '","data":"', + vm.toString(params), + '"},"latest"]' + ); + + bool useArchivedBlockNumber = false; + + if (useArchivedBlockNumber) { + string memory blockNumberHex = string.concat("0x", toHexString(block.number)); + + jsonPayload = string.concat( + '[{"to":"', + vm.toString(target), + '","data":"', + vm.toString(params), + '"},"', + blockNumberHex, + '"]' + ); + } + + // Make the RPC call + try vm.rpc("eth_call", jsonPayload) returns (bytes memory data) { + return data; + } catch { + return ""; + } + } + + function toHexString(uint256 a) internal pure returns (string memory) { + uint256 count = 0; + uint256 b = a; + while (b != 0) { + count++; + b /= 16; + } + bytes memory res = new bytes(count); + for (uint256 i = 0; i < count; ++i) { + b = a % 16; + res[count - i - 1] = toHexDigit(uint8(b)); + a /= 16; + } + return string(res); + } + + function toHexDigit(uint8 d) internal pure returns (bytes1) { + if (0 <= d && d <= 9) { + return bytes1(uint8(bytes1("0")) + d); + } else if (10 <= uint8(d) && uint8(d) <= 15) { + return bytes1(uint8(bytes1("a")) + d - 10); + } + // revert("Invalid hex digit"); + revert(); + } + + function position(address user, uint16 perp) internal returns (PrecompileLib.Position memory) { + bytes memory result = _makeRpcCall(POSITION_PRECOMPILE_ADDRESS, abi.encode(user, perp)); + + if (result.length == 0) { + return PrecompileLib.Position({ szi: 0, entryNtl: 0, isolatedRawUsd: 0, leverage: 0, isIsolated: false }); + } + return abi.decode(result, (PrecompileLib.Position)); + } + + function spotBalance(address user, uint64 token) internal returns (PrecompileLib.SpotBalance memory) { + bytes memory result = _makeRpcCall(SPOT_BALANCE_PRECOMPILE_ADDRESS, abi.encode(user, token)); + if (result.length == 0) { + return PrecompileLib.SpotBalance({ total: 0, hold: 0, entryNtl: 0 }); + } + return abi.decode(result, (PrecompileLib.SpotBalance)); + } + + function userVaultEquity(address user, address vault) internal returns (PrecompileLib.UserVaultEquity memory) { + bytes memory result = _makeRpcCall(VAULT_EQUITY_PRECOMPILE_ADDRESS, abi.encode(user, vault)); + if (result.length == 0) { + return PrecompileLib.UserVaultEquity({ equity: 0, lockedUntilTimestamp: 0 }); + } + return abi.decode(result, (PrecompileLib.UserVaultEquity)); + } + + function withdrawable(address user) internal returns (PrecompileLib.Withdrawable memory) { + bytes memory result = _makeRpcCall(WITHDRAWABLE_PRECOMPILE_ADDRESS, abi.encode(user)); + if (result.length == 0) { + return PrecompileLib.Withdrawable({ withdrawable: 45 }); + } + return abi.decode(result, (PrecompileLib.Withdrawable)); + } + + function delegations(address user) internal returns (PrecompileLib.Delegation[] memory) { + bytes memory result = _makeRpcCall(DELEGATIONS_PRECOMPILE_ADDRESS, abi.encode(user)); + if (result.length == 0) { + return new PrecompileLib.Delegation[](0); + } + return abi.decode(result, (PrecompileLib.Delegation[])); + } + + function delegatorSummary(address user) internal returns (PrecompileLib.DelegatorSummary memory) { + bytes memory result = _makeRpcCall(DELEGATOR_SUMMARY_PRECOMPILE_ADDRESS, abi.encode(user)); + return abi.decode(result, (PrecompileLib.DelegatorSummary)); + } + + function markPx(uint32 index) internal returns (uint64) { + bytes memory result = _makeRpcCall(MARK_PX_PRECOMPILE_ADDRESS, abi.encode(index)); + return abi.decode(result, (uint64)); + } + + function oraclePx(uint32 index) internal returns (uint64) { + bytes memory result = _makeRpcCall(ORACLE_PX_PRECOMPILE_ADDRESS, abi.encode(index)); + return abi.decode(result, (uint64)); + } + + function spotPx(uint32 index) internal returns (uint64) { + bytes memory result = _makeRpcCall(SPOT_PX_PRECOMPILE_ADDRESS, abi.encode(index)); + return abi.decode(result, (uint64)); + } + + function l1BlockNumber() internal returns (uint64) { + bytes memory result = _makeRpcCall(L1_BLOCK_NUMBER_PRECOMPILE_ADDRESS, abi.encode()); + return abi.decode(result, (uint64)); + } + + function perpAssetInfo(uint32 perp) internal returns (PerpAssetInfo memory) { + bytes memory result = _makeRpcCall(PERP_ASSET_INFO_PRECOMPILE_ADDRESS, abi.encode(perp)); + return abi.decode(result, (PerpAssetInfo)); + } + + function spotInfo(uint32 spot) internal returns (PrecompileLib.SpotInfo memory) { + bytes memory result = _makeRpcCall(SPOT_INFO_PRECOMPILE_ADDRESS, abi.encode(spot)); + return abi.decode(result, (PrecompileLib.SpotInfo)); + } + + function tokenInfo(uint32 token) internal returns (PrecompileLib.TokenInfo memory) { + bytes memory result = _makeRpcCall(TOKEN_INFO_PRECOMPILE_ADDRESS, abi.encode(token)); + if (result.length == 0) { + return + PrecompileLib.TokenInfo({ + name: "", + spots: new uint64[](0), + deployerTradingFeeShare: 0, + deployer: INVALID_ADDRESS, + evmContract: INVALID_ADDRESS, + szDecimals: 0, + weiDecimals: 0, + evmExtraWeiDecimals: 0 + }); + } + return abi.decode(result, (PrecompileLib.TokenInfo)); + } + + function tokenSupply(uint32 token) internal returns (TokenSupply memory) { + bytes memory result = _makeRpcCall(TOKEN_SUPPLY_PRECOMPILE_ADDRESS, abi.encode(token)); + return abi.decode(result, (TokenSupply)); + } + + function bbo(uint32 asset) internal returns (Bbo memory) { + bytes memory result = _makeRpcCall(BBO_PRECOMPILE_ADDRESS, abi.encode(asset)); + return abi.decode(result, (Bbo)); + } + + function accountMarginSummary(uint32 perp_dex_index, address user) internal returns (AccountMarginSummary memory) { + bytes memory result = _makeRpcCall(ACCOUNT_MARGIN_SUMMARY_PRECOMPILE_ADDRESS, abi.encode(perp_dex_index, user)); + return abi.decode(result, (AccountMarginSummary)); + } + + function coreUserExists(address user) internal returns (CoreUserExists memory) { + bytes memory result = _makeRpcCall(CORE_USER_EXISTS_PRECOMPILE_ADDRESS, abi.encode(user)); + return abi.decode(result, (CoreUserExists)); + } +} From a2aad398884c8d2d4db1e50206538a5d18037f7f Mon Sep 17 00:00:00 2001 From: Ihor Farion <65650773+grasphoper@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:47:34 -0800 Subject: [PATCH 33/47] improve: update verify bytecode script to work with `foundry` deployments (#82) Signed-off-by: Ihor Farion --- tasks/verifyBytecode.ts | 369 ++++++++++++++++++++++++++++++++-------- 1 file changed, 297 insertions(+), 72 deletions(-) diff --git a/tasks/verifyBytecode.ts b/tasks/verifyBytecode.ts index 9e6ca230d..71bea5ead 100644 --- a/tasks/verifyBytecode.ts +++ b/tasks/verifyBytecode.ts @@ -2,99 +2,324 @@ import { task } from "hardhat/config"; import type { HardhatRuntimeEnvironment } from "hardhat/types"; import "hardhat-deploy"; import "@nomiclabs/hardhat-ethers"; +import fs from "fs"; +import path from "path"; +import { execSync } from "child_process"; + +type VerifyBytecodeArgs = { + contract?: string; + txHash?: string; + libraries?: string; + broadcast?: string; +}; + +/** + * Best-effort parser for `foundry.toml` that extracts the default profile's `out` directory. + * Falls back to `/out` if anything goes wrong. + */ +function getFoundryOutDir(): string { + const root = process.cwd(); + const configPath = path.join(root, "foundry.toml"); + + if (!fs.existsSync(configPath)) { + return path.join(root, "out"); + } + + const contents = fs.readFileSync(configPath, "utf8"); + const lines = contents.split(/\r?\n/); + + let inDefaultProfile = false; + for (const rawLine of lines) { + const line = rawLine.trim(); + + if (line.startsWith("[") && line.endsWith("]")) { + // Enter or exit `[profile.default]` section. + inDefaultProfile = line === "[profile.default]"; + continue; + } + + if (!inDefaultProfile) continue; + + const match = line.match(/^out\s*=\s*"(.*)"\s*$/); + if (match) { + const configuredOut = match[1].trim(); + if (configuredOut.length > 0) { + return path.isAbsolute(configuredOut) ? configuredOut : path.join(root, configuredOut); + } + } + } + + // Default Foundry output directory. + return path.join(root, "out"); +} + +function normalizeFoundryBytecode(raw: any, key: "bytecode" | "deployedBytecode"): [string, any] { + const value = raw[key]; + if (!value) { + return ["0x", {}]; + } + + if (typeof value === "string") { + const linksKey = key === "bytecode" ? "linkReferences" : "deployedLinkReferences"; + const links = raw[linksKey] ?? {}; + return [value, links]; + } + + if (typeof value === "object") { + return [value.object ?? "0x", value.linkReferences ?? {}]; + } + + return ["0x", {}]; +} /** - * Verify that the deployment init code (creation bytecode + encoded constructor args) - * matches the locally reconstructed init code from artifacts and recorded args. - * - * Compares keccak256(initCodeOnChain) vs keccak256(initCodeLocal). - * - * Sample usage: - * yarn hardhat verify-bytecode --contract Arbitrum_Adapter --network mainnet - * yarn hardhat verify-bytecode --contract Arbitrum_Adapter --tx-hash 0x... --network mainnet - * yarn hardhat verify-bytecode --contract X --tx-hash 0x... --libraries "MyLib=0x...,OtherLib=0x..." --network mainnet + * Load a Foundry artifact (`out/...json`) and adapt it into a Hardhat-style artifact + * that can be consumed by `ethers.getContractFactoryFromArtifact`. + */ +function loadFoundryArtifact(contractName: string): any { + const outDir = getFoundryOutDir(); + const candidates = [ + path.join(outDir, `${contractName}.sol`, `${contractName}.json`), + path.join(outDir, `${contractName}.json`), + ]; + + const artifactPath = candidates.find((p) => fs.existsSync(p)); + if (!artifactPath) { + throw new Error( + `Could not find Foundry artifact for contract "${contractName}". Tried:\n` + + candidates.map((p) => ` - ${p}`).join("\n") + ); + } + + const rawJson = fs.readFileSync(artifactPath, "utf8"); + const raw: any = JSON.parse(rawJson); + + const abi = raw.abi ?? []; + const [bytecode, linkReferences] = normalizeFoundryBytecode(raw, "bytecode"); + const [deployedBytecode, deployedLinkReferences] = normalizeFoundryBytecode(raw, "deployedBytecode"); + + return { + _format: "hh-foundry-compat-0", + contractName, + sourceName: raw.sourceName ?? raw.source_name ?? path.basename(artifactPath), + abi, + bytecode, + deployedBytecode, + linkReferences, + deployedLinkReferences, + }; +} + +function ensureForgeBuildArtifacts() { + try { + // This keeps Foundry's `out/` artifacts up to date when verifying Foundry deployments. + console.log("Running `forge build` to refresh Foundry artifacts..."); + execSync("forge build", { stdio: "inherit" }); + } catch (error: any) { + throw new Error(`forge build failed: ${error?.message ?? String(error)}`); + } +} + +/** +Verify that the deployment init code (creation bytecode + encoded constructor args) +matches the locally reconstructed init code from artifacts and recorded args. + +Compares keccak256(initCodeOnChain) vs keccak256(initCodeLocal). + +Sample usage: +yarn hardhat verify-bytecode --contract Arbitrum_Adapter --network mainnet +yarn hardhat verify-bytecode --contract Arbitrum_Adapter --tx-hash 0x... --network mainnet +yarn hardhat verify-bytecode --contract X --tx-hash 0x... --libraries "MyLib=0x...,OtherLib=0x..." --network mainnet + +For Foundry deployments that used `forge script --broadcast`, you can instead +point this task at the Foundry broadcast JSON: + +yarn hardhat verify-bytecode \ + --contract DstOFTHandler \ + --broadcast broadcast/DeployDstHandler.s.sol/999/run-latest.json \ + --network hyperevm */ task("verify-bytecode", "Verify deploy transaction input against local artifacts") .addOptionalParam("contract", "Contract name; falls back to env CONTRACT") // @dev For proxies, we don't save transactionHash in deployments/. You have to provide it manually via --tx-hash 0x... by checking e.g. block explorer first .addOptionalParam("txHash", "Deployment transaction hash (defaults to deployments JSON)") .addOptionalParam("libraries", "Libraries to link. JSON string or 'Name=0x..,Other=0x..'") - .setAction( - async (args: { contract?: string; txHash?: string; libraries?: string }, hre: HardhatRuntimeEnvironment) => { - const { deployments, ethers, artifacts, network } = hre; + .addOptionalParam( + "broadcast", + "Path to Foundry broadcast JSON (e.g. broadcast/DeployFoo.s.sol/1/run-latest.json). " + + "If set, constructor args and default txHash are taken from this file instead of hardhat-deploy deployments." + ) + .setAction(async (args: VerifyBytecodeArgs, hre: HardhatRuntimeEnvironment) => { + const { deployments, ethers, artifacts, network } = hre; - // make sure we're using latest local contract artifacts for verification + const useFoundryArtifacts = Boolean(args.broadcast); + + // For Hardhat deployments, make sure we're using latest local Hardhat artifacts. + if (!useFoundryArtifacts) { await hre.run("compile"); + } else { + // For Foundry deployments, refresh Foundry's `out/` artifacts instead. + ensureForgeBuildArtifacts(); + } - const contractName = args.contract || process.env.CONTRACT; - if (!contractName) throw new Error("Please provide --contract or set CONTRACT env var"); + const contractName = args.contract || process.env.CONTRACT; + if (!contractName) throw new Error("Please provide --contract or set CONTRACT env var"); - const deployment = await deployments.get(contractName); - const deployedAddress: string = deployment.address; - const constructorArgs: any[] = deployment.args || []; - - const parseLibraries = (s?: string): Record => { - if (!s) return {}; - const out: Record = {}; - const trimmed = s.trim(); - if (trimmed.startsWith("{") && trimmed.endsWith("}")) { - const parsed = JSON.parse(trimmed); - for (const [k, v] of Object.entries(parsed)) out[k] = String(v); - return out; - } - for (const part of trimmed.split(/[\,\n]/)) { - const [k, v] = part.split("=").map((x) => x.trim()); - if (k && v) out[k] = v; - } - return out; + /** + * Resolve constructor args, deployed address and default tx hash either from: + * - hardhat-deploy deployments (default), or + * - Foundry broadcast JSON (when --broadcast is provided). + */ + let deployedAddress: string | undefined; + let constructorArgs: any[] = []; + let defaultTxHash: string | undefined; + + if (args.broadcast) { + const resolvedPath = path.isAbsolute(args.broadcast) ? args.broadcast : path.join(process.cwd(), args.broadcast); + + if (!fs.existsSync(resolvedPath)) { + throw new Error(`Broadcast file not found at path ${resolvedPath}`); + } + + // Narrow JSON structure to only what we need. + type BroadcastTx = { + hash?: string; + transactionType?: string; + contractName?: string; + contractAddress?: string; + arguments?: any[]; + transaction?: { + input?: string; + }; }; + type BroadcastJson = { + transactions?: BroadcastTx[]; + }; + + const raw = fs.readFileSync(resolvedPath, "utf8"); + const parsed: BroadcastJson = JSON.parse(raw); + const txs = parsed.transactions || []; - // Read local compilation artifact - const artifact = await artifacts.readArtifact(contractName); - console.log("Reading compilation artifact for", artifact.sourceName); - - /** - * TODO - * the `libraries` bit is untested. Could be wrong. Could remove this part if we don't have contracts with dynamic libraries - * artifact.linkReferences might help solve this better. Also, deployments.libraries. Implement only if required later. - */ - const libraries: Record = parseLibraries(args.libraries); - const factory = await ethers.getContractFactoryFromArtifact( - artifact, - Object.keys(libraries).length ? { libraries } : {} + const createTxsForContract = txs.filter( + (tx) => tx.transactionType === "CREATE" && tx.contractName === contractName ); - // Note: `factory.getDeployTransaction` populates the transaction with whatever data we WOULD put in it if we were deploying it right now - const populatedDeployTransaction = factory.getDeployTransaction(...constructorArgs); - const expectedInit: string = ethers.utils.hexlify(populatedDeployTransaction.data!).toLowerCase(); - if (!expectedInit || expectedInit === "0x") { - throw new Error("Failed to reconstruct deployment init code from local artifacts"); + if (!createTxsForContract.length) { + throw new Error(`No CREATE transaction for contract "${contractName}" found in broadcast file ${resolvedPath}`); + } + + let selected: BroadcastTx; + if (args.txHash) { + const match = createTxsForContract.find( + (tx) => tx.hash && tx.hash.toLowerCase() === args.txHash!.toLowerCase() + ); + if (!match) { + throw new Error( + `No CREATE transaction with hash ${args.txHash} for contract "${contractName}" in ${resolvedPath}` + ); + } + selected = match; + } else if (createTxsForContract.length === 1) { + selected = createTxsForContract[0]; + } else { + const hashes = createTxsForContract + .map((tx) => tx.hash) + .filter(Boolean) + .join(", "); + throw new Error( + `Multiple CREATE transactions for contract "${contractName}" found in ${resolvedPath}. ` + + `Please re-run with --tx-hash set to one of: ${hashes}` + ); + } + + if (!selected.hash) { + throw new Error(`Selected broadcast transaction for "${contractName}" is missing a tx hash`); } - // Get on-chain creation input - const txHash = args.txHash ?? deployment.transactionHash; - if (!txHash) { - throw new Error("Could not find deployment tx hash. Pass --tx-hash when running script."); + deployedAddress = selected.contractAddress; + constructorArgs = selected.arguments || []; + defaultTxHash = selected.hash; + } else { + const deployment = await deployments.get(contractName); + deployedAddress = deployment.address; + constructorArgs = deployment.args || []; + defaultTxHash = deployment.transactionHash; + } + + const parseLibraries = (s?: string): Record => { + if (!s) return {}; + const out: Record = {}; + const trimmed = s.trim(); + if (trimmed.startsWith("{") && trimmed.endsWith("}")) { + const parsed = JSON.parse(trimmed); + for (const [k, v] of Object.entries(parsed)) out[k] = String(v); + return out; } - const tx = await ethers.provider.getTransaction(txHash); - if (!tx) throw new Error(`Transaction not found for hash ${txHash}`); - if (tx.to && tx.to != "") { - throw new Error(`Transaction ${txHash} is not a direct contract creation (tx.to=${tx.to})`); + for (const part of trimmed.split(/[\,\n]/)) { + const [k, v] = part.split("=").map((x) => x.trim()); + if (k && v) out[k] = v; } + return out; + }; - const expectedHash = ethers.utils.keccak256(expectedInit); - const onchainHash = ethers.utils.keccak256(tx.data.toLowerCase()); + // Read local compilation artifact (Hardhat or Foundry) for reconstructing init code. + const artifact = useFoundryArtifacts + ? loadFoundryArtifact(contractName) + : await artifacts.readArtifact(contractName); + console.log( + "Reading compilation artifact for", + (artifact as any).sourceName ?? (useFoundryArtifacts ? "" : "") + ); - console.log("\n=============== Deploy Tx Verification ==============="); - console.log(`Contract : ${contractName}`); - console.log(`Network : ${network.name}`); + /** + * TODO + * the `libraries` bit is untested. Could be wrong. Could remove this part if we don't have contracts with dynamic libraries + * artifact.linkReferences might help solve this better. Also, deployments.libraries. Implement only if required later. + */ + const libraries: Record = parseLibraries(args.libraries); + const factory = await ethers.getContractFactoryFromArtifact( + artifact, + Object.keys(libraries).length ? { libraries } : {} + ); + + // Note: `factory.getDeployTransaction` populates the transaction with whatever data we WOULD put in it if we were deploying it right now + const populatedDeployTransaction = factory.getDeployTransaction(...constructorArgs); + const expectedInit: string = ethers.utils.hexlify(populatedDeployTransaction.data!).toLowerCase(); + if (!expectedInit || expectedInit === "0x") { + throw new Error("Failed to reconstruct deployment init code from local artifacts"); + } + + // Get on-chain creation input + const txHash = args.txHash ?? defaultTxHash; + if (!txHash) { + throw new Error( + "Could not find deployment tx hash. Pass --tx-hash when running script, " + + "or ensure deployments / broadcast metadata includes it." + ); + } + const tx = await ethers.provider.getTransaction(txHash); + if (!tx) throw new Error(`Transaction not found for hash ${txHash}`); + if (tx.to && tx.to != "") { + throw new Error(`Transaction ${txHash} is not a direct contract creation (tx.to=${tx.to})`); + } + + const expectedHash = ethers.utils.keccak256(expectedInit); + const onchainHash = ethers.utils.keccak256(tx.data.toLowerCase()); + + console.log("\n=============== Deploy Tx Verification ==============="); + console.log(`Contract : ${contractName}`); + console.log(`Network : ${network.name}`); + if (deployedAddress) { console.log(`Deployed address : ${deployedAddress}`); - if (txHash) console.log(`Tx hash : ${txHash}`); - console.log("-------------------------------------------------------"); - console.log(`On-chain init hash : ${onchainHash}`); - console.log(`Local init hash : ${expectedHash}`); - console.log("-------------------------------------------------------"); - console.log(onchainHash === expectedHash ? "✅ MATCH" : "❌ MISMATCH – init code differs"); - console.log("=======================================================\n"); } - ); + if (args.broadcast) { + console.log(`Broadcast file : ${args.broadcast}`); + } + if (txHash) console.log(`Tx hash : ${txHash}`); + console.log("-------------------------------------------------------"); + console.log(`On-chain init hash : ${onchainHash}`); + console.log(`Local init hash : ${expectedHash}`); + console.log("-------------------------------------------------------"); + console.log(onchainHash === expectedHash ? "✅ MATCH" : "❌ MISMATCH – init code differs"); + console.log("=======================================================\n"); + }); From 90471933497b6242ffc2946088913c3a414e15f1 Mon Sep 17 00:00:00 2001 From: Ihor Farion Date: Fri, 5 Dec 2025 15:06:33 -0800 Subject: [PATCH 34/47] fix duplicate foundry flag Signed-off-by: Ihor Farion --- foundry.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index b34affefd..cd0c2b3f7 100644 --- a/foundry.toml +++ b/foundry.toml @@ -32,7 +32,6 @@ arbitrum = "${NODE_URL_42161}" base = "${NODE_URL_8453}" bsc = "${NODE_URL_56}" ethereum = "${NODE_URL_1}" -hyperevm = "${NODE_URL_999}" ink = "${NODE_URL_57073}" lens = "${NODE_URL_232}" linea = "${NODE_URL_59144}" From 4733c4ce63fb903c9b628ec7405a367e7ba3c6a4 Mon Sep 17 00:00:00 2001 From: Ihor Farion Date: Fri, 5 Dec 2025 16:31:44 -0800 Subject: [PATCH 35/47] conditional compilation when testing w/ hardhat Signed-off-by: Ihor Farion --- hardhat.config.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/hardhat.config.ts b/hardhat.config.ts index e534bbff3..3bc4a907c 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,3 +1,25 @@ +const { subtask } = require("hardhat/config"); +const { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } = require("hardhat/builtin-tasks/task-names"); + +subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction(async (_: any, __: any, runSuper: any) => { + const paths = await runSuper(); + + // Filter out files that cause problems when using "paris" hardfork (currently used to compile everything when IS_TEST=true) + // Reference: https://github.com/NomicFoundation/hardhat/issues/2306#issuecomment-1039452928 + if (process.env.IS_TEST === "true" || process.env.CI === "true") { + return paths.filter((p: any) => { + return ( + !p.includes("contracts/periphery/mintburn") && + !p.includes("contracts/external/libraries/BytesLib.sol") && + !p.includes("contracts/libraries/SponsoredCCTPQuoteLib.sol") && + !p.includes("contracts/external/libraries/MinimalLZOptions.sol") + ); + }); + } + + return paths; +}); + import * as dotenv from "dotenv"; dotenv.config(); import { HardhatUserConfig } from "hardhat/config"; From e220bc06e889be43a6f8d4191c4531b4e28e3040 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Mon, 8 Dec 2025 11:41:19 -0500 Subject: [PATCH 36/47] fix: Support USDC HyperCore transfer Signed-off-by: Faisal Usmani --- contracts/libraries/HyperCoreLib.sol | 28 +++++++++++++++---- .../mintburn/HyperCoreFlowExecutor.sol | 13 +++------ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/contracts/libraries/HyperCoreLib.sol b/contracts/libraries/HyperCoreLib.sol index 65d97f156..fe3157800 100644 --- a/contracts/libraries/HyperCoreLib.sol +++ b/contracts/libraries/HyperCoreLib.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; import { IERC20 } from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; +import { ICoreDepositWallet } from "../external/interfaces/ICoreDepositWallet.sol"; interface ICoreWriter { function sendRawAction(bytes calldata data) external; @@ -50,12 +51,16 @@ library HyperCoreLib { address public constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; address public constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C; address public constant CORE_WRITER_PRECOMPILE_ADDRESS = 0x3333333333333333333333333333333333333333; + address public constant CORE_DEPOSIT_WALLET_ADDRESS = 0x6B9E773128f453f5c2C60935Ee2DE2CBc5390A24; // CoreWriter action headers bytes4 public constant LIMIT_ORDER_HEADER = 0x01000001; // version=1, action=1 bytes4 public constant SPOT_SEND_HEADER = 0x01000006; // version=1, action=6 bytes4 public constant CANCEL_BY_CLOID_HEADER = 0x0100000B; // version=1, action=11 + // HyperCore protocol constants + uint32 private constant CORE_SPOT_DEX_ID = type(uint32).max; + // Errors error LimitPxIsZero(); error OrderSizeIsZero(); @@ -87,9 +92,7 @@ library HyperCoreLib { (uint256 _amountEVMToSend, uint64 _amountCoreToReceive) = maximumEVMSendAmountToAmounts(amountEVM, decimalDiff); if (_amountEVMToSend != 0) { - // Transfer the tokens to this contract's address on HyperCore - IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), _amountEVMToSend); - + transferToCore(erc20EVMAddress, erc20CoreIndex, _amountEVMToSend); // Transfer the tokens from this contract on HyperCore to the `to` address on HyperCore transferERC20CoreToCore(erc20CoreIndex, to, _amountCoreToReceive); } @@ -117,8 +120,7 @@ library HyperCoreLib { (uint256 _amountEVMToSend, uint64 _amountCoreToReceive) = maximumEVMSendAmountToAmounts(amountEVM, decimalDiff); if (_amountEVMToSend != 0) { - // Transfer the tokens to this contract's address on HyperCore - IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), _amountEVMToSend); + transferToCore(erc20EVMAddress, erc20CoreIndex, _amountEVMToSend); } return (_amountEVMToSend, _amountCoreToReceive); @@ -137,6 +139,22 @@ library HyperCoreLib { ICoreWriter(CORE_WRITER_PRECOMPILE_ADDRESS).sendRawAction(payload); } + /** + * @notice Transfers tokens from this contract on HyperEVM to this contract's address on HyperCore + * @param erc20EVMAddress The address of the ERC20 token on HyperEVM + * @param erc20CoreIndex The HyperCore index id of the token to transfer + * @param amountEVMToSend The amount to transfer on HyperEVM + */ + function transferToCore(address erc20EVMAddress, uint64 erc20CoreIndex, uint256 amountEVMToSend) internal { + // index 0 is USDC, which requires a special transfer to core + if (erc20CoreIndex == 0) { + ICoreDepositWallet(CORE_DEPOSIT_WALLET_ADDRESS).deposit(amountEVMToSend, CORE_SPOT_DEX_ID); + } else { + // Transfer the tokens to this contract's address on HyperCore + IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), amountEVMToSend); + } + } + /** * @notice Submit a limit order on HyperCore. * @dev Expects price & size already scaled by 1e8 per HyperCore spec. diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 7372f84dd..d05e352b0 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -273,9 +273,6 @@ contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow /// @notice Thrown when an attemp to finalize an already finalized swap is made error SwapAlreadyFinalized(); - /// @notice Thrown when trying to finalize a quoteNonce, calling a finalizeSwapFlows with an incorrect token - error WrongSwapFinalizationToken(bytes32 quoteNonce); - /// @notice Emitted when we're inside the sponsored flow and a user doesn't have a HyperCore account activated. The /// bot should activate user's account first by calling `activateUserAccount` error AccountNotActivatedError(address user); @@ -521,9 +518,9 @@ contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow } if (amountToSponsor > 0) { - if (!_availableInDonationBox(params.quoteNonce, coreTokenInfo.tokenInfo.evmContract, amountToSponsor)) { + if (!_availableInDonationBox(params.quoteNonce, finalToken, amountToSponsor)) { // If the full amount is not available in the donation box, use the balance of the token in the donation box - amountToSponsor = IERC20(coreTokenInfo.tokenInfo.evmContract).balanceOf(address(donationBox)); + amountToSponsor = IERC20(finalToken).balanceOf(address(donationBox)); } } } @@ -556,7 +553,7 @@ contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow if (amountToSponsor > 0) { // This will succeed because we checked the balance earlier - donationBox.withdraw(IERC20(coreTokenInfo.tokenInfo.evmContract), amountToSponsor); + donationBox.withdraw(IERC20(finalToken), amountToSponsor); } $.cumulativeSponsoredAmount[finalToken] += amountToSponsor; @@ -780,7 +777,7 @@ contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow ) { // We expect this situation to be so rare and / or intermittend that we're willing to rely on admin to sweep the funds if this leads to // swaps being impossible to finalize - revert UnsafeToBridgeError(finalCoreTokenInfo.tokenInfo.evmContract, totalAdditionalToSend); + revert UnsafeToBridgeError(finalToken, totalAdditionalToSend); } $.cumulativeSponsoredAmount[finalToken] += totalAdditionalToSendEVM; @@ -809,7 +806,6 @@ contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow SwapFlowState storage swap = _getMainStorage().swaps[quoteNonce]; if (swap.finalRecipient == address(0)) revert SwapDoesNotExist(); if (swap.finalized) revert SwapAlreadyFinalized(); - if (swap.finalToken != finalCoreTokenInfo.tokenInfo.evmContract) revert WrongSwapFinalizationToken(quoteNonce); uint64 totalToSend; (totalToSend, additionalToSend) = _calcSwapFlowSendAmounts( @@ -946,7 +942,6 @@ contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow uint64 bridgeSafetyBufferCore ) internal { HyperCoreLib.TokenInfo memory tokenInfo = HyperCoreLib.tokenInfo(coreIndex); - require(tokenInfo.evmContract == token, "Token mismatch"); (uint256 accountActivationFeeEVM, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( accountActivationFeeCore, From 765f440d3a79a9ea1c4679fcd260c623f1165815 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Mon, 8 Dec 2025 11:42:14 -0500 Subject: [PATCH 37/47] Added ICoreDepositWallet Signed-off-by: Faisal Usmani --- .../interfaces/ICoreDepositWallet.sol | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 contracts/external/interfaces/ICoreDepositWallet.sol diff --git a/contracts/external/interfaces/ICoreDepositWallet.sol b/contracts/external/interfaces/ICoreDepositWallet.sol new file mode 100644 index 000000000..2bcf1068f --- /dev/null +++ b/contracts/external/interfaces/ICoreDepositWallet.sol @@ -0,0 +1,125 @@ +/* + * Copyright 2025 Circle Internet Group, Inc. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +pragma solidity ^0.8.0; + +/** + * @title IForwardDepositReceiver + * @notice Interface for a contract that can receive deposits from the CCTP Forwarder + */ +interface IForwardDepositReceiver { + /** + * @notice Deposit tokens for a recipient + * @param recipient Recipient of the deposit + * @param amount Amount of tokens to deposit + * @param destinationId Forwarding-address-specific id used in conjunction with + * recipient to route the deposit to a specific location. + */ + function depositFor(address recipient, uint256 amount, uint32 destinationId) external; +} + +/** + * @title ICoreDepositWallet + * @notice Interface for the core deposit wallet + */ +interface ICoreDepositWallet is IForwardDepositReceiver { + /** + * @notice Deposits tokens for the sender. + * @param amount The amount of tokens being deposited. + * @param destinationDex The destination dex on HyperCore. + */ + function deposit(uint256 amount, uint32 destinationDex) external; + + /** + * @notice Handles the token transfer from the ICoreDepositWallet to the recipient. + * @param to The address receiving the tokens. + * @param amount The amount of tokens being transferred. + * @return success True if the transfer succeeded. + */ + function transfer(address to, uint256 amount) external returns (bool success); + + /** + * @notice Handles cross-chain token withdrawals from HyperCore to a destination chain via CCTP. + * @dev This function initiates a cross-chain transfer of tokens using CCTP to mint tokens on the destination chain. + * It constructs and sends a CCTP message containing encoded hook data that embeds the optional user-provided + * data to be used on the destination chain and determines the CCTP forwarding behavior. + * + * @dev Requirements: + * - Caller must be the token system address. + * - `amount` must be strictly greater than the computed maximum withdrawal fee + * (see calculateCrossChainWithdrawalFee). + * + * @dev CCTP behavior: + * - The CCTP message's destinationCaller is always set to `bytes32(0)` and anyone can call + * MessageTransmitterV2.receiveMessage directly on the destination chain. + * + * @dev Forwarding is the process of completing the mint on the destination chain by paying gas to + * submit a transaction that includes the CCTP attestation. When forwarding is requested: + * - The forwarder subtracts a configured amount of minted tokens from the final recipient, + * not exceeding the CCTP maxFee. + * - This forwarding amount is added to the CCTP maxFee to compute the total fee for the + * cross-chain withdrawal. + * + * @dev Forwarding logic: + * The forwarding logic is determined by the user-provided data: + * - If `data` is empty: the hook data will be a default forwarding hook and forwarding will be performed. + * - If `data` begins with the CCTP forwarding magic bytes: the hook data will embed `data` and forwarding will be performed. + * - Otherwise: the hook data will embed `data` and forwarding will NOT be performed. + * + * @dev Hook data encoding: + * The hook data is constructed using the `CrossChainWithdrawalHookData` library. See that library for the full + * encoding details. + * + * @param from The HyperCore address debited by the cross-chain withdrawal. + * @param destinationRecipient The address receiving the minted tokens on the destination chain, as bytes32. + * @param destinationChainId The CCTP domain ID of the destination chain. + * @param amount The amount of tokens being transferred. + * @param coreNonce The HyperCore transaction nonce. + * @param data Optional user-provided data to embed in the CCTP message payload hook data; also determines the + * forwarding logic as described above. Must be less than or equal to MAX_HOOK_DATA_SIZE. + */ + function coreReceiveWithData( + address from, + bytes32 destinationRecipient, + uint32 destinationChainId, + uint256 amount, + uint64 coreNonce, + bytes calldata data + ) external; + + /** + * @notice Deposits tokens with authorization. + * @param amount The amount of tokens being deposited. + * @param authValidAfter The timestamp after which the authorization is valid. + * @param authValidBefore The timestamp before which the authorization is valid. + * @param authNonce A unique nonce for the authorization. + * @param v The V value of the signature. + * @param r The R value of the signature. + * @param s The S value of the signature. + * @param destinationDex The destination dex on HyperCore. + */ + function depositWithAuth( + uint256 amount, + uint256 authValidAfter, + uint256 authValidBefore, + bytes32 authNonce, + uint8 v, + bytes32 r, + bytes32 s, + uint32 destinationDex + ) external; +} From ee54660bdae7a555e02c81e538ddc7af777dd686 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Mon, 8 Dec 2025 11:45:56 -0500 Subject: [PATCH 38/47] Add approve call Signed-off-by: Faisal Usmani --- contracts/libraries/HyperCoreLib.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/libraries/HyperCoreLib.sol b/contracts/libraries/HyperCoreLib.sol index fe3157800..518a473e0 100644 --- a/contracts/libraries/HyperCoreLib.sol +++ b/contracts/libraries/HyperCoreLib.sol @@ -148,6 +148,7 @@ library HyperCoreLib { function transferToCore(address erc20EVMAddress, uint64 erc20CoreIndex, uint256 amountEVMToSend) internal { // index 0 is USDC, which requires a special transfer to core if (erc20CoreIndex == 0) { + IERC20(erc20EVMAddress).forceApprove(CORE_DEPOSIT_WALLET_ADDRESS, amountEVMToSend); ICoreDepositWallet(CORE_DEPOSIT_WALLET_ADDRESS).deposit(amountEVMToSend, CORE_SPOT_DEX_ID); } else { // Transfer the tokens to this contract's address on HyperCore From ffb1684a8f65dead13fcb87f18e995fbcd12dcf0 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Mon, 8 Dec 2025 11:52:49 -0500 Subject: [PATCH 39/47] USDC const Signed-off-by: Faisal Usmani --- contracts/libraries/HyperCoreLib.sol | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/contracts/libraries/HyperCoreLib.sol b/contracts/libraries/HyperCoreLib.sol index 518a473e0..0c5828041 100644 --- a/contracts/libraries/HyperCoreLib.sol +++ b/contracts/libraries/HyperCoreLib.sol @@ -51,7 +51,10 @@ library HyperCoreLib { address public constant CORE_USER_EXISTS_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000810; address public constant TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C; address public constant CORE_WRITER_PRECOMPILE_ADDRESS = 0x3333333333333333333333333333333333333333; - address public constant CORE_DEPOSIT_WALLET_ADDRESS = 0x6B9E773128f453f5c2C60935Ee2DE2CBc5390A24; + + // USDC + address public constant USDC_CORE_DEPOSIT_WALLET_ADDRESS = 0x6B9E773128f453f5c2C60935Ee2DE2CBc5390A24; + uint64 public constant USDC_CORE_INDEX = 0; // CoreWriter action headers bytes4 public constant LIMIT_ORDER_HEADER = 0x01000001; // version=1, action=1 @@ -146,12 +149,12 @@ library HyperCoreLib { * @param amountEVMToSend The amount to transfer on HyperEVM */ function transferToCore(address erc20EVMAddress, uint64 erc20CoreIndex, uint256 amountEVMToSend) internal { - // index 0 is USDC, which requires a special transfer to core - if (erc20CoreIndex == 0) { - IERC20(erc20EVMAddress).forceApprove(CORE_DEPOSIT_WALLET_ADDRESS, amountEVMToSend); - ICoreDepositWallet(CORE_DEPOSIT_WALLET_ADDRESS).deposit(amountEVMToSend, CORE_SPOT_DEX_ID); + // USDC requires a special transfer to core + if (erc20CoreIndex == USDC_CORE_INDEX) { + IERC20(erc20EVMAddress).forceApprove(USDC_CORE_DEPOSIT_WALLET_ADDRESS, amountEVMToSend); + ICoreDepositWallet(USDC_CORE_DEPOSIT_WALLET_ADDRESS).deposit(amountEVMToSend, CORE_SPOT_DEX_ID); } else { - // Transfer the tokens to this contract's address on HyperCore + // For all other tokens, transfer to the asset bridge address on HyperCore IERC20(erc20EVMAddress).safeTransfer(toAssetBridgeAddress(erc20CoreIndex), amountEVMToSend); } } From 11a47082458e8ac931949fad1169ddab0db0ab86 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Mon, 8 Dec 2025 11:53:16 -0500 Subject: [PATCH 40/47] remove unused ICoreDepositWallet methods Signed-off-by: Faisal Usmani --- .../interfaces/ICoreDepositWallet.sol | 94 ------------------- 1 file changed, 94 deletions(-) diff --git a/contracts/external/interfaces/ICoreDepositWallet.sol b/contracts/external/interfaces/ICoreDepositWallet.sol index 2bcf1068f..fa35425b0 100644 --- a/contracts/external/interfaces/ICoreDepositWallet.sol +++ b/contracts/external/interfaces/ICoreDepositWallet.sol @@ -17,21 +17,6 @@ */ pragma solidity ^0.8.0; -/** - * @title IForwardDepositReceiver - * @notice Interface for a contract that can receive deposits from the CCTP Forwarder - */ -interface IForwardDepositReceiver { - /** - * @notice Deposit tokens for a recipient - * @param recipient Recipient of the deposit - * @param amount Amount of tokens to deposit - * @param destinationId Forwarding-address-specific id used in conjunction with - * recipient to route the deposit to a specific location. - */ - function depositFor(address recipient, uint256 amount, uint32 destinationId) external; -} - /** * @title ICoreDepositWallet * @notice Interface for the core deposit wallet @@ -43,83 +28,4 @@ interface ICoreDepositWallet is IForwardDepositReceiver { * @param destinationDex The destination dex on HyperCore. */ function deposit(uint256 amount, uint32 destinationDex) external; - - /** - * @notice Handles the token transfer from the ICoreDepositWallet to the recipient. - * @param to The address receiving the tokens. - * @param amount The amount of tokens being transferred. - * @return success True if the transfer succeeded. - */ - function transfer(address to, uint256 amount) external returns (bool success); - - /** - * @notice Handles cross-chain token withdrawals from HyperCore to a destination chain via CCTP. - * @dev This function initiates a cross-chain transfer of tokens using CCTP to mint tokens on the destination chain. - * It constructs and sends a CCTP message containing encoded hook data that embeds the optional user-provided - * data to be used on the destination chain and determines the CCTP forwarding behavior. - * - * @dev Requirements: - * - Caller must be the token system address. - * - `amount` must be strictly greater than the computed maximum withdrawal fee - * (see calculateCrossChainWithdrawalFee). - * - * @dev CCTP behavior: - * - The CCTP message's destinationCaller is always set to `bytes32(0)` and anyone can call - * MessageTransmitterV2.receiveMessage directly on the destination chain. - * - * @dev Forwarding is the process of completing the mint on the destination chain by paying gas to - * submit a transaction that includes the CCTP attestation. When forwarding is requested: - * - The forwarder subtracts a configured amount of minted tokens from the final recipient, - * not exceeding the CCTP maxFee. - * - This forwarding amount is added to the CCTP maxFee to compute the total fee for the - * cross-chain withdrawal. - * - * @dev Forwarding logic: - * The forwarding logic is determined by the user-provided data: - * - If `data` is empty: the hook data will be a default forwarding hook and forwarding will be performed. - * - If `data` begins with the CCTP forwarding magic bytes: the hook data will embed `data` and forwarding will be performed. - * - Otherwise: the hook data will embed `data` and forwarding will NOT be performed. - * - * @dev Hook data encoding: - * The hook data is constructed using the `CrossChainWithdrawalHookData` library. See that library for the full - * encoding details. - * - * @param from The HyperCore address debited by the cross-chain withdrawal. - * @param destinationRecipient The address receiving the minted tokens on the destination chain, as bytes32. - * @param destinationChainId The CCTP domain ID of the destination chain. - * @param amount The amount of tokens being transferred. - * @param coreNonce The HyperCore transaction nonce. - * @param data Optional user-provided data to embed in the CCTP message payload hook data; also determines the - * forwarding logic as described above. Must be less than or equal to MAX_HOOK_DATA_SIZE. - */ - function coreReceiveWithData( - address from, - bytes32 destinationRecipient, - uint32 destinationChainId, - uint256 amount, - uint64 coreNonce, - bytes calldata data - ) external; - - /** - * @notice Deposits tokens with authorization. - * @param amount The amount of tokens being deposited. - * @param authValidAfter The timestamp after which the authorization is valid. - * @param authValidBefore The timestamp before which the authorization is valid. - * @param authNonce A unique nonce for the authorization. - * @param v The V value of the signature. - * @param r The R value of the signature. - * @param s The S value of the signature. - * @param destinationDex The destination dex on HyperCore. - */ - function depositWithAuth( - uint256 amount, - uint256 authValidAfter, - uint256 authValidBefore, - bytes32 authNonce, - uint8 v, - bytes32 r, - bytes32 s, - uint32 destinationDex - ) external; } From 1656fd8c0452ca1b12e70cb164e37cf1b6ef941d Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Mon, 8 Dec 2025 12:08:50 -0500 Subject: [PATCH 41/47] add back final token check in swap finalization Signed-off-by: Faisal Usmani --- contracts/external/interfaces/ICoreDepositWallet.sol | 2 +- contracts/periphery/mintburn/HyperCoreFlowExecutor.sol | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/contracts/external/interfaces/ICoreDepositWallet.sol b/contracts/external/interfaces/ICoreDepositWallet.sol index fa35425b0..fb55df436 100644 --- a/contracts/external/interfaces/ICoreDepositWallet.sol +++ b/contracts/external/interfaces/ICoreDepositWallet.sol @@ -21,7 +21,7 @@ pragma solidity ^0.8.0; * @title ICoreDepositWallet * @notice Interface for the core deposit wallet */ -interface ICoreDepositWallet is IForwardDepositReceiver { +interface ICoreDepositWallet { /** * @notice Deposits tokens for the sender. * @param amount The amount of tokens being deposited. diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index d05e352b0..1b6457836 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -273,6 +273,9 @@ contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow /// @notice Thrown when an attemp to finalize an already finalized swap is made error SwapAlreadyFinalized(); + /// @notice Thrown when trying to finalize a quoteNonce, calling a finalizeSwapFlows with an incorrect token + error WrongSwapFinalizationToken(bytes32 quoteNonce); + /// @notice Emitted when we're inside the sponsored flow and a user doesn't have a HyperCore account activated. The /// bot should activate user's account first by calling `activateUserAccount` error AccountNotActivatedError(address user); @@ -747,6 +750,7 @@ contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow limitOrderOuts[finalized], finalCoreTokenInfo, finalTokenInfo.swapHandler, + finalToken, availableBalance ); if (!success) { @@ -801,11 +805,13 @@ contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow uint64 limitOrderOut, CoreTokenInfo memory finalCoreTokenInfo, SwapHandler swapHandler, + address finalToken, uint64 availableBalance ) internal returns (bool success, uint64 additionalToSend, uint64 balanceRemaining) { SwapFlowState storage swap = _getMainStorage().swaps[quoteNonce]; if (swap.finalRecipient == address(0)) revert SwapDoesNotExist(); if (swap.finalized) revert SwapAlreadyFinalized(); + if (swap.finalToken != finalToken) revert WrongSwapFinalizationToken(quoteNonce); uint64 totalToSend; (totalToSend, additionalToSend) = _calcSwapFlowSendAmounts( From 5a4c408e806c09551390d45e8bc76143348dbb3c Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Mon, 8 Dec 2025 13:03:00 -0500 Subject: [PATCH 42/47] fixed tests Signed-off-by: Faisal Usmani --- .../foundry/local/SponsorredCCTPDstPeriphery.t.sol | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol b/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol index fc2411a69..4c59b9766 100644 --- a/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol +++ b/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol @@ -11,6 +11,8 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { HyperCoreMockHelper } from "./HyperCoreMockHelper.sol"; import { BaseSimulatorTest } from "./external/hyper-evm-lib/test/BaseSimulatorTest.sol"; import { PrecompileLib } from "./external/hyper-evm-lib/src/PrecompileLib.sol"; +import { CoreWriterLib } from "./external/hyper-evm-lib/src/CoreWriterLib.sol"; +import { CoreSimulatorLib } from "./external/hyper-evm-lib/test/simulation/CoreSimulatorLib.sol"; contract MockMessageTransmitter is IMessageTransmitterV2 { bool internal shouldSucceed = true; @@ -104,12 +106,15 @@ contract SponsoredCCTPDstPeripheryTest is BaseSimulatorTest { // Deploy mock contracts messageTransmitter = new MockMessageTransmitter(); donationBox = new MockDonationBox(); - usdc = new MockUSDC(); + usdc = MockUSDC(0xb88339CB7199b77E23DB6E890353E22632Ba630f); + + vm.prank(0x68e37dE8d93d3496ae143F2E900490f6280C57cD); + usdc.transfer(finalRecipient, 1000e6); // Setup HyperCore precompile mocks using the helper // setupDefaultHyperCoreMocks(address(usdc), "Mock USDC", 6); hyperCore.forceAccountActivation(finalRecipient); - hyperCore.forceTokenInfo(CORE_INDEX, "USDC", address(usdc), 8, 8, 0); + // hyperCore.forceTokenInfo(CORE_INDEX, "USDC", address(usdc), 8, 8, 0); // Deploy periphery vm.startPrank(admin); @@ -124,8 +129,9 @@ contract SponsoredCCTPDstPeripheryTest is BaseSimulatorTest { IHyperCoreFlowExecutor(address(periphery)).setCoreTokenInfo(address(usdc), CORE_INDEX, true, 1e6, 1e6); vm.stopPrank(); - // Mint USDC to periphery for testing - usdc.mint(address(periphery), 10000e6); + // Transfer USDC to periphery for testing + vm.prank(0x68e37dE8d93d3496ae143F2E900490f6280C57cD); + usdc.transfer(address(periphery), 10000e6); } /// @dev Helper function to create a valid CCTP message From 216baa3b55bc1ded006e2eef9c4b85ba07722913 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Mon, 8 Dec 2025 14:23:34 -0500 Subject: [PATCH 43/47] fixed tests Signed-off-by: Faisal Usmani --- broadcast/deployed-addresses.json | 6 +++--- broadcast/deployed-addresses.md | 4 ++-- test/evm/foundry/local/HyperCoreFlowExecutor.t.sol | 6 ++---- .../evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol | 10 ++-------- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/broadcast/deployed-addresses.json b/broadcast/deployed-addresses.json index 4d72aa918..10e764c3d 100644 --- a/broadcast/deployed-addresses.json +++ b/broadcast/deployed-addresses.json @@ -484,9 +484,9 @@ "transaction_hash": "0xf72c3e798991af0e7123197bfd7da40585f936b4353b91159b749008ceac541a" }, "SponsoredCCTPDstPeriphery": { - "address": "0x83e245941befbde29682df068bcda006a804eb0c", - "block_number": 20793257, - "transaction_hash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802" + "address": "0xb63c02e60c05f05975653edc83f876c334e07c6d", + "block_number": 21313320, + "transaction_hash": "0x7ba663e21e7399474f6ce30190460110c285ff703b1bc1f661936550fd4997a5" }, "PermissionedMulticallHandler": { "address": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", diff --git a/broadcast/deployed-addresses.md b/broadcast/deployed-addresses.md index 5f7a39ee1..e17821e75 100644 --- a/broadcast/deployed-addresses.md +++ b/broadcast/deployed-addresses.md @@ -160,7 +160,7 @@ This file contains the latest deployed smart contract addresses from the broadca | Contract Name | Address | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| DonationBox | [0x002E76DC036A1efF1488ee5435eE66C6aBF32674](https://hyperevmscan.io//address/0x002E76DC036A1efF1488ee5435eE66C6aBF32674) | +| DonationBox | [0xbC217096db9EB6d2782c1d9E725D462077a4d1f6](https://hyperevmscan.io//address/0xbC217096db9EB6d2782c1d9E725D462077a4d1f6) | | DonationBox | [0x90E2487764E5316a2e4109c2Ed40A3B3ad423659](https://hyperevmscan.io//address/0x90E2487764E5316a2e4109c2Ed40A3B3ad423659) | | DstOFTHandler | [0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461](https://hyperevmscan.io//address/0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461) | | Helios | [0xc19B7EF43a6eBd393446F401d1eCFac01B181ac0](https://hyperevmscan.io//address/0xc19B7EF43a6eBd393446F401d1eCFac01B181ac0) | @@ -170,7 +170,7 @@ This file contains the latest deployed smart contract addresses from the broadca | SpokePool | [0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04](https://hyperevmscan.io//address/0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04) | | SpokePoolPeriphery | [0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb](https://hyperevmscan.io//address/0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb) | | SpokePoolVerifier | [0x3Fb9cED51E968594C87963a371Ed90c39519f65A](https://hyperevmscan.io//address/0x3Fb9cED51E968594C87963a371Ed90c39519f65A) | -| SponsoredCCTPDstPeriphery | [0x83e245941BefbDe29682dF068Bcda006A804eb0C](https://hyperevmscan.io//address/0x83e245941BefbDe29682dF068Bcda006A804eb0C) | +| SponsoredCCTPDstPeriphery | [0xb63c02e60C05F05975653edC83F876C334E07C6d](https://hyperevmscan.io//address/0xb63c02e60C05F05975653edC83F876C334E07C6d) | ## Lisk (1135) diff --git a/test/evm/foundry/local/HyperCoreFlowExecutor.t.sol b/test/evm/foundry/local/HyperCoreFlowExecutor.t.sol index aa8e1f3e9..2cf61d9c2 100644 --- a/test/evm/foundry/local/HyperCoreFlowExecutor.t.sol +++ b/test/evm/foundry/local/HyperCoreFlowExecutor.t.sol @@ -66,7 +66,7 @@ contract HyperCoreFlowExecutorTest is BaseSimulatorTest { finalRecipient = makeAddr("finalRecipient"); - token = new MockERC20(); + token = MockERC20(0xb88339CB7199b77E23DB6E890353E22632Ba630f); donationBox = new DonationBox(); handler = new TestHyperCoreHandler(address(donationBox), address(token)); @@ -74,9 +74,7 @@ contract HyperCoreFlowExecutorTest is BaseSimulatorTest { vm.prank(donationBox.owner()); donationBox.transferOwnership(address(handler)); - // Link token to HyperCore and set token info in the module via delegatecall - // name, weiDecimals=8, szDecimals=8, evmExtraWeiDecimals=0 - hyperCore.forceTokenInfo(CORE_INDEX, "MOCK", address(token), 8, 8, 0); + // Set token info in the module via delegatecall handler.callSetCoreTokenInfo(address(token), CORE_INDEX, true, 1e6, 1e6); // Ensure recipient has an active HyperCore account for sponsored simple transfer path diff --git a/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol b/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol index 4c59b9766..34042f60a 100644 --- a/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol +++ b/test/evm/foundry/local/SponsorredCCTPDstPeriphery.t.sol @@ -108,13 +108,8 @@ contract SponsoredCCTPDstPeripheryTest is BaseSimulatorTest { donationBox = new MockDonationBox(); usdc = MockUSDC(0xb88339CB7199b77E23DB6E890353E22632Ba630f); - vm.prank(0x68e37dE8d93d3496ae143F2E900490f6280C57cD); - usdc.transfer(finalRecipient, 1000e6); - // Setup HyperCore precompile mocks using the helper - // setupDefaultHyperCoreMocks(address(usdc), "Mock USDC", 6); hyperCore.forceAccountActivation(finalRecipient); - // hyperCore.forceTokenInfo(CORE_INDEX, "USDC", address(usdc), 8, 8, 0); // Deploy periphery vm.startPrank(admin); @@ -129,9 +124,8 @@ contract SponsoredCCTPDstPeripheryTest is BaseSimulatorTest { IHyperCoreFlowExecutor(address(periphery)).setCoreTokenInfo(address(usdc), CORE_INDEX, true, 1e6, 1e6); vm.stopPrank(); - // Transfer USDC to periphery for testing - vm.prank(0x68e37dE8d93d3496ae143F2E900490f6280C57cD); - usdc.transfer(address(periphery), 10000e6); + // Deal USDC to periphery for testing + deal(address(usdc), address(periphery), 10000e6); } /// @dev Helper function to create a valid CCTP message From 8a0838a4edca41977503e75f97a47b51ecac1657 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Mon, 8 Dec 2025 14:24:11 -0500 Subject: [PATCH 44/47] new deployment Signed-off-by: Faisal Usmani --- .../999/run-latest.json | 154 +++++++++--------- .../mintburn/cctp/createSponsoredDeposit.sol | 8 +- 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json b/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json index 24f6cc66c..e3500d80e 100644 --- a/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json +++ b/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json @@ -1,68 +1,68 @@ { "transactions": [ { - "hash": "0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d", + "hash": "0xf8b97d99b6c68adf40a93b675eee70389c3c3d621d018dceb796c2cf58949678", "transactionType": "CREATE", "contractName": "DonationBox", - "contractAddress": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "contractAddress": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6", "function": null, "arguments": null, "transaction": { "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", - "gas": "0x522e7", + "gas": "0x4fb31", "value": "0x0", - "input": "0x6080806040523461005a575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610344908161005f8239f35b5f80fdfe608060409080825260049081361015610016575f80fd5b5f3560e01c908163715018a61461026e5781638da5cb5b1461024c57508063f2fde38b146101d45763f3fef3a31461004c575f80fd5b346101545781600319360112610154578035916001600160a01b0383168093036101545760249261007b6102d2565b8151916020830163a9059cbb60e01b815233868501528535604485015260448452608084019367ffffffffffffffff94818110868211176101c25760c08201818110878211176101b0578452602090527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152515f9182919082865af1923d1561019f573d9181831161018d57805195601f8401601f19908116603f011687019283118784101761017b575052835261013e93503d5f602085013e6102e5565b8051908115918215610158575b50501561015457005b5f80fd5b819250906020918101031261015457602001518015158103610154575f8061014b565b60418891634e487b7160e01b5f52525ffd5b86604187634e487b7160e01b5f52525ffd5b5050915061013e92506060916102e5565b88604189634e487b7160e01b5f52525ffd5b87604188634e487b7160e01b5f52525ffd5b503461015457602036600319011261015457356001600160a01b03808216809203610154576102016102d2565b8115610154575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b34610154575f366003190112610154576020906001600160a01b035f54168152f35b34610154575f366003190112610154576102866102d2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361015457565b90156102ff578151156102f6575090565b3b156101545790565b50805190811561015457602001fdfea2646970667358221220c55691de465342d56d68c31160f5d7d661422ef39ebccaa5223872c823ad6d6964736f6c63430008180033", - "nonce": "0x243", + "input": "0x608080604052346059575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610320908161005e8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c908163715018a6146102265781638da5cb5b1461020457508063f2fde38b146101845763f3fef3a314610048575f80fd5b3461013c57604036600319011261013c576004356001600160a01b03811680910361013c5761007561028a565b6040515f806020830163a9059cbb60e01b81523360248501526024356044850152604484526100a560648561029d565b604051936100b460408661029d565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020860152519082865af13d15610177573d9067ffffffffffffffff821161016357604051610126949092610118601f8201601f19166020018561029d565b83523d5f602085013e6102bf565b8051908115918215610140575b50501561013c57005b5f80fd5b819250906020918101031261013c5760200151801515810361013c575f80610133565b634e487b7160e01b5f52604160045260245ffd5b91610126926060916102bf565b3461013c57602036600319011261013c576004356001600160a01b03811680910361013c576101b161028a565b801561013c576001600160a01b035f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b3461013c575f36600319011261013c576020906001600160a01b035f54168152f35b3461013c575f36600319011261013c5761023e61028a565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361013c57565b90601f8019910116810190811067ffffffffffffffff82111761016357604052565b919250156102db578151156102d2575090565b3b1561013c5790565b50805190811561013c57602001fdfea26469706673582212201599abe8d67ef129ea7b740727c3050bd5ff3fc0efd05707667feeec0709c1f864736f6c634300081e0033", + "nonce": "0x29c", "chainId": "0x3e7" }, "additionalContracts": [], "isFixedGasLimit": false }, { - "hash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "hash": "0x7ba663e21e7399474f6ce30190460110c285ff703b1bc1f661936550fd4997a5", "transactionType": "CREATE", "contractName": "SponsoredCCTPDstPeriphery", - "contractAddress": "0x83e245941befbde29682df068bcda006a804eb0c", + "contractAddress": "0xb63c02e60c05f05975653edc83f876c334e07c6d", "function": null, "arguments": [ "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", - "0x002E76DC036A1efF1488ee5435eE66C6aBF32674", + "0xbC217096db9EB6d2782c1d9E725D462077a4d1f6", "0xb88339CB7199b77E23DB6E890353E22632Ba630f", "0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba" ], "transaction": { "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", - "gas": "0x8ec9d8", + "gas": "0x90de56", "value": "0x0", - "input": "0x610100346200027257601f6200833338819003918201601f19168301926001600160401b03929091838511838610176200025e578160a092849260409788528339810103126200027257620000548162000276565b91620000636020830162000276565b926200007185840162000276565b926200008e6080620000866060840162000276565b920162000276565b60015f5586519093615d3e808301918211838310176200025e5788918391620025f583396001600160a01b03978816815284881660208201520301905ff09283156200025457848094166080525f7f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68082527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020528160018a82200181815491557fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff92838380a47f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f80835260018a8420019183835493558380a460a05260e0521660c0527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0080546001600160a01b031916919092161790556107087fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0155620001f7336200028b565b50516122b890816200033d82396080518181816101fe01526112fa015260a051818181610711015281816114650152612030015260c0518181816107db0152610aef015260e0518181816108aa01528181610c3f0152610cf50152f35b86513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b51906001600160a01b03821682036200027257565b6001600160a01b03165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260409020547f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009060ff1662000336575f805260205260405f20815f5260205260405f20600160ff1982541617905533905f7f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b50505f9056fe60806040526004361015610030575b361561002e573461002a5761002236610dbb565b602081519101f35b5f80fd5b005b5f3560e01c806301ffc9a71461017f57806309cfd6751461017a5780631b1062b81461017557806321081d3c14610170578063238ac9331461016b578063248a9ca3146101665780632561efb214610161578063277e661d1461015c5780632f2ff15d1461015757806336568abe14610152578063490e662f1461014d5780634b3b029b146101485780634f7d9d2e14610143578063657cad8a1461013e5780636c19e783146101395780638c73eb041461013457806391d148541461012f578063a217fddf1461012a578063c55dae6314610125578063d547741f146101205763feb617240361000e5761092d565b6108ce565b61088b565b610871565b6107ff565b6107bc565b610735565b6106f2565b6106b6565b61067c565b610642565b6105f7565b610598565b610542565b6104f6565b6104aa565b610465565b61042b565b610388565b6101df565b3461002a57602036600319011261002a5760043563ffffffff60e01b811680910361002a57602090637965db0b60e01b81149081156101c4575b506040519015158152f35b6301ffc9a760e01b1490505f6101b9565b5f91031261002a57565b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff811161024a57604052565b610222565b6040810190811067ffffffffffffffff82111761024a57604052565b6080810190811067ffffffffffffffff82111761024a57604052565b90601f8019910116810190811067ffffffffffffffff82111761024a57604052565b6040519060c0820182811067ffffffffffffffff82111761024a57604052565b604051906102d68261026b565b565b60405190610200820182811067ffffffffffffffff82111761024a57604052565b604051906060820182811067ffffffffffffffff82111761024a57604052565b604051906102d68261024f565b67ffffffffffffffff811161024a57601f01601f191660200190565b81601f8201121561002a5780359061035982610326565b926103676040519485610287565b8284526020838301011161002a57815f926020809301838601378301015290565b3461002a57606036600319011261002a5767ffffffffffffffff60043581811161002a576103ba903690600401610342565b60243582811161002a576103d2903690600401610342565b9160443590811161002a575f926103f0610424923690600401610342565b906103f9610e00565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1009360018555610abc565b5560015f55005b3461002a575f36600319011261002a5760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b3461002a575f36600319011261002a5760206001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f005416604051908152f35b3461002a57602036600319011261002a576004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526020600160405f200154604051908152f35b3461002a57602036600319011261002a5761050f610e00565b61051761175e565b6004357fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f015560015f55005b3461002a57602036600319011261002a5760043567ffffffffffffffff811161002a5761057d6105786020923690600401610342565b611823565b6040519015158152f35b6001600160a01b0381160361002a57565b3461002a57604036600319011261002a5761002e6024356004356105bb82610587565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526105f2600160405f2001546117b4565b6118cf565b3461002a57604036600319011261002a5760243561061481610587565b336001600160a01b038216036106305761002e90600435611988565b60405163334bd91960e11b8152600490fd5b3461002a575f36600319011261002a5760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b3461002a575f36600319011261002a5760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b3461002a575f36600319011261002a5760207fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0154604051908152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57602036600319011261002a5760043561075281610587565b61075a610e00565b61076261175e565b6001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0091167fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905560015f555f80f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57604036600319011261002a57602060ff61086560243561082381610587565b6004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800845260405f20906001600160a01b03165f5260205260405f2090565b54166040519015158152f35b3461002a575f36600319011261002a5760206040515f8152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57604036600319011261002a5761002e6024356004356108f182610587565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052610928600160405f2001546117b4565b611988565b3461002a57602036600319011261002a576004355f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f02602052602060ff60405f2054166040519015158152f35b9081602091031261002a5751801515810361002a5790565b5f5b8381106109a45750505f910152565b8181015183820152602001610995565b906020916109cd81518092818552858086019101610993565b601f01601f1916010190565b90916109f06109fe936040845260408401906109b4565b9160208184039101526109b4565b90565b6040513d5f823e3d90fd5b9060206109fe9281815201906109b4565b634e487b7160e01b5f52601160045260245ffd5b5f19810191908211610a3f57565b610a1d565b91908203918211610a3f57565b634e487b7160e01b5f52602160045260245ffd5b60c09093929193610ab88160e081019660a08091805184526020810151602085015260408101516001600160a01b0380911660408601526060820151166060850152608081015160808501520151910152565b0152565b604051630afd9fa560e31b81526020939192849082908190610ae29087600484016109d9565b03815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af18015610db657610d99575b5060405163277e661d60e01b8152838180610b3a8660048301610a0c565b0381305afa5f9181610d6a575b50610b525750505050565b15610d6557610b63610b6c92610f1f565b92909182611049565b80610d1a575b610b80836060840151610a44565b92610100830151610bee610b9861018086015161129a565b8415610cf057610bde610baf6101a088015161129a565b915b8615610ce957610140880151945b610bc76102a9565b998a528a8a01526001600160a01b03166040890152565b6001600160a01b03166060870152565b608085015260a084015280610cb9575b15610c78576102d69281610c65600160ff610c276101c06101e0610c7398015195015160ff1690565b161492610c326102c9565b9586526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690860152565b604084015215156060830152565b611383565b610cb692610ca3610160610cb193015160405194859363073ffe1360e31b9085015260248401610a65565b03601f198101835282610287565b6112ef565b50565b5060ff610ccb6101c083015160ff1690565b1660018114908115610cde575b50610bfe565b60029150145f610cd8565b5f94610bbf565b610bde7f000000000000000000000000000000000000000000000000000000000000000091610bb1565b610d60610d536101008401515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b805460ff19166001179055565b610b72565b505050565b610d8b919250853d8711610d92575b610d838183610287565b81019061097b565b905f610b47565b503d610d79565b610daf90843d8611610d9257610d838183610287565b505f610b1c565b610a01565b610dc3610e00565b610dcc81610326565b610dd96040519182610287565b81815236821161002a575f602083610df9948383860137830101526112ef565b9060015f55565b60025f541461002a5760025f55565b610e176102d8565b905f82525f60208301525f60408301525f60608301525f60808301525f60a08301525f60c08301525f60e08301525f6101008301525f6101208301525f6101408301525f6101608301525f6101808301525f6101a08301525f6101c083015260606101e0830152565b81601f8201121561002a578051610e9681610326565b92610ea46040519485610287565b8184526020828401011161002a576109fe9160208085019101610993565b9190916101008184031261002a5780519260208201519260408301519260608101519260808201519260a08301519260c081015160ff8116810361002a579260e082015167ffffffffffffffff811161002a576109fe9201610e80565b90610f8a610f2b610e0f565b92610f42610f3882611a38565b63ffffffff168552565b610f5b610f4e82611a5a565b63ffffffff166020860152565b610f6481611a7a565b60a0850152610f82610f7582611a6a565b63ffffffff1660e0860152565b805190611c56565b90610f9482611a8a565b6040840152610fa282611a9a565b6060840152610fb082611aaa565b6080840152610fbe82611aba565b60c0840152611008610fed610fde610fd585611aca565b94805190611cb0565b60208082518301019101610ec2565b6101e08c999394959697989901526101c08b019060ff169052565b6101a0890152610180880152610160870152610140860152610120850152610100840152565b9060018201809211610a3f57565b91908201809211610a3f57565b6112086001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f00541692611086835163ffffffff1690565b93611098602085015163ffffffff1690565b946040850151956111186060870151608088015160a089015160c08a0151916110c860e08c015163ffffffff1690565b936040519c8d9760208901998a96929360e09692959199989461010089019a63ffffffff97888092168b521660208a015260408901526060880152608087015260a086015260c085015216910152565b039561112c601f1997888101835282610287565b5190206111f36111ff61010087019788516101208901986111d48a516111c861014084015193610160810151906101808101516101a0820151906101e06111786101c085015160ff1690565b9301516020815191012093604051988997602089019b8c9490989796929360ff9460e0979361010088019b8852602088015260408701526060860152608085015260a08401521660c08201520152565b03848101835282610287565b5190206040805160208101968752908101919091529283906060820190565b03908101835282610287565b51902090611cdd565b918261124d575b5081611219575090565b6112479150517fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f01549061103c565b42111590565b61129391925061128861128f91515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b5460ff1690565b1590565b905f61120f565b8060a01c6112ae576001600160a01b031690565b6040516379ec0ed760e11b8152600490fd5b3d156112ea573d906112d182610326565b916112df6040519384610287565b82523d5f602084013e565b606090565b5f80916020815191017f00000000000000000000000000000000000000000000000000000000000000005af46113236112c0565b901561132c5790565b602081519101fd5b6102d69092919260c081019360a08091805184526020810151602085015260408101516001600160a01b0380911660408601526060820151166060850152608081015160808501520151910152565b61138b611aea565b5060409081810151916113a983519360208080968301019101611b43565b92808301936113d16113c56113c587516001600160a01b031690565b6001600160a01b031690565b83516370a0823160e01b80825230600483015296909290918490849060249082905afa928315610db6575f9361173f575b506060926114216113c56113c5868a51016001600160a01b0390511690565b865189815230600482015291908690839060249082905afa918215610db6575f92611720575b506114a661145f6113c586516001600160a01b031690565b9361148f7f000000000000000000000000000000000000000000000000000000000000000095868c515191611da9565b89518701513091906001600160a01b031690611fab565b6001600160a01b038094166114c286516001600160a01b031690565b918a515191803b1561002a576114f3935f80948d5196879586948593633a5be8cb60e01b8552309160048601611c28565b03925af18015610db657611707575b5061151a6113c56113c586516001600160a01b031690565b87518a815230600482015291908790839060249082905afa918215610db6575f926116e8575b50036116575750610cb6965061157261156083516001600160a01b031690565b84885101906001600160a01b03169052565b855151915b61158a83885160a0815191015190612126565b60a088510152828751527fb88fc27be67e678ffb77faf8f8bb00d39b66b4845e4f7ec1e623b0f15abd52138751926115cd8785015193516001600160a01b031690565b946116046115e68887519701516001600160a01b031690565b91838b51948594169816968360209093929193604081019481520152565b0390a4835190840151156116365750610ca3610cb19293519351938492632132ff4360e11b9084015260248301611334565b9250610ca3610cb19251938492627f9f5760e91b9084015260248301611334565b846116736113c56113c5878b51016001600160a01b0390511690565b8751998a523060048b0152899060249082905afa908115610db657610cb6985f926116b9575b508082106116b0576116aa91610a44565b91611577565b50505f91611577565b6116da919250863d88116116e1575b6116d28183610287565b810190611c19565b905f611699565b503d6116c8565b611700919250873d89116116e1576116d28183610287565b905f611540565b8061171461171a92610236565b806101d5565b5f611502565b611738919250863d88116116e1576116d28183610287565b905f611447565b611757919350843d86116116e1576116d28183610287565b915f611402565b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161561179657565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff6117fb3360405f20906001600160a01b03165f5260205260405f2090565b5416156118055750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b6102988151106118ca5761183e61183982611ada565b61129a565b6001600160a01b03309116036118ca578051908160941082609418028218808303928311610a3f5782610fde9260206118796118ab96610326565b936118876040519586610287565b83855261189384610326565b8583019390601f19013685370101905e805190611cb0565b505094509250505060a01c1590816118c1575090565b905060a01c1590565b505f90565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205260ff6119178460405f20906001600160a01b03165f5260205260405f2090565b541661198157815f526020526119418260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916600117905533916001600160a01b0316907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205260ff6119d08460405f20906001600160a01b03165f5260205260405f2090565b54161561198157815f526020526119fb8260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916905533916001600160a01b0316907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b6008815110611a48576008015190565b604051632d0483c560e21b8152600490fd5b600c815110611a4857600c015190565b6090815110611a48576090015190565b608c815110611a4857608c015190565b6044815110611a48576044015190565b6064815110611a48576064015190565b6024815110611a48576024015190565b60a4815110611a485760a4015190565b60c4815110611a485760c4015190565b60d8815110611a485760d8015190565b6040519060c0820182811067ffffffffffffffff82111761024a576040525f60a0838281528260208201528260408201528260608201528260808201520152565b67ffffffffffffffff811161024a5760051b60200190565b90602090818382031261002a57825167ffffffffffffffff9384821161002a57019080601f8301121561002a578151611b7b81611b2b565b94604090611b8c6040519788610287565b828752858088019360051b8601019484861161002a57868101935b868510611bb957505050505050505090565b845183811161002a5782019084601f19838903011261002a57845190611bde8261024f565b89830151611beb81610587565b8252858301519185831161002a57611c0a898c80969581960101610e80565b83820152815201940193611ba7565b9081602091031261002a575190565b906109fe94936080936001600160a01b038093168452602084015216604082015281606082015201906109b4565b908151908180821091180218806094108160941802811891828203918211610a3f576020611c8383610326565b93611c916040519586610287565b838552611c9d84610326565b8583019390601f19013685370101905e90565b9081519081808210911802188060e4108160e41802811891828203918211610a3f576020611c8383610326565b611ce783836121a9565b6005819592951015611da457159384611d8e575b508315611d09575b50505090565b5f929350908291604051611d4181610ca36020820194630b135d3f60e11b998a875260248401526040604484015260648301906109b4565b51915afa90611d4e6112c0565b82611d80575b82611d64575b50505f8080611d03565b90915060208180518101031261002a5760200151145f80611d5a565b915060208251101591611d54565b6001600160a01b0383811691161493505f611cfb565b610a51565b905f611e3093819260405194602086019263a9059cbb60e01b84526001600160a01b038093166024880152604487015260448652611de68661026b565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051611e178161024f565b8181520152519082855af1611e2a6112c0565b91612259565b8051908115918215611e46575b50501561002a57565b611e59925060208091830101910161097b565b5f80611e3d565b90611e6a82611b2b565b6040611e796040519283610287565b8382528193611e8a601f1991611b2b565b01905f5b828110611e9b5750505050565b8151906060918281019281841067ffffffffffffffff85111761024a5760209385525f82528390818301525f85830152828701015201611e8e565b8051821015611eea5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b9190916020908181526060916060820192855193604080848601528551809252608085018460808460051b8801019701935f925b848410611f5a575050505050506040906109fe93949501519101906001600160a01b03169052565b9091929394978680600192607f198b82030187528b51906001600160a01b0382511681528580611f95858501518a878601528a8501906109b4565b9301519101529a01940194019294939190611f32565b92918351611fc0611fbb8261102e565b611e60565b925f5b82811061209a575060405163ef8738d360e01b60208201526001600160a01b03918216602482015290831660448083019190915281529394506111f3936109fe93601f1993909261208b9261206f919061201e606482610287565b6120266102f9565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681529060208201525f60408201526120688287611ed6565b5284611ed6565b50612078610319565b9283526001600160a01b03166020830152565b60405193849160208301611efe565b806120b86120aa6001938a611ed6565b51516001600160a01b031690565b6020806120c5848c611ed6565b510151906120e36120d46102f9565b6001600160a01b039094168452565b8201525f60408201526120f68288611ed6565b526121018187611ed6565b5001611fc3565b8115612112570490565b634e487b7160e01b5f52601260045260245ffd5b9190808301809311610a3f57670de0b6b3a76400009283820291808304851490151715610a3f57808201809211610a3f575f198201918211610a3f5761216b91612108565b820391808311610a3f57808202908282041482151715610a3f57828101809111610a3f576109fe9261219f6121a492610a31565b612108565b610a44565b9060418151145f146121d5576121d191602082015190606060408401519301515f1a906121de565b9091565b50505f90600290565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161224e576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15610db6575f516001600160a01b0381161561224657905f90565b505f90600190565b505050505f90600390565b90156122735781511561226a575090565b3b1561002a5790565b50805190811561002a57602001fdfea26469706673582212205aa57543ac0ca275e5f6cf9aea12226da06ca1a789701807a553fd671792689c64736f6c6343000818003360c0346200010457601f62005d3e38819003918201601f19168301916001600160401b038311848410176200010857808492604094855283398101031262000104576200005a602062000052836200011c565b92016200011c565b6001600160a01b0390911660805260a052604051615c0c90816200013282396080518181816108b7015281816109ff0152818161158b0152818161182f01528181612a1701528181614a0501528181614b7c01528181614f68015261504e015260a051818181610670015281816107de01528181611a7b01528181613a6e01528181614188015281816141e401528181614288015281816142b40152818161435301526146990152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001045756fe6080604052600436101562000012575f80fd5b5f803560e01c806246912e146200257057806301ffc9a71462002515578063037a06a414620022c957806304c73f6014620022a9578063057f037014620021f45780631f74a0b5146200217257806321081d3c1462002135578063248a9ca314620020e75780632e748b211462001f7f5780632f2ff15d1462001f1a578063319adf9f1462001b6657806336568abe1462001b1757806337710e201462001abd57806339fff0981462001a225780633b1c6a0114620017275780633cf3a02514620016eb5780634265fe861462001699578063490e662f146200165c5780634b3b029b146200161f578063502a82e21462001554578063521c98ba1462000d9157806369b97ac71462000d7157806379c7b60b1462000d1757806379c7f2891462000c1557806390a0827b1462000bd457806391d148541462000b6657806396cc2cfb14620008f9578063a217fddf14620008db578063a4b672b61462000895578063af5de6f91462000802578063c55dae6314620007bc578063ccbedaec1462000542578063d06e28ed1462000443578063d547741f14620003da578063e38b73a914620003bc578063ea0aaf241462000359578063eb84e7f2146200024b5763ff3eae0014620001e2575f80fd5b34620002485760c0366003190112620002485762000200366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10054156200023657620002339062004e31565b80f35b60405163cd6d8f7d60e01b8152600490fd5b80fd5b503462000248576020366003190112620002485760408160c09260a0835162000274816200270b565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051620002c8816200270b565b6001600160a01b039060028284541693848352600181015490602084019185811683526001600160401b0393849283604088019360a01c1683520154956060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b50346200024857602036600319011262000248576020620003b36200037d620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b50346200024857806003193601126200024857602060405160068152f35b50346200024857604036600319011262000248576200043f600435620003ff620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262000439600160408620015462002e64565b620039b1565b5080f35b503462000248576040366003190112620002485762000461620025de565b602435906fffffffffffffffffffffffffffffffff82168092036200053e576200048a62002e08565b6200049581620032e8565b90836001600160a01b039263ffffffff84608083015116915116813b156200053a57829160448392604051948593849263435354d360e01b845260048401528a60248401525af180156200052f5762000513575b5050167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b6200051e90620026f7565b6200052b57835f620004e9565b8380fd5b6040513d84823e3d90fd5b8280fd5b5f80fd5b50346200024857606036600319011262000248578062000561620025de565b6200056b6200260c565b906200057662002623565b906200058162002d37565b620005bc816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200071a574362000601826001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0380600162000648846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b01541693816001600160401b03938483166200071f575b5050508183166200066e578480f35b7f00000000000000000000000000000000000000000000000000000000000000001684525f8051602062005bb783398151915260205260046040852001541690823b156200071a5760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156200052f5762000702575b8080808480f35b6200070d90620026f7565b6200024857805f620006fb565b505050fd5b1686525f8051602062005bb783398151915260205282600460408820015416853b15620007b85760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152858160648183895af1908115620007ad57869162000791575b82906200065f565b6200079c90620026f7565b620007a957845f62000789565b8480fd5b6040513d88823e3d90fd5b8680fd5b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503462000248576020366003190112620002485760206001600160a01b03620008346200082e620025de565b620031ff565b6109ab60405162000848858301826200277c565b818152848101916200520c8339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152620008898162002744565b51902016604051908152f35b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346200024857806003193601126200024857602090604051908152f35b503462000248576040366003190112620002485762000917620025de565b906200092262002e08565b6200092d8262002f58565b6200093883620032e8565b926200094e60e083510151840b60243562004da2565b9460208401906001600160401b036200097381845116898360a08a01511691620038d2565b1562000b3657869750620009b7856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b620009c48582546200287c565b90556001600160a01b03808616807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051898152a2817f000000000000000000000000000000000000000000000000000000000000000016803b1562000b325760405163f3fef3a360e01b81526001600160a01b038316600482015260248101889052908a908290604490829084905af1801562000b275787918b9162000b08575b505062000a8290608060e0960192848451169062003353565b511692511694510151850b93813b1562000b0457604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156200052f5762000af15750f35b62000afc90620026f7565b620002485780f35b8580fd5b62000b15919250620026f7565b62000b235785895f62000a69565b8880fd5b6040513d8c823e3d90fd5b8980fd5b6040516377e88bc960e11b81526001600160a01b03861660048201526001600160401b0389166024820152604490fd5b50346200024857604036600319011262000248576001600160a01b03604062000b8e620025f5565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020522091165f52602052602060ff60405f2054166040519015158152f35b50346200024857604036600319011262000248576200023362000bf6620025de565b62000c0062002d37565b602435906001600160a01b0333911662003353565b503462000248576020366003190112620002485760a062000c7a62000c39620025de565b62000c4362002d0b565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b0360016040519262000c9384620026db565b805463ffffffff90818116865260ff8160201c1615156020870152818160281c16604087015260481c166060850152015416608082015262000d1560405180926001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565bf35b50346200024857602036600319011262000248576020620003b362000d3b620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b503462000248578060031936011262000248576020604051620f42408152f35b5034620002485760a0366003190112620002485762000daf620025de565b62000db96200263a565b9062000dc46200264e565b6001600160401b0360643516606435036200053e57608435906001600160401b03821682036200053e5762000df862002db1565b62000e0262002c05565b508480604051602081019063ffffffff881682526020815262000e258162002760565b519061080c5afa62000e3662002cd7565b901562001542578051810190602081830312620007b85760208101516001600160401b038111620015055761010081830184031262001505576040519262000e7e8462002727565b602082840101516001600160401b03811162000b325760208201603f828587010101121562000b32576020818486010101519062000ebc8262002cbb565b9162000ecc60405193846200277c565b8083526020840160408284888a01010101116200152a5762000efa9160406020850191878901010162002691565b8452604082840101516001600160401b03811162000b325760208201603f828587010101121562000b3257602081848601010151916001600160401b0383116200152e578260051b6040519362000f5560208301866200277c565b84526020808501920160408285888a01010101116200152a576040838688010101915b60408285888a01010101831062001509575050505060208401526101009062000fa683820160600162003428565b604085015262000fbb6080828501016200512c565b606085015262000fd060a0828501016200512c565b9283608086015262000fe760c08383010162005141565b60a086015262000ffc60e08383010162005141565b60c08601520101519081880b820362001505578160e08401526001600160a01b03808716911603620007b8576200103890870b60643562003829565b506040519162001048836200270b565b825263ffffffff86166020830152821515604083015260608201526001600160401b036064351660808201526001600160401b03831660a08201526001600160a01b03841686525f8051602062005bb783398151915260205260408620815180518051906001600160401b03821162001438578190620010c9855462002c80565b601f8111620014c5575b50602090601f831160011462001458578b926200144c575b50508160011b915f199060031b1c19161782555b60208101518051906001600160401b038211620014385768010000000000000000821162001438576020906001850154836001870155808410620013e5575b500190600184018a5260208a20908a5b8160021c81106200139d5750600319811680820362001334575b50505050936001600160a01b03937f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd82244353969360068460809860026001600160401b039701876040830151168154907bffffffffffffffffffffffffffffffffffffffff0000000000000000606085015160401b169163ffffffff60e01b16171790556003820190898b820151169082549174ff000000000000000000000000000000000000000060a083015160a01b16907fffffffffffffffffff000000000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff00000000000000000000000000000000000000000000169416171717179055620012c5600482018760208601511688198254161781556040850151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b6060830151600582015501908488820151166fffffffffffffffff000000000000000060a0845493015160401b16916fffffffffffffffffffffffffffffffff19161717905563ffffffff6040519816885215156020880152816064351660408801521660608601521692a280f35b928b938c5b81840381106200135d5750505060021c0155838360066001600160401b0362001168565b9091946020620013926001926001600160401b03895116908560031b60031b916001600160401b03809116831b921b19161790565b960192910162001339565b8b8c5b60048110620013b75750838201556001016200114e565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501620013a0565b6200141890600187018d52838d20600380870160021c820192601888831b16806200141f575b500160021c019062004d8a565b5f6200113e565b5f1990818601918254918a03851b1c1690555f6200140b565b634e487b7160e01b8a52604160045260248afd5b015190505f80620010eb565b9250848b5260208b20908b935b601f1984168510620014a9576001945083601f1981161062001490575b505050811b018255620010ff565b01515f1960f88460031b161c191690555f808062001482565b8181015183556020948501946001909301929091019062001465565b620014f390868d5260208d20601f850160051c81019160208610620014fa575b601f0160051c019062004d8a565b5f620010d3565b9091508190620014e5565b8780fd5b906020806040936200151b8662003428565b81520193019290915062000f78565b8b80fd5b634e487b7160e01b8b52604160045260248bfd5b604051639b0c335d60e01b8152600490fd5b503462000248576040366003190112620002485762001572620025de565b6024356200157f62002d37565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156200161b5760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156200052f5762001603575b50506200023391339062003353565b6200160e90620026f7565b6200053a57825f620015f4565b5080fd5b5034620002485780600319360112620002485760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b5034620002485780600319360112620002485760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b5034620002485760c03660031901126200024857620016b8366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576200023390620046c4565b50346200024857602036600319011262000248576200170962002d37565b80808080600435335af16200171d62002cd7565b5015620002485780f35b503462000248576060366003190112620002485762001745620025f5565b6001600160a01b0360443581811692918382036200053e576200176762002e08565b620017728262002f58565b906200177e8462003257565b62000b045760408201511562000b04576001600160401b0391600183608083015116019083821162001a0e57620017bd60e082510151890b8362003829565b5094620017db602083019386855116908760a08601511691620038d2565b1562000b23576200181c906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b620018298682546200287c565b905587837f000000000000000000000000000000000000000000000000000000000000000016803b156200161b5760405163f3fef3a360e01b81526001600160a01b038a166004820152602481018890529082908290604490829084905af180156200052f57620019f0575b50509183826200192494620018bc60e062001913986024975116925101518c0b8962004da2565b508481620019d0575b505050511695604051916020830191169687825260408301526001606083015260608252620018f48262002744565b6040519485926280000360e11b60208501525180928585019062002691565b81010360048101845201826200277c565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062001970906024830190620026b4565b03925af18015620019c557620019b3575b506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b620019be90620026f7565b5f62001981565b6040513d5f823e3d90fd5b620019df620019e79362002847565b168b62003353565b5f8084620018c5565b620019ff9094929394620026f7565b62001505579190875f62001895565b634e487b7160e01b5f52601160045260245ffd5b5034620002485760e0366003190112620002485762001a41366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576001600160a01b0380606083015116907f000000000000000000000000000000000000000000000000000000000000000016145f1462001aae576200023390620046c4565b620002339060c4359062003a45565b50346200024857602036600319011262000248576020620003b362001ae1620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b503462000248576040366003190112620002485762001b35620025f5565b336001600160a01b0382160362001b54576200043f90600435620039b1565b60405163334bd91960e11b8152600490fd5b50346200024857602036600319011262000248576001600160a01b0362001b8c620025de565b62001b9662002c45565b501681525f8051602062005bb783398151915260205260408120906040519162001bc0836200270b565b60405162001bce8162002727565b6040518254818562001be08362002c80565b808352926001811690811562001ef9575060011462001eb4575b62001c08925003826200277c565b815260405160018301805480835290855260208086209083019186915b81600384011062001e72579360069593819362001c73936001600160401b039997549181811062001e5e575b81811062001e47575b81811062001e30575b1062001e21575b5003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c850b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a0830152604051906020825282519060c0602084015262001d31825161010060e08601526101e0850190620026b4565b60208084015185830360df190161010087015280518084529282019492910190835b81811062001e015750505083946001600160401b039260e08385604060a0960151166101208901526001600160a01b036060820151166101408901526001600160a01b0360808201511661016089015260ff858201511661018089015260ff60c0820151166101a08901520151900b6101c086015282602082015116604086015260408101511515606086015260608101516080860152826080820151168286015201511660c08301520390f35b82516001600160401b031686526020958601959092019160010162001d53565b60c01c81526020015f62001c6a565b9260206001918c8560801c16815201930162001c63565b9260206001918c8560401c16815201930162001c5a565b9260206001918c8516815201930162001c51565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162001c25565b5090848652602086209086915b81831062001edc57505090602062001c089282010162001bfa565b602091935080600191548385880101520191019091839262001ec1565b6020925062001c0894915060ff191682840152151560051b82010162001bfa565b50346200024857604036600319011262000248576200043f60043562001f3f620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262001f79600160408620015462002e64565b62003912565b503462000248576080366003190112620002485762001f9d620025de565b62001fa76200260c565b9062001fb262002623565b90606435926fffffffffffffffffffffffffffffffff84168094036200053e5762001fdc62002e08565b62001fe782620032e8565b856001600160a01b03928360808401511692833b156200053a57620020646101048492836040519586948593636f0d192560e11b855260048501906001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565b6001600160401b038091169b8c60a485015216978860c48401528c60e48401525af180156200052f57620020cb575b5050907f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c92604092835195865260208601521692a380f35b620020d990939293620026f7565b62000b045790855f62002093565b50346200024857602036600319011262000248576001604060209260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008452200154604051908152f35b5034620002485780600319360112620002485760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503462000248576060366003190112620002485762002190620025de565b6001600160401b036024358181116200052b57620021b39036906004016200265e565b91909360443591821162000248576020620021ec868686620021d936600489016200265e565b939092620021e662002e08565b6200288a565b604051908152f35b50346200053e5760403660031901126200053e5762002212620025de565b906024356200222062002d37565b6001600160a01b03928360806200223783620032e8565b015116803b156200053e576040516390a0827b60e01b81526001600160a01b038316600482015260248101849052905f908290604490829084905af18015620019c55762002290575b5062000233929333911662003353565b620002339350620022a190620026f7565b5f9262002280565b346200053e575f3660031901126200053e5760206040516305f5e1008152f35b346200053e5760a03660031901126200053e57620022e6620025de565b620022f06200263a565b620022fa6200264e565b6064359063ffffffff938483168093036200053e57608435908582168092036200053e57620023298162002f58565b506200233462002db1565b6001600160a01b03918260016200237b846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b0154168015620024bc575b8360018960405199620023998b620026db565b16988981526020810197151597888152604082018a81526060830190878252856080850197169d8e8852620023fe8a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b9451166cffffffff00000000000000000068ffffffff000000000064ff0000000087549651151560201b16935160281b16935160481b16936cffffffffffffffffffffffffff19161717171781550191511673ffffffffffffffffffffffffffffffffffffffff19825416179055620024778762003257565b156200053e577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9946080946040519788526020880152604087015260608601521692a3005b50620024c882620031ff565b6040516109ab8082018281106001600160401b03821117620025015782916200520c833903905ff58062002386576040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b346200053e5760203660031901126200053e5760043563ffffffff60e01b81168091036200053e57602090637965db0b60e01b81149081156200255e575b506040519015158152f35b6301ffc9a760e01b1490508262002553565b346200053e5760403660031901126200053e57620025dc62002591620025de565b6001600160a01b03620025a36200260c565b91620025ae62002d37565b165f525f8051602062005bb78339815191526020526001600160401b03600460405f20015416339062002ea6565b005b600435906001600160a01b03821682036200053e57565b602435906001600160a01b03821682036200053e57565b602435906001600160401b03821682036200053e57565b604435906001600160401b03821682036200053e57565b6024359063ffffffff821682036200053e57565b6044359081151582036200053e57565b9181601f840112156200053e578235916001600160401b0383116200053e576020808501948460051b0101116200053e57565b5f5b838110620026a35750505f910152565b818101518382015260200162002693565b90602091620026cf8151809281855285808601910162002691565b601f01601f1916010190565b60a081019081106001600160401b038211176200250157604052565b6001600160401b0381116200250157604052565b60c081019081106001600160401b038211176200250157604052565b61010081019081106001600160401b038211176200250157604052565b608081019081106001600160401b038211176200250157604052565b604081019081106001600160401b038211176200250157604052565b90601f801991011681019081106001600160401b038211176200250157604052565b60c09060031901126200053e5760405190620027ba826200270b565b60043582526024356020830152816001600160a01b0360443581811681036200053e57604083015260643590811681036200053e576060820152608435608082015260a060a435910152565b9190811015620028175760051b0190565b634e487b7160e01b5f52603260045260245ffd5b9190916001600160401b038080941691160191821162001a0e57565b9073200000000000000000000000000000000000000091820180921162001a0e57565b906305f5e0ff820180921162001a0e57565b9190820180921162001a0e57565b919392935f945f948083036200053e57620028d5856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200053e57869796939291620028ef8662002f58565b926080620028fd88620032e8565b01906001600160a01b0392838351169460208701976200292a6001600160401b0397888b5116906200343d565b845f9b5b1062002b94575b50508c15925062002b8791505057436200297f886001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5582861662002993575b5050505050505050565b620029c195620029ab60e0865101515f0b8262003829565b85889992995116908660a08901511691620038d2565b1562002b4d575062002a03876001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62002a108782546200287c565b90558682167f00000000000000000000000000000000000000000000000000000000000000008316803b156200053e5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101899052905f908290604490829084905af18015620019c55762002b29575b509062002a9587849360e096958451169062003353565b511693511691510151850b93823b1562000b0457604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156200052f5762002b11575b80808080808062002989565b62002b1d8291620026f7565b62000248578062002b05565b60e094939291995062002b3c90620026f7565b5f98929350909162002a9562002a7e565b8451608001516040516377e88bc960e11b81529084166001600160a01b031660048201526001600160401b03919091166024820152604490fd5b505f985050505050505050565b62002ba6859e9f809c95968462002806565b359062002bb58c858862002806565b359189831683036200053e578a62002bd2938a8a51169262003520565b9490911562002bfb5762002beb8f93926001926200282b565b9b019e9d8f95949192956200292e565b9a9e9d5062002935565b6040519062002c148262002727565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b6040519062002c54826200270b565b5f60a08362002c6262002c05565b81528260208201528260408201528260608201528260808201520152565b90600182811c9216801562002cb0575b602083101462002c9c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162002c90565b6001600160401b0381116200250157601f01601f191660200190565b3d1562002d06573d9062002ceb8262002cbb565b9162002cfb60405193846200277c565b82523d5f602084013e565b606090565b6040519062002d1a82620026db565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a60205260409020547f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f9060ff161562002d935750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161562002dea57565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd60205260409020547f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef69060ff161562002d935750565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f2054161562002d935750565b620019139262002eec92602492604051926001600160a01b0360208501931683526001600160401b03809216604085015216606083015260608252620018f48262002744565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062002f38906024830190620026b4565b03925af18015620019c55762002f4b5750565b62002f5690620026f7565b565b9062002f6362002c45565b506001600160a01b038092165f5260205f8051602062005bb78339815191528152604092835f2084519062002f98826200270b565b85519562002fa68762002727565b80518254905f8162002fb88462002c80565b918282526001948a86821691825f14620031e05750506001146200319f575b62002fe5925003826200277c565b88528083019082519087829384928282549586815201915f52825f20945f5b8160038201106200315a57620030469654928583831062003140575b83831062003124575b83831062003107575b505010620030f8575b50905003826200277c565b858801526006600283015492856001600160401b0394858116858c0152841c1660608a015260ff60038201549760808b019a888a168c52828a60a01c1660a082015260c0810199838160a81c168b5260b01c5f0b60e0820152875260048301549086821690880152841c161515838601526005810154606086015201549082821660808501521c1660a082015293511615159081620030e9575b50156200053e57565b60ff9150511615155f620030e0565b60c01c81520188905f6200303b565b9091946001600160401b038560801c1681520193015f8562003032565b8192956001600160401b03868d1c168152019401908562003029565b8192956001600160401b0386168152019401908562003020565b86546001600160401b038082168652818b1c811696860196909652608081811c9096168a86015260c01c6060850152958201958795508c949093019260040162003004565b5050845f528188805f20855f915b858310620031c657505062002fe5935082010162002fd7565b8091929450548385880101520191018990858593620031ad565b60ff19168482015262002fe594151560051b840101915062002fd79050565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152606081018181106001600160401b03821117620025015760405251902090565b519081151582036200053e57565b5f80916040516001600160a01b036020820192168252602081526200327c8162002760565b51906108105afa6200328d62002cd7565b9015620032d6576020818051810103126200053e5760405190602082018281106001600160401b038211176200250157604052620032ce9060200162003249565b809152151590565b6040516313dd7ccd60e31b8152600490fd5b620032f79062000c4362002d0b565b906001600160a01b036001604051936200331185620026db565b805463ffffffff90818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156200053e57565b60405163a9059cbb60e01b602082019081526001600160a01b039384166024830152604480830195909552938152620033ea9390925f92839291906200339b6064876200277c565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051620033ce8162002760565b8181520152519082855af1620033e362002cd7565b91620051de565b805190811591821562003402575b5050156200053e57565b81925090602091810103126200053e57602062003420910162003249565b5f80620033f8565b51906001600160401b03821682036200053e57565b604080516001600160a01b0392909216602083019081526001600160401b039390931682820152808252915f918291906200347a6060826200277c565b51906108015afa6200348b62002cd7565b9015620034f5576060818051810103126200053e5781516001600160401b0392606082018481118382101762002501578152620034ee6060620034d16020860162003428565b94858552620034e284820162003428565b60208601520162003428565b9101521690565b8151639d2c8fcb60e01b8152600490fd5b6001600160401b03918216908216039190821162001a0e57565b949192909394805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20906001600160a01b039485835416958615620037ab57600284019788549860ff8a60481c166200379957600186015483608086510151168482160362003780576001600160401b039060a01c166001600160401b038b1660ff8c60401c165f146200371f575080926001600160401b03811682115f146200371657620035d79162003506565b995b620035e58b8d6200282b565b9b6001600160401b038d166001600160401b0385161162003704575069010000000000000000009069ff00000000000000000019161790556200362b8160019b62003506565b976001600160401b036020846200364b8d60e0895101515f0b9062003829565b50991695015116843b156200053e5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152925f908490606490829084905af1918215620019c5577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4393604093620036f2575b5060018187541696015416956001600160401b038351921682526020820152a4565b620036fd90620026f7565b5f620036d0565b5f9c508c9b5099505050505050505050565b50505f620035d7565b9092809b93806001600160401b038316105f1462003773576200374f91620037479162003506565b809c6200282b565b816001600160401b038216115f146200376b57505b91620035d9565b905062003764565b50506200374f5f62003747565b60405163358d72d160e01b815260048101879052602490fd5b60405163f7348a7960e01b8152600490fd5b604051631a40316d60e01b8152600490fd5b60ff16604d811162001a0e57600a0a90565b9190820391821162001a0e57565b8115620037e8570490565b634e487b7160e01b5f52601260045260245ffd5b90620f42409182810292818404149015171562001a0e57565b8181029291811591840414171562001a0e57565b9190805f0b80155f14620038475750506001600160401b0382169190565b5f81131562003877575062003863620038739160ff16620037bd565b6001600160401b03841662003815565b9190565b9050607f19811462001a0e5762003893905f0360ff16620037bd565b91620038aa836001600160401b038093166200287c565b5f19810190811162001a0e57620038c684620038ce92620037dd565b938462003815565b1690565b6200390a6200390392936001600160401b0393836001600160a01b03620038fc8780971662002847565b166200343d565b946200282b565b169116101590565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f205416155f14620039aa57825f5260205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f2054165f14620039aa57825f5260205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b9062003a5e6001600160a01b0360408401511662003257565b1562004618576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165f525f8051602062005bb783398151915260205260405f20906040519162003ab6836200270b565b60405162003ac48162002727565b6040518254815f62003ad68362002c80565b8083529260018116908115620045f75750600114620045b2575b62003afe925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b81600384011062004570579360069593819362003b67936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a08301526001600160a01b036060840151165f525f8051602062005bb783398151915260205260405f206040519062003c2e826200270b565b60405162003c3c8162002727565b6040518254815f62003c4e8362002c80565b80835292600181169081156200454f57506001146200450a575b62003c76925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b816003840110620044bd579360069593819362003cdf936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015262003d896001600160a01b03606086015116620032e8565b9184519062003dd662003dbb62003dac60a089015160808a01511515956200287c565b60e0885101515f0b9062004da2565b905060ff60c0885101511660ff60c087510151169162005165565b91156200448c5781925b5f8063ffffffff875116604051602081019182526020815262003e038162002760565b51906108085afa9062003e1562002cd7565b91156200447a576020828051810103126200053e576001600160401b0362003e44602062003e6d940162003428565b60208801519116919015620044665760ff60a062003e669251015116620037bd565b9062003815565b602085015115620044175763ffffffff62003e8e8160608801511662005026565b16620f4240818101811162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c01511691016200287c565b0416906080870151908115155f146200440e5750905b602085015115620043c5576305f5e1008110156200437e57505f5b62003f2462003f0b60a0890151620037fc565b62003f1d895160a08b0151906200287c565b906200287c565b5f1981019190821162001a0e5762003f1d62003f539262003f4c8a5160a08c0151906200287c565b90620037dd565b606482028281046064148315171562001a0e578111620042df57505062003f84855160e0865101515f0b9062004da2565b92909562003faf6001600160401b03602088015116856001600160401b0360a08a01511691620038d2565b156200423b5791608093917f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91860a0856001600160401b036001600160a01b036040819b990151168a606084015116908a8401511515916040519162004014836200270b565b82526020820152828816604082015282871660608201528a81019182528481015f815262004118602086019384515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b03602052856060600260405f206001600160a01b0388511673ffffffffffffffffffffffffffffffffffffffff19825416178155600181016001600160a01b0360208a0151168154907bffffffffffffffff000000000000000000000000000000000000000060408c015160a01b169163ffffffff60e01b161717905501950151168619855416178455511515839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151569ff00000000000000000082549160481b169069ff00000000000000000019161790555194818b60408501511697818d6060870151169a87875197015160405197885260208801521660408601521660608401521688820152a4015116620041ae83826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001662003353565b60e06001600160401b03602084015116925101515f0b90803b156200053e57604051639c45c34b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160401b03909316602484015260448301939093525f90810b6064830152909182908183816084810162002f38565b94505050905062002f56925060208201516001600160401b03604051921682527f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c60206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b9350949350505060208201516001600160a01b0360608401511691606481019081811162001a0e5760630190811162001a0e5760407fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e9162002f569660648351920482526020820152a36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166060820152620046c4565b6001600160401b036305f5e0ff1982011162001a0e57620043be620043b86001600160401b036305f5e100936305f5e0ff190116620037fc565b6200286a565b0462003ef8565b6305f5e100811115620043da57505f62003ef8565b6001600160401b03816305f5e100031162001a0e57620043be620043b86001600160401b036305f5e10093840316620037fc565b90509062003edd565b63ffffffff6200442d8160608801511662005026565b16620f4240808281031162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c0151169103620037cf565b5062003e6660ff60a08951015116620037bd565b604051635cffc5fb60e11b8152600490fd5b6127108181031162001a0e576001600160401b03612710620044b383820383861662003815565b0416919262003de0565b926001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019162003c95565b5090845f5260205f20905f915b8183106200453257505090602062003c769282010162003c68565b602091935080600191548385880101520191019091839262004517565b6020925062003c7694915060ff191682840152151560051b82010162003c68565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162003b1d565b5090845f5260205f20905f915b818310620045da57505090602062003afe9282010162003af0565b6020919350806001915483858801015201910190918392620045bf565b6020925062003afe94915060ff191682840152151560051b82010162003af0565b506080810151909190156200464c5760246001600160a01b03604084015116604051906320a2097d60e11b82526004820152fd5b9080602062002f569201517f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b03604085015116604051908152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b6060906060810151905f6001600160a01b0380931692835f526020945f8051602062005bb78339815191528652604091825f2096835162004705816200270b565b845193620047138562002727565b85518a5491905f81620047268562002c80565b918282526001958887821691825f1462004d6b57505060011462004d2a575b62004756925094939403826200277c565b8652808b019184885193898586948484549283815201935f52845f20965f915b83600384011062004cde575085620047b998549484841062004cc4575b84841062004ca7575b5083831062004c8a575b50501062004c7b5750905003826200277c565b82850152600289015495836001600160401b039788811688880152871c16606086015260038a0154946080958581168783015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528252600660048b01549a60ff8585019c8d8b82169052891c1615158885015260058101546060850152015490878216868401528760a0840192881c168252868901976200485e868a511662003257565b1562004c0d57899a9b5f9a98999a508c612710620048958b519b8b6200488b60a083019e8f51906200287c565b9101519062003815565b04988a5199808b1162004c04575b508d8a8062004b2e575b620048c2620048d191620048e493516200287c565b60e08a5101515f0b9062004da2565b91909782878088511692511691620038d2565b1562004ae65750908c9695949392918a620049f9575b509260e0896200498f94620049c59b9997947fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b99976200496e906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6200497b8d82546200287c565b9055511696511694510151900b9062004da2565b9190928d84620049ca575b5050505050880151965116965192519351938493846040919493926060820195825260208201520152565b0390a4565b620049ee94620049e89188620049e08562002847565b169062003353565b62002ea6565b5f8080808d6200499a565b919293949596505087807f00000000000000000000000000000000000000000000000000000000000000001691875101511690803b156200053e578b5163f3fef3a360e01b81526001600160a01b03929092166004830152602482018a90525f908290604490829084905af1801562004adc578c948f91998098956200498f957fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b9995620049c59d62004ac0575b509497995094509950939597999a9b620048fa565b60e0945062004acf90620026f7565b6200496e5f945062004aab565b8b513d5f823e3d90fd5b9a50505050955050505093957f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c955084915062004b238162004e31565b0151945191168152a3565b62004b45908a8301518c858c51015116906200503f565b1562004b53575b8a620048ad565b509192939495968881999a50602492508389510151168c51928380926370a0823160e01b82528d7f00000000000000000000000000000000000000000000000000000000000000001660048301525afa90811562004adc57918e918e9a999897969594935f9162004bc7575b509962004b4c565b92505098508681813d831162004bfc575b62004be481836200277c565b810103126200053e57518c988e91620048d162004bbf565b503d62004bd8565b99505f620048a3565b505050509296509450839293015115155f1462004c405750505191516320a2097d60e11b81529116600482015260249150fd5b62002f56948294827f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d9401519451169051908152a262004e31565b60c01c81520185905f6200303b565b9091946001600160401b038560801c1681520193015f85620047a6565b956001600160401b03868495981c16815201940190858e6200479c565b6001600160401b0386168752958101959282019262004793565b95509360049296509284919388546001600160401b039283821683528d8483831c169084015260809382851c169083015260c01c8782015201960191019386948c9296938a9562004776565b50508c5f528186805f20865f915b85831062004d5157505062004756935082010162004745565b809192945054838588010152019101879086859362004d38565b60ff1916848201526200475694151560051b8401019150620047459050565b81811062004d96575050565b5f815560010162004d8a565b9190805f0b9081155f1462004dc05750506001600160401b03821690565b5f82131562004e035762004dd8915060ff16620037bd565b918215620037e857620038ce62004dfb82856001600160401b03940690620037cf565b9384620037dd565b505f0380805f0b0362001a0e57620038ce62004e2a6001600160401b039260ff16620037bd565b8462003815565b805161271062004e5862004e4c60a08501938451906200287c565b60808501519062003815565b0481518181115f146200501d5750905b8190602084019182519162004e8f60608701956001600160a01b039485885116906200503f565b1562005014575b8162004f61575b620049c5837f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9462004ed1858a516200287c565b9662004eeb828a51169860408c0199848b51169062003353565b62004f29828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62004f368782546200287c565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b84518316927f00000000000000000000000000000000000000000000000000000000000000008116803b156200053e5760405163f3fef3a360e01b81526001600160a01b03959095166004860152602485018490525f908590604490829084905af1908115620019c5577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f94620049c59262005002575b5093505062004e9d565b6200500d90620026f7565b5f62004ff8565b5f915062004e96565b90509062004e68565b90606463ffffffff8093160291821691820362001a0e57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa918215620019c5575f92620050f2575b5080821094851595620050b2575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080620050ac565b9091506020813d60201162005123575b8162005111602093836200277c565b810103126200053e5751905f6200509e565b3d915062005102565b51906001600160a01b03821682036200053e57565b519060ff821682036200053e57565b9060ff8091169116039060ff821162001a0e57565b919060ff821660ff82168181145f1462005180575050505090565b929391921015620051b857620038ce9192620051b0620051aa6001600160401b0395869362005150565b620037bd565b911662003815565b620038ce91620051d6620051aa6001600160401b0395869362005150565b9116620037dd565b9015620051fb57815115620051f1575090565b3b156200053e5790565b5080519081156200053e57602001fdfe60a080604052346100455733608052610961908161004a8239608051818181608b015281816101ac0152818161038e015281816104a20152818161051b015261069c0152f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f915f3560e01c908163435354d3146104d757816390a0827b1461047b5781639c45c34b14610469578163a703334f1461033c578163de1a324a146100d457508063e94b77c1146100b35763eba61c0e1461006d575f80fd5b346100af57816003193601126100af57602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b82346100d1576100ce6100c5366105b5565b9291909161068d565b80f35b80fd5b91905034610338576101009136600319018381126103345760a01361033057815167ffffffffffffffff939060a081018581118282101761031d5784526101196105a2565b91828252602435918215158303610319576020810192835263ffffffff9360443585811681036102ce578783015260643585811681036102ce5760608301526084356001600160a01b03928382168203610315576080015260a4358881168091036102ce5760c435918983168093036102ce5760e435936fffffffffffffffffffffffffffffffff85168095036102ce577f000000000000000000000000000000000000000000000000000000000000000016330361031557866101dd9116610663565b9451151581156103055782156102f5578a6102e557885196602088019616865288870152606086015260808501525f60a0850152600260c085015260e084015260e08352820194828610908611176102d25761026891602491868652630100000160e01b6101208301526102598251809261012485019061079c565b81010383810186520184610641565b73333333333333333333333333333333333333333392833b156102ce576102a5935f928385518097819582946317938e1360e01b845283016107bd565b03925af19081156102c557506102b9575080f35b6102c391506105fd565b005b513d5f823e3d90fd5b5f80fd5b604183634e487b7160e01b5f525260245ffd5b88516376d4929560e11b81528890fd5b88516313c0a8df60e01b81528890fd5b885163017461b760e71b81528890fd5b8a80fd5b8780fd5b604184634e487b7160e01b5f525260245ffd5b8380fd5b8480fd5b8280fd5b919050346103385760603660031901126103385781359067ffffffffffffffff928383168093036102ce576024356001600160a01b03948582168092036102ce576044359081168091036102ce5786957f000000000000000000000000000000000000000000000000000000000000000016330361046557610259946103fc9260249286519260208401928352878401526060830152606082526103df82610625565b85519687926280000360e11b60208501525180928585019061079c565b73333333333333333333333333333333333333333392833b15610334576104399385928385518097819582946317938e1360e01b845283016107bd565b03925af190811561045c575061044c5750f35b610455906105fd565b6100d15780f35b513d84823e3d90fd5b8580fd5b83346100d1576100ce6100c5366105b5565b91905034610338573660031901126100af57356001600160a01b038082168092036102ce577f00000000000000000000000000000000000000000000000000000000000000001633036100af576100ce906024359033906107e9565b919050346102ce57806003193601126102ce576104f26105a2565b91602435926fffffffffffffffffffffffffffffffff84168094036102ce576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102ce5761054a90610663565b9282519063ffffffff602083019516855283820152828152606081019381851067ffffffffffffffff8611176102d25761026891602491868652630100000b60e01b608083015261025982518092608485019061079c565b6004359063ffffffff821682036102ce57565b60809060031901126102ce576004356001600160a01b03811681036102ce579060243567ffffffffffffffff811681036102ce579060443590606435805f0b81036102ce5790565b67ffffffffffffffff811161061157604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761061157604052565b90601f8019910116810190811067ffffffffffffffff82111761061157604052565b9061271063ffffffff8093160191821161067957565b634e487b7160e01b5f52601160045260245ffd5b9092916001600160a01b0392837f00000000000000000000000000000000000000000000000000000000000000001633036102ce57805f0b80155f1461071c575050915b826106dd575b50505050565b67ffffffffffffffff732000000000000000000000000000000000000000941684018094116106795780610713941691166107e9565b5f8080806106d7565b5f81131561075c57506107319060ff166108f1565b8015610748578106810390811161067957916106d1565b634e487b7160e01b5f52601260045260245ffd5b90505f9391930380805f0b03610679576107789060ff166108f1565b9280938181810204149015176106d157634e487b7160e01b5f52601160045260245ffd5b5f5b8381106107ad5750505f910152565b818101518382015260200161079e565b604091602082526107dd815180928160208601526020868601910161079c565b601f01601f1916010190565b9190604051906020938483019363a9059cbb60e01b85526001600160a01b03809316602485015260448401526044835261082283610625565b169160405190604082019067ffffffffffffffff9383831085841117610611577f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564875f9586956040528181520152519082865af13d156108e4573d918211610611576108ac926040519261089f86601f19601f8401160185610641565b83523d5f8685013e610902565b80519182159182156108c4575b50509050156102ce57565b8092508193810103126102ce57015180151581036102ce57805f806108b9565b6108ac9260609250610902565b60ff16604d811161067957600a0a90565b901561091c57815115610913575090565b3b156102ce5790565b5080519081156102ce57602001fdfea2646970667358221220c909e7dbe656d7c973709e34eb673837a89b1ae88012e9262063134290e6f21f64736f6c634300081800336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00a2646970667358221220d7009235facd2afacf7dbd784aa03c97dd6444be46f4a33aa486147317b81a7764736f6c6343000818003300000000000000000000000081d40f21f12a8f0e3252bccb954d722d4c464b640000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000002e76dc036a1eff1488ee5435ee66c6abf32674000000000000000000000000b88339cb7199b77e23db6e890353e22632ba630f0000000000000000000000005e7840e06faccb6d1c3b5f5e0d1d3d07f2829bba", - "nonce": "0x244", + "input": "0x610100346102b357601f61851338819003918201601f19168301916001600160401b0383118484101761029f5780849260a0946040528339810103126102b357610048816102b7565b610054602083016102b7565b91610061604082016102b7565b6100796080610072606085016102b7565b93016102b7565b60015f556040519091615ba08083016001600160401b0381118482101761029f57604092849261291384396001600160a01b039081168252861660208201520301905ff08015610294576001600160a01b03166080527f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef65f8181525f5160206184f35f395f51905f526020527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bace80549082905590915f5160206184b35f395f51905f528380a47f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f5f8181525f5160206184f35f395f51905f526020527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49b80549082905590915f5160206184b35f395f51905f528380a460a05260e0526001600160a01b0390811660c0527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0080546001600160a01b031916929091169190911790556107087fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0155610228336102cb565b506040516125be9081610355823960805181818161020e015261151d015260a0518181816106e30152818161169301526122f8015260c0518181816107cb01528181610b6c0152610e80015260e05181818161095c01528181610cbf01528181610d790152610f5c0152f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b51906001600160a01b03821682036102b357565b6001600160a01b0381165f9081525f5160206184d35f395f51905f52602052604090205460ff1661034f576001600160a01b03165f8181525f5160206184d35f395f51905f5260205260408120805460ff191660011790553391907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b505f9056fe60806040526004361015610030575b361561002e573461002a5761002236611036565b602081519101f35b5f80fd5b005b5f3560e01c806301ffc9a71461018f57806309cfd6751461018a5780631b1062b81461018557806321081d3c14610180578063238ac9331461017b578063248a9ca3146101765780632561efb214610171578063277e661d1461016c5780632f2ff15d1461016757806336568abe14610162578063490e662f1461015d5780634b3b029b146101585780634f7d9d2e14610153578063657cad8a1461014e5780636c19e783146101495780638c73eb041461014457806391d148541461013f57806393de31191461013a578063a217fddf14610135578063c55dae6314610130578063d547741f1461012b5763feb617240361000e576109cd565b610980565b61093d565b610923565b61084e565b6107ef565b6107ac565b610707565b6106c4565b610688565b61064e565b610614565b6105cc565b61057a565b610524565b6104d8565b610499565b610454565b61041a565b610347565b6101ef565b3461002a57602036600319011261002a5760043563ffffffff60e01b811680910361002a57602090637965db0b60e01b81149081156101d4575b506040519015158152f35b6301ffc9a760e01b1490505f6101c9565b5f91031261002a57565b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b634e487b7160e01b5f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761026257604052565b610232565b90601f8019910116810190811067ffffffffffffffff82111761026257604052565b6040519061029860c083610267565b565b60405190610298608083610267565b6040519061029861020083610267565b60405190610298606083610267565b60405190610298604083610267565b67ffffffffffffffff811161026257601f01601f191660200190565b9291926102ff826102d7565b9161030d6040519384610267565b82948184528183011161002a578281602093845f960137010152565b9080601f8301121561002a57816020610344933591016102f3565b90565b3461002a57606036600319011261002a5760043567ffffffffffffffff811161002a57610378903690600401610329565b60243567ffffffffffffffff811161002a57610398903690600401610329565b906044359167ffffffffffffffff831161002a576103bd6103ef933690600401610329565b916103c6611054565b60017fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10055610b3d565b5f7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1005560015f555f80f35b3461002a575f36600319011261002a5760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b3461002a575f36600319011261002a5760206001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f005416604051908152f35b3461002a57602036600319011261002a5760206104d06004355f525f5160206125695f395f51905f52602052600160405f20015490565b604051908152f35b3461002a57602036600319011261002a576104f1611054565b6104f96119cd565b6004357fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f015560015f55005b3461002a57602036600319011261002a5760043567ffffffffffffffff811161002a5761055f61055a6020923690600401610329565b611a70565b6040519015158152f35b6001600160a01b0381160361002a57565b3461002a57604036600319011261002a5761002e60243560043561059d82610569565b6105c76105c2825f525f5160206125695f395f51905f52602052600160405f20015490565b611a1c565b611b24565b3461002a57604036600319011261002a576004356024356105ec81610569565b336001600160a01b038216036106055761002e91611bd6565b63334bd91960e11b5f5260045ffd5b3461002a575f36600319011261002a5760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b3461002a575f36600319011261002a5760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b3461002a575f36600319011261002a5760207fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0154604051908152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57602036600319011261002a576001600160a01b0360043561072c81610569565b610734611054565b61073c6119cd565b167fffffffffffffffffffffffff00000000000000000000000000000000000000007fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f005416177fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f005560015f555f80f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57604036600319011261002a57602060ff61084260243560043561081682610569565b5f525f5160206125695f395f51905f52845260405f20906001600160a01b03165f5260205260405f2090565b54166040519015158152f35b3461002a57604036600319011261002a5760043567ffffffffffffffff811161002a5761087f903690600401610329565b60243567ffffffffffffffff811161002a5761089f903690600401610329565b6108a7611054565b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd602052604090205460ff16156108ec576108e691610e51565b60015f55005b63e2517d3f60e01b5f52336004527f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef660245260445ffd5b3461002a575f36600319011261002a5760206040515f8152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57604036600319011261002a5761002e6024356004356109a382610569565b6109c86105c2825f525f5160206125695f395f51905f52602052600160405f20015490565b611bd6565b3461002a57602036600319011261002a576004355f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f02602052602060ff60405f2054166040519015158152f35b9081602091031261002a5751801515810361002a5790565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9091610a6e61034493604084526040840190610a33565b916020818403910152610a33565b6040513d5f823e3d90fd5b906020610344928181520190610a33565b634e487b7160e01b5f52601160045260245ffd5b5f19810191908211610aba57565b610a98565b91908203918211610aba57565b634e487b7160e01b5f52602160045260245ffd5b60c09093929193610b398160e081019660a0809180518452602081015160208501526001600160a01b0360408201511660408501526001600160a01b036060820151166060850152608081015160808501520151910152565b0152565b906020610b5f9160405180938192630afd9fa560e31b83528660048401610a57565b03815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af1908115610e4c575f91610e2d575b5015610e1e5760405163277e661d60e01b815260208180610bc08560048301610a87565b0381305afa5f9181610ded575b50610bd757505050565b15610de957610be8610bf191611177565b919092836112a1565b80610d9e575b610c05826060850151610abf565b91610100840151610c74610c1d6101808701516114c0565b8415610d7457610c64610c346101a08901516114c0565b915b8615610d6d57610140890151945b610c4c610289565b98895260208901526001600160a01b03166040880152565b6001600160a01b03166060860152565b608084015260a083015280610d39575b15610cf757610cf2600160ff84610ca96101c06101e061029898015192015160ff1690565b90610cb261029a565b9586526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166020870152604086015216146060830152565b6115ac565b610d31610160610d3693015191610d2360405193849263073ffe1360e31b602085015260248401610ae0565b03601f198101835282610267565b611512565b50565b506101c08201600160ff610d4e835160ff1690565b1614908115610d5e575b50610c84565b5160ff1660021490505f610d58565b5f94610c44565b610c647f000000000000000000000000000000000000000000000000000000000000000091610c36565b610de4610dd76101008501515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b805460ff19166001179055565b610bf7565b5050565b610e1091925060203d602011610e17575b610e088183610267565b810190610a1b565b905f610bcd565b503d610dfe565b6368c2a52360e11b5f5260045ffd5b610e46915060203d602011610e1757610e088183610267565b5f610b9c565b610a7c565b906020610e739160405180938192630afd9fa560e31b83528660048401610a57565b03815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af1908115610e4c575f91611017575b5015610d365760405163277e661d60e01b815260208180610ed48560048301610a87565b0381305afa5f9181610ff6575b50610eea575050565b15610d3657610f197f1f46a1a2dc661ca8c3c13ebcec8d8dbefdf24d5f9c9b5fe909f1b8036c719e7e91611177565b610ff1610100830191610f57610dd784515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b610fc17f000000000000000000000000000000000000000000000000000000000000000091610fba610180870195610fb26060610f9489516114c0565b990198610fa2858b51610abf565b906001600160a01b038816611c80565b5195516114c0565b9551610abf565b9060405194859485909493926001600160a01b039081606094608085019885521660208401521660408201520152565b0390a1565b61101091925060203d602011610e1757610e088183610267565b905f610ee1565b611030915060203d602011610e1757610e088183610267565b5f610eb0565b610d3161104d91611045611054565b36905f6102f3565b9060015f55565b60025f541461002a5760025f55565b61106b6102a9565b905f82525f60208301525f60408301525f60608301525f60808301525f60a08301525f60c08301525f60e08301525f6101008301525f6101208301525f6101408301525f6101608301525f6101808301525f6101a08301525f6101c083015260606101e0830152565b81601f8201121561002a578051906110eb826102d7565b926110f96040519485610267565b8284526020838301011161002a57815f9260208093018386015e8301015290565b9190916101008184031261002a5780519260208201519260408301519260608101519260808201519260a08301519260c081015160ff8116810361002a579260e082015167ffffffffffffffff811161002a5761034492016110d4565b906111e2611183611063565b9261119a61119082611cfd565b63ffffffff168552565b6111b36111a682611d1c565b63ffffffff166020860152565b6111bc81611d3c565b60a08501526111da6111cd82611d2c565b63ffffffff1660e0860152565b805190611f26565b906111ec82611d4c565b60408401526111fa82611d5c565b606084015261120882611d6c565b608084015261121682611d7c565b60c084015261126061124561123661122d85611d8c565b94805190611f80565b6020808251830101910161111a565b6101e08c999394959697989901526101c08b019060ff169052565b6101a0890152610180880152610160870152610140860152610120850152610100840152565b9060018201809211610aba57565b91908201809211610aba57565b61142e6001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f005416926112de835163ffffffff1690565b602084015163ffffffff16611369604086015191610d236060880151608089015160a08a015160c08b01519161131b60e08d015163ffffffff1690565b93604051988997602089019b8c96929363ffffffff95919998948660e09894816101008c019d168b521660208a015260408901526060880152608087015260a086015260c085015216910152565b51902093610100840194610d2361142587516101208801976114068951610d2361014084015193610160810151906101808101516101a0820151906101e06113b66101c085015160ff1690565b9301516020815191012093604051988997602089019b8c9490989796929360ff9460e0979361010088019b8852602088015260408701526060860152608085015260a08401521660c08201520152565b5190206040805160208101958652908101919091529182906060820190565b51902090611fad565b9182611473575b508161143f575090565b61146d9150517fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f015490611294565b42111590565b6114b99192506114ae6114b591515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b5460ff1690565b1590565b905f611435565b8060a01c6114d4576001600160a01b031690565b6379ec0ed760e11b5f5260045ffd5b3d1561150d573d906114f4826102d7565b916115026040519384610267565b82523d5f602084013e565b606090565b5f80916020815191017f00000000000000000000000000000000000000000000000000000000000000005af46115466114e3565b901561154f5790565b602081519101fd5b6102989092919260c081019360a0809180518452602081015160208501526001600160a01b0360408201511660408501526001600160a01b036060820151166060850152608081015160808501520151910152565b6115b4611dac565b506115cc604082015160208082518301019101611e05565b90602081016115f46115e86115e883516001600160a01b031690565b6001600160a01b031690565b6040516370a0823160e01b815230600482015290602090829060249082905afa908115610e4c575f916119ae575b5061163e6115e86115e86060865101516001600160a01b031690565b6040516370a0823160e01b81523060048201529490602090869060249082905afa948515610e4c575f9561198d575b506001600160a01b036116d561168d6115e886516001600160a01b031690565b926116bd7f000000000000000000000000000000000000000000000000000000000000000094858951519161207e565b8651606001513091906001600160a01b03169061227c565b91166116e884516001600160a01b031690565b9185515191803b1561002a5761171a935f809460405196879586948593633a5be8cb60e01b8552309160048601611ef8565b03925af18015610e4c57611973575b506117416115e86115e884516001600160a01b031690565b6040516370a0823160e01b81523060048201529190602090839060249082905afa918215610e4c575f92611952575b50036118b257610d3692506117a261178f82516001600160a01b031690565b6060845101906001600160a01b03169052565b815151905b6117ba82845160a08151910151906123ee565b60a084510152818351528251907fb88fc27be67e678ffb77faf8f8bb00d39b66b4845e4f7ec1e623b0f15abd52136001600160a01b03611806602085015193516001600160a01b031690565b9461183f611820606087519701516001600160a01b031690565b9183604051948594169816968360209093929193604081019481520152565b0390a48051606082015115611874575051604051632132ff4360e11b602082015290610d31908290610d239060248301611557565b6040517fff3eae000000000000000000000000000000000000000000000000000000000060208201529150610d31908290610d239060248301611557565b6118ce6115e86115e860608551016001600160a01b0390511690565b6040516370a0823160e01b81523060048201529390602090859060249082905afa908115610e4c57610d36945f92611921575b508082106119185761191291610abf565b906117a7565b50505f906117a7565b61194491925060203d60201161194b575b61193c8183610267565b810190611ee9565b905f611901565b503d611932565b61196c91925060203d60201161194b5761193c8183610267565b905f611770565b806119815f61198793610267565b806101e5565b5f611729565b6119a791955060203d60201161194b5761193c8183610267565b935f61166d565b6119c7915060203d60201161194b5761193c8183610267565b5f611622565b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff1615611a0557565b63e2517d3f60e01b5f52336004525f60245260445ffd5b805f525f5160206125695f395f51905f5260205260ff611a503360405f20906001600160a01b03165f5260205260405f2090565b541615611a5a5750565b63e2517d3f60e01b5f523360045260245260445ffd5b610298815110611b1f57611a8b611a8682611d9c565b6114c0565b6001600160a01b0330911603611b1f578051806094108160941802811891828203918211610aba57611b0c9282611236926020611aca611afc966102d7565b93611ad86040519586610267565b838552611ae4846102d7565b8583019390601f19013685370101905e805190611f80565b50509594509250505060a01c1590565b9081611b16575090565b60a01c15905090565b505f90565b805f525f5160206125695f395f51905f5260205260ff611b588360405f20906001600160a01b03165f5260205260405f2090565b5416611bd057805f525f5160206125695f395f51905f52602052611b908260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916600117905533916001600160a01b0316907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f5160206125695f395f51905f5260205260ff611c0a8360405f20906001600160a01b03165f5260205260405f2090565b541615611bd057805f525f5160206125695f395f51905f52602052611c438260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916905533916001600160a01b0316907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b916001600160a01b036040519263a9059cbb60e01b5f521660045260245260205f60448180865af160015f5114811615611cde575b60409190915215611cc35750565b635274afe760e01b5f526001600160a01b031660045260245ffd5b6001811516611cf4573d15833b15151616611cb5565b503d5f823e3d90fd5b6008815110611d0d576008015190565b632d0483c560e21b5f5260045ffd5b600c815110611d0d57600c015190565b6090815110611d0d576090015190565b608c815110611d0d57608c015190565b6044815110611d0d576044015190565b6064815110611d0d576064015190565b6024815110611d0d576024015190565b60a4815110611d0d5760a4015190565b60c4815110611d0d5760c4015190565b60d8815110611d0d5760d8015190565b6040519060c0820182811067ffffffffffffffff821117610262576040525f60a0838281528260208201528260408201528260608201528260808201520152565b67ffffffffffffffff81116102625760051b60200190565b60208183031261002a5780519067ffffffffffffffff821161002a57019080601f8301121561002a57815191611e3a83611ded565b92611e486040519485610267565b80845260208085019160051b8301019183831161002a5760208101915b838310611e7457505050505090565b825167ffffffffffffffff811161002a578201906040828703601f19011261002a5760405190611ea382610246565b6020830151611eb181610569565b825260408301519167ffffffffffffffff831161002a57611eda886020809695819601016110d4565b83820152815201920191611e65565b9081602091031261002a575190565b9061034494936080936001600160a01b03809316845260208401521660408201528160608201520190610a33565b908151908180821091180218806094108160941802811891828203918211610aba576020611f53836102d7565b93611f616040519586610267565b838552611f6d846102d7565b8583019390601f19013685370101905e90565b9081519081808210911802188060e4108160e41802811891828203918211610aba576020611f53836102d7565b90611fb8838261248d565b600581959295101561207957159384612063575b508315611fda575b50505090565b5f935090610d236120128594936040519283916020830195630b135d3f60e11b87526024840152604060448401526064830190610a33565b51915afa61201e6114e3565b81612055575b81612033575b505f8080611fd4565b905060208180518101031261002a5760200151630b135d3f60e11b145f61202a565b905060208151101590612024565b6001600160a01b0384811691161493505f611fcc565b610acc565b905f806001600160a01b0361210b95946040519582602088019663a9059cbb60e01b88521660248801526044870152604486526120bc606487610267565b1692604051946120cd604087610267565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020870152519082855af16121056114e3565b9161253d565b8051908115918215612121575b50501561002a57565b6121349250602080918301019101610a1b565b5f80612118565b9061214582611ded565b6121526040519182610267565b8281528092612163601f1991611ded565b015f5b81811061217257505050565b60405190606082019180831067ffffffffffffffff841117610262576020926040525f81526060838201525f604082015282828601015201612166565b80518210156121c35760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b6020815260608101918051926040602084015283518091526080830190602060808260051b8601019501915f905b82821061222b575050505090604060206103449301519101906001600160a01b03169052565b90919295602080600192607f198982030185528951906001600160a01b038251168152604080612268858501516060878601526060850190610a33565b930151910152980192019201909291612205565b9291835161229161228c82611286565b61213b565b925f5b828110612362575060405163ef8738d360e01b60208201526001600160a01b0391821660248201529083166044820152939450610d2393610344939261235392909161233791906122e681606481018a565b6122ee6102b9565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681529060208201525f604082015261233082876121af565b52846121af565b506123406102c8565b9283526001600160a01b03166020830152565b604051928391602083016121d7565b806123806123726001938a6121af565b51516001600160a01b031690565b602061238c838b6121af565b5101516123a961239a6102b9565b6001600160a01b039093168352565b60208201525f60408201526123be82886121af565b526123c981876121af565b5001612294565b81156123da570490565b634e487b7160e01b5f52601260045260245ffd5b818101809111610aba57670de0b6b3a7640000820291808304670de0b6b3a76400001490151715610aba57808201809211610aba575f198201918211610aba57612437916123d0565b670de0b6b3a76400000390670de0b6b3a76400008211610aba57670de0b6b3a7640000808202908282041482151715610aba578261248361247e6103449561248894611294565b610aac565b6123d0565b610abf565b9060418151145f146124b9576124b591602082015190606060408401519301515f1a906124c2565b9091565b50505f90600290565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411612532576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15610e4c575f516001600160a01b0381161561252a57905f90565b505f90600190565b505050505f90600390565b9192501561255957815115612550575090565b3b1561002a5790565b50805190811561002a57602001fdfe02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800a2646970667358221220a6c072dd21a55a10b5685da20ea8a228aeae229a429258b65acadb2705eaa3b864736f6c634300081e003360c0346100d157601f615ba038819003918201601f19168301916001600160401b038311848410176100d55780849260409485528339810103126100d157610052602061004b836100e9565b92016100e9565b6001600160a01b0390911660805260a052604051615aa290816100fe8239608051818181610842015281816109820152818161143a015281816116c7015281816127bb015281816145ca015281816146ec01528181614a5c0152614c00015260a051818181610610015281816107740152818161191101526136c80152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036100d15756fe60806040526004361015610011575f80fd5b5f5f3560e01c806246912e146123be57806301ffc9a714612368578063037a06a41461211c57806304c73f60146120fe578063057f0370146120445780631f74a0b514611fd257806321081d3c14611f97578063248a9ca314611f575780632e748b2114611dfb5780632f2ff15d14611db0578063319adf9f146119e857806336568abe146119a357806337710e201461194e57806339fff098146118b75780633b1c6a01146115bc5780633cf3a025146115875780634265fe861461153c578063490e662f146115015780634b3b029b146114c6578063502a82e214611409578063521c98ba14610cd857806369b97ac714610cba57806379c7b60b14610c6557806379c7f28914610b6657806390a0827b14610b2d57806391d1485414610ad657806396cc2cfb14610882578063a217fddf14610866578063a4b672b614610822578063af5de6f914610798578063c55dae6314610754578063ccbedaec146104e9578063d06e28ed146103fc578063d547741f146103a8578063e38b73a91461038c578063ea0aaf241461032e578063eb84e7f21461021a5763ff3eae00146101bb575f80fd5b346102175760c0366003190112610217576101d536612562565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1005415610208576102059061492d565b80f35b63cd6d8f7d60e01b8252600482fd5b80fd5b50346102175760203660031901126102175760408160c09260a0835161023f8161250a565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051906102928261250a565b6001600160a01b03815416918281526001600160401b0360018301549281600260208501926001600160a01b038716845282604087019760a01c1687520154946001600160a01b036060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b503461021757602036600319011261021757602061038361034d612421565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b5034610217578060031936011261021757602060405160068152f35b5034610217576040366003190112610217576103f86004356103c8612437565b906103f36103ee825f525f516020615a4d5f395f51905f52602052600160405f20015490565b612be7565b613628565b5080f35b503461021757604036600319011261021757610416612421565b602435906fffffffffffffffffffffffffffffffff82168092036104da5761043c612b78565b8261044682613027565b63ffffffff6001600160a01b03608083015116915116813b156104da57829160448392604051948593849263435354d360e01b845260048401528960248401525af180156104de576104c5575b50506001600160a01b03167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b816104cf91612541565b6104da57825f610493565b8280fd5b6040513d84823e3d90fd5b50346102175760603660031901126102175780610504612421565b61050c61244d565b610514612463565b9061051d612aba565b610557836001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156106b2574361059a846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0360016105df856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b015416926001600160401b0382166106b7575b50506001600160401b038116610606575050f35b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001683525f516020615a2d5f395f51905f526020526001600160401b0360046040852001541690823b156106b25760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156104de576106a15750f35b816106ab91612541565b6102175780f35b505050fd5b6001600160a01b031684525f516020615a2d5f395f51905f526020526001600160401b03600460408620015416833b156107505760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152838160648183875af1908115610745578491156105f2578161073691612541565b61074157825f6105f2565b5050fd5b6040513d86823e3d90fd5b8480fd5b503461021757806003193601126102175760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346102175760203660031901126102175760206001600160a01b036107c46107bf612421565b612f58565b610b046040516107d685830182612541565b81815284810191614f298339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152610816607582612541565b51902016604051908152f35b503461021757806003193601126102175760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5034610217578060031936011261021757602090604051908152f35b50346102175760403660031901126102175761089c612421565b906108a5612b78565b6108ae82612d18565b6108b783613027565b926108cb60e083510151840b6024356148ac565b9460208401906108f46001600160401b03835116886001600160401b0360a08901511691613553565b15610aad57859650610936846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b610941848254612623565b90556001600160a01b038416807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051878152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b15610aa95760405163f3fef3a360e01b81526001600160a01b0383166004820152602481018690529088908290604490829084905af18015610a9e5785918991610a81575b50506001600160401b0391610a116001600160a01b0392608060e0960192848451169061308b565b511692511694510151850b93813b15610a7d57604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156104de576106a15750f35b8580fd5b81925090610a8e91612541565b610a9a5783875f6109e9565b8680fd5b6040513d8a823e3d90fd5b8780fd5b6377e88bc960e11b86526001600160a01b0384166004526001600160401b038716602452604486fd5b5034610217576040366003190112610217576001600160a01b036040610afa612437565b9260043581525f516020615a4d5f395f51905f526020522091165f52602052602060ff60405f2054166040519015158152f35b503461021757604036600319011261021757610205610b4a612421565b610b52612aba565b602435906001600160a01b0333911661308b565b50346102175760203660031901126102175760a0610bc4610b85612421565b610b8d612a90565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b03600160405192610bdb846124ef565b63ffffffff8154818116865260ff8160201c1615156020870152818160281c16604087015260481c1660608501520154166080820152610c6360405180926001600160a01b036080809263ffffffff815116855260208101511515602086015263ffffffff604082015116604086015263ffffffff6060820151166060860152015116910152565bf35b5034610217576020366003190112610217576020610383610c84612421565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b50346102175780600319360112610217576020604051620f42408152f35b50346102175760a036600319011261021757610cf2612421565b610cfa612479565b90610d0361248c565b6064356001600160401b038116810361075057608435916001600160401b0383168303610a7d57610d32612b29565b610d3a612999565b508580604051602081019063ffffffff8916825260208152610d5d604082612541565b519061080c5afa610d6c612a61565b90156113fa578051810160208101916020818303126113d6576020810151906001600160401b0382116113f65701906101009082900312610aa95760405191610db483612525565b60208201516001600160401b0381116113f65760209083010181601f820112156113f65789815191610de583612a46565b92610df36040519485612541565b80845284602082840101116104da578060208093018386015e83010152835260408201516001600160401b0381116113f6576020908301019080601f830112156113f6578151916001600160401b0383116113c2578260051b9060405193610e5e6020840186612541565b84526020808501928201019283116113f257602001905b8282106113da57505050602083015261010090610e94606082016130cc565b6040840152610ea560808201614e68565b6060840152610eb660a08201614e68565b6080840152610ec760c08201614e7c565b60a0840152610ed860e08201614e7c565b60c0840152015180880b8082036113d657610ef89160e0840152846134b2565b5060405191610f068361250a565b825263ffffffff87166020830152821515604083015260608201526001600160401b03831660808201526001600160401b03841660a08201526001600160a01b03851687525f516020615a2d5f395f51905f5260205260408720815180518051906001600160401b0382116113c25781908b610f828654612a0e565b601f8111611387575b5050602090601f8311600114611325578c9261131a575b50508160011b915f199060031b1c19161782555b6001820160208201518051906001600160401b03821161130657680100000000000000008211611306576020908c84548486558085106112ba575b505001918b5260208b20908b5b8160021c811061127557506003198116810380611219575b505050506001600160a01b03947f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd8224435397946001600160401b039460a06006868896600260809d9901888060408401511616891982541617815560608201517fffffffff0000000000000000000000000000000000000000ffffffffffffffff7bffffffffffffffffffffffffffffffffffffffff000000000000000083549260401b16911617905560038201908c808f83015116166001600160a01b0319835416178255848101519082547fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff000000000000000000000000000000000000000000001694891b16911617171790556111a76004820188806020880151161689198254161781556040860151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b60608401516005820155019185808c8301511616861984541617835501516fffffffffffffffff000000000000000082549160401b16906fffffffffffffffff0000000000000000191617905563ffffffff6040519a168a52151560208a01521660408801521660608601521692a280f35b928c938d5b8181106112375750505060021c015584848460a0611016565b909194602061126b6001926001600160401b03895116908560031b6001600160401b03809160031b9316831b921b19161790565b960192910161121e565b8c8d5b6004811061128d575083820155600101610ffe565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501611278565b8382876112e6945220600380870160021c820192601888831b16806112ed575b500160021c0190614896565b8c5f610ff1565b5f198501908154905f19908a03851b1c1690555f6112da565b634e487b7160e01b8c52604160045260248cfd5b015190505f80610fa2565b858d52818d209250601f1984168d5b81811061136f5750908460019594939210611357575b505050811b018255610fb6565b01515f1960f88460031b161c191690555f808061134a565b92936020600181928786015181550195019301611334565b602082886113b1945220601f850160051c810191602086106113b8575b601f0160051c0190614896565b8b5f610f8b565b90915081906113a4565b634e487b7160e01b8b52604160045260248bfd5b8880fd5b602080916113e7846130cc565b815201910190610e75565b8b80fd5b8980fd5b639b0c335d60e01b8752600487fd5b503461021757604036600319011261021757611423612421565b60243561142e612aba565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156114c25760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156104de576114ad575b505061020591339061308b565b816114b791612541565b6104da57825f6114a0565b5080fd5b503461021757806003193601126102175760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b503461021757806003193601126102175760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b50346102175760c03660031901126102175761155736612562565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541561020857610205906142b4565b5034610217576020366003190112610217576115a1612aba565b80808080600435335af16115b3612a61565b50156102175780f35b5034610217576060366003190112610217576115d6612437565b604435906001600160a01b0382169182810361186e576115f4612b78565b6115fd81612d18565b61160683612fa0565b610750576040810151156107505760016001600160401b03608083015116016001600160401b0381116118a35790859161164760e083510151840b826134b2565b509361167160208401926001600160401b03845116906001600160401b0360a08701511691613553565b1561186e576116b0816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b6116bb868254612623565b90556001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b156107505760405163f3fef3a360e01b81526001600160a01b0389166004820152602481018790529085908290604490829084905af1908115611898578591611883575b50506001600160401b039261174d60e08585511692510151860b876148ac565b509081611872575b5050505116926117bf60246040516001600160a01b036020820194169687855260408201526001606082015260608152611790608082612541565b6040519384916280000360e11b60208401525180918484015e810185838201520301601f198101835282612541565b7333333333333333333333333333333333333333333b156114c2578161180191604051809381926317938e1360e01b83526020600484015260248301906124cb565b0381837333333333333333333333333333333333333333335af180156104de57611859575b50506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b8161186391612541565b61186e57835f611826565b8380fd5b61187b92614cd4565b5f8080611755565b8161188d91612541565b61186e57835f61172d565b6040513d87823e3d90fd5b634e487b7160e01b86526011600452602486fd5b50346102175760e0366003190112610217576118d236612562565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1005415610208576001600160a01b036060820151166001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145f1461194157610205906142b4565b6102059060c435906136c5565b503461021757602036600319011261021757602061038361196d612421565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5034610217576040366003190112610217576119bd612437565b336001600160a01b038216036119d9576103f890600435613628565b63334bd91960e11b8252600482fd5b5034610217576020366003190112610217576001600160a01b03611a0a612421565b611a126129d7565b501681525f516020615a2d5f395f51905f52602052604081209060405190611a398261250a565b604051611a4581612525565b60405184548184611a5583612a0e565b8083529260018116908115611d915750600114611d50575b611a7992500382612541565b815260018401604051808260208294549384815201908652602086209286905b806003830110611cfe57611acf945491818110611ce4575b818110611cc7575b818110611caa575b10611c9c575b500382612541565b60208201526001600160a01b0360028501546001600160401b038116604084015260401c16606082015260038401546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c820b60e0820152825260048301549260208301906001600160401b038516825260ff604085019560401c161515855260066005820154916060860192835201549460808501926001600160401b03871684526001600160401b0360a087019760401c1687526040519560208752519460c06020880152611bb6865161010060e08a01526101e08901906124cb565b60208088015189830360df19016101008b015280518084529282019892910190835b818110611c7d57505050926001600160401b03809693899a969360e0878c610120866040819c0151169101528c6101406001600160a01b036060840151169101528c6101606001600160a01b036080840151169101528c61018060ff60a0840151169101528c6101a060ff60c0840151169101520151900b6101c08b0152511660408901525115156060880152516080870152511660a0850152511660c08301520390f35b82516001600160401b03168a526020998a019990920191600101611bd8565b60c01c81526020015f611ac7565b9260206001916001600160401b038560801c168152019301611ac1565b9260206001916001600160401b038560401c168152019301611ab9565b9260206001916001600160401b0385168152019301611ab1565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c6060820152019401920184929391611a99565b5090868552602085209085915b818310611d75575050906020611a7992820101611a6d565b6020919350806001915483858801015201910190918392611d5d565b60209250611a7994915060ff191682840152151560051b820101611a6d565b5034610217576040366003190112610217576103f8600435611dd0612437565b90611df66103ee825f525f516020615a4d5f395f51905f52602052600160405f20015490565b613581565b503461021757608036600319011261021757611e15612421565b611e1d61244d565b611e25612463565b90606435926fffffffffffffffffffffffffffffffff841680940361075057611e4c612b78565b84611e5682613027565b926001600160a01b0360808501511693843b156104da57611ee06101048492836001600160401b03806040519788968795636f0d192560e11b875260048701906001600160a01b036080809263ffffffff815116855260208101511515602086015263ffffffff604082015116604086015263ffffffff6060820151166060860152015116910152565b169a8b60a485015216988960c48401528b60e48401525af180156104de57611f42575b50506001600160a01b036040917f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c93835195865260208601521692a380f35b81611f4c91612541565b61075057845f611f03565b5034610217576020366003190112610217576020611f8f6004355f525f516020615a4d5f395f51905f52602052600160405f20015490565b604051908152f35b503461021757806003193601126102175760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503461021757606036600319011261021757611fec612421565b6024356001600160401b0381116104da5761200b90369060040161249b565b9092604435906001600160401b038211610217576020611f8f868686612034366004890161249b565b93909261203f612b78565b612630565b50346120fa5760403660031901126120fa5761205e612421565b9060243561206a612aba565b6001600160a01b03608061207d85613027565b015116803b156120fa576040516390a0827b60e01b81526001600160a01b038516600482015260248101839052905f908290604490829084905af180156120ef576120d9575b5061020591926001600160a01b0333911661308b565b61020592505f6120e891612541565b5f916120c3565b6040513d5f823e3d90fd5b5f80fd5b346120fa575f3660031901126120fa5760206040516305f5e1008152f35b346120fa5760a03660031901126120fa57612135612421565b61213d612479565b61214561248c565b9160643563ffffffff81168091036120fa576084359163ffffffff83168093036120fa5761217281612d18565b5061217b612b29565b6001600160a01b0360016121bf836001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b015416908115612317575b63ffffffff604051956121dc876124ef565b16948581526001600160a01b036001816020840199151595868b52604085019a888c52606086018a8152836080880193169c8d845263ffffffff6122508a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b985116926cffffffff00000000000000000068ffffffff000000000064ff000000008b549351151560201b16935160281b16935160481b16936cffffffff000000000000000000199168ffffffffffffffffff1916171617171785555116920191166001600160a01b03198254161790556122ca86612fa0565b156120fa577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9936080936001600160a01b03936040519788526020880152604087015260608601521692a3005b905061232281612f58565b604051610b048082018281106001600160401b03821117612354578291614f29833903905ff580156120ef57906121ca565b634e487b7160e01b5f52604160045260245ffd5b346120fa5760203660031901126120fa5760043563ffffffff60e01b81168091036120fa57602090637965db0b60e01b81149081156123ad575b506040519015158152f35b6301ffc9a760e01b149050826123a2565b346120fa5760403660031901126120fa5761241f6123da612421565b6001600160a01b036123ea61244d565b916123f3612aba565b165f525f516020615a2d5f395f51905f526020526001600160401b03600460405f200154163390612c34565b005b600435906001600160a01b03821682036120fa57565b602435906001600160a01b03821682036120fa57565b602435906001600160401b03821682036120fa57565b604435906001600160401b03821682036120fa57565b6024359063ffffffff821682036120fa57565b6044359081151582036120fa57565b9181601f840112156120fa578235916001600160401b0383116120fa576020808501948460051b0101116120fa57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60a081019081106001600160401b0382111761235457604052565b60c081019081106001600160401b0382111761235457604052565b61010081019081106001600160401b0382111761235457604052565b90601f801991011681019081106001600160401b0382111761235457604052565b60c09060031901126120fa576040519061257b8261250a565b81600435815260243560208201526044356001600160a01b03811681036120fa5760408201526064356001600160a01b03811681036120fa576060820152608435608082015260a060a435910152565b91908110156125db5760051b0190565b634e487b7160e01b5f52603260045260245ffd5b906001600160401b03809116911601906001600160401b03821161260f57565b634e487b7160e01b5f52601160045260245ffd5b9190820180921161260f57565b9194935f935f968281036120fa57612678856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156120fa57929061268b85612d18565b91608061269787613027565b01916001600160a01b03835116946126be60208601966001600160401b03885116906130e0565b965f975b818d1061292e575b50508a1592506129239150505743612712866001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160401b0384168061272c575b50505050505050565b6127416127649560e0855101515f0b906134b2565b6001600160401b03869792975116906001600160401b0360a08701511691613553565b1561290457506127a4856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6127af858254612623565b90556001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166001600160a01b03861690803b156120fa5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101879052905f908290604490829084905af180156120ef576128d8575b506001600160a01b038261284c876001600160401b03948460e09751169061308b565b511693511691510151850b93823b15610a7d57604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156104de576128c3575b8080808080612723565b6128ce828092612541565b61021757806128b9565b6001600160401b0391975060e0926128f85f6001600160a01b0393612541565b5f989093509150612829565b6001600160a01b03866377e88bc960e11b5f521660045260245260445ffd5b505f96505050505050565b61293d8d83879f9b96976125cb565b359061294a8a85886125cb565b356001600160401b03811681036120fa5761297392898d926001600160a01b038b5116926131c2565b9490911561299157600191612987916125ef565b98019b93926126c2565b989c506126ca565b604051906129a682612525565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b604051906129e48261250a565b5f60a0836129f0612999565b81528260208201528260408201528260608201528260808201520152565b90600182811c92168015612a3c575b6020831014612a2857565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612a1d565b6001600160401b03811161235457601f01601f191660200190565b3d15612a8b573d90612a7282612a46565b91612a806040519384612541565b82523d5f602084013e565b606090565b60405190612a9d826124ef565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a602052604090205460ff1615612af257565b63e2517d3f60e01b5f52336004527f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f60245260445ffd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff1615612b6157565b63e2517d3f60e01b5f52336004525f60245260445ffd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd602052604090205460ff1615612bb057565b63e2517d3f60e01b5f52336004527f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef660245260445ffd5b805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0333165f5260205260ff60405f20541615612c1e5750565b63e2517d3f60e01b5f523360045260245260445ffd5b6024906001600160401b03612ca3939481604051936001600160a01b03602086019816885216604084015216606082015260608152612c74608082612541565b6040519384916280000360e11b60208401525180918484015e81015f838201520301601f198101835282612541565b7333333333333333333333333333333333333333333b156120fa575f612ce591604051809381926317938e1360e01b83526020600484015260248301906124cb565b0381837333333333333333333333333333333333333333335af180156120ef57612d0c5750565b5f612d1691612541565b565b6001600160a01b0390612d296129d7565b50165f525f516020615a2d5f395f51905f5260205260405f2060405191612d4f8361250a565b60405191612d5c83612525565b6040518154815f612d6c83612a0e565b8083529260018116908115612f395750600114612ef8575b612d9092500382612541565b8352600181019360405180602087549182815201965f5260205f20905f915b816003840110612eac5797612df09284926001600160a01b039798999a5491818110611ce457818110611cc757818110611caa5710611c9c57500382612541565b60208601528160028401546001600160401b038116604088015260401c1660608601526001600160401b0360066003850154946080880197858716895260ff8760a01c1660a082015260c081019660ff8160a81c16885260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015293511615159081612e9e575b50156120fa57565b60ff9150511615155f612e96565b97600160806004928b546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c6060820152019901920191612daf565b5090835f5260205f20905f915b818310612f1d575050906020612d9092820101612d84565b6020919350806001915483858801015201910190918392612f05565b60209250612d9094915060ff191682840152151560051b820101612d84565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152612f8d604882612541565b51902090565b519081151582036120fa57565b5f80916040516001600160a01b03602082019216825260208152612fc5604082612541565b51906108105afa612fd4612a61565b9015613018576020818051810103126120fa5760405190602082018281106001600160401b038211176123545760405261301090602001612f93565b809152151590565b6313dd7ccd60e31b5f5260045ffd5b61303390610b8d612a90565b906001600160a01b0360016040519361304b856124ef565b63ffffffff8154818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156120fa57565b60405163a9059cbb60e01b60208201526001600160a01b03929092166024830152604480830193909352918152612d16916130c7606483612541565b614b17565b51906001600160401b03821682036120fa57565b5f919082916001600160401b03604051916001600160a01b03602084019416845216604082015260408152613116606082612541565b51906108015afa613125612a61565b9015613193576060818051810103126120fa576040519060608201918083106001600160401b03841117612354576001600160401b0392604052604061318c6060613172602086016130cc565b948585526131818482016130cc565b6020860152016130cc565b9101521690565b639d2c8fcb60e01b5f5260045ffd5b906001600160401b03809116911603906001600160401b03821161260f57565b95929190939495805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20916001600160a01b0383541695861561343a576002840180549860ff8a60481c1661342b576001600160a01b03600187015491166001600160a01b03821603613418576001600160401b039060a01c166001600160401b038a1660ff8b60401c165f146133c1575080926001600160401b03811682115f146133b957613279916131a2565b985b6132858a8c6125ef565b9a6001600160401b038c166001600160401b038516116133a8575069010000000000000000009069ff00000000000000000019161790556132c88160019a6131a2565b966001600160401b0360206001600160a01b036132ec60e0875101515f0b8d6134b2565b50981694015116833b156120fa5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152915f908390606490829084905af19081156120ef577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4392604092613398575b506001600160a01b0360018187541696015416956001600160401b038351921682526020820152a4565b5f6133a291612541565b5f61336e565b5f9b508b9a50985050505050505050565b50505f613279565b9092809a93806001600160401b038316105f1461340d576133ec916133e5916131a2565b809b6125ef565b816001600160401b038216115f1461340657505b9161327b565b9050613400565b50506133ec5f6133e5565b8463358d72d160e01b5f5260045260245ffd5b63f7348a7960e01b5f5260045ffd5b631a40316d60e01b5f5260045ffd5b60ff16604d811161260f57600a0a90565b9190820391821161260f57565b8115613471570490565b634e487b7160e01b5f52601260045260245ffd5b90620f4240820291808304620f4240149015171561260f57565b8181029291811591840414171561260f57565b9190805f0b80155f146134cf5750506001600160401b0382169190565b5f8113156134fa57506134e76134f69160ff16613449565b6001600160401b03841661349f565b9190565b9050607f19811461260f576001600160401b039261351f613528925f0360ff16613449565b93849116612623565b5f19810190811161260f5761354f613548846001600160401b0393613467565b938461349f565b1690565b6135796001600160401b039293613573849361356e81614bb0565b6130e0565b946125ef565b169116101590565b805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260ff60405f205416155f1461362257805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260405f20600160ff198254161790556001600160a01b03339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260ff60405f2054165f1461362257805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260405f2060ff1981541690556001600160a01b03339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b907f0000000000000000000000000000000000000000000000000000000000000000604083016136fe6001600160a01b03825116612fa0565b1561423f576001600160a01b038216805f525f516020615a2d5f395f51905f5260205260405f2090604051946137338661250a565b60405161373f81612525565b6040518454815f61374f83612a0e565b808352926001811690811561422057506001146141df575b61377392500382612541565b815260018401604051808260208294549384815201905f5260205f20925f905b80600383011061418d576137c5945491818110611ce457818110611cc757818110611caa5710611c9c57500382612541565b60208201526001600160a01b0360028501546001600160401b038116604084015260401c16606082015260038401546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528652600660048401549360ff60208901956001600160401b038116875260401c1615156040890152600581015460608901520154966001600160401b03881660808801526001600160401b0360a088019860401c168852606081016001600160a01b038151165f525f516020615a2d5f395f51905f5260205260405f2095604051966138b08861250a565b6040516138bc81612525565b6040518254815f6138cc83612a0e565b808352926001811690811561416e575060011461412d575b6138f092500382612541565b81526040516001830180548083525f9182526020808320849391840192905b8160038401106140db5754918181106140c1575b8181106140a4575b818110614087575b10614079575b50036139459082612541565b602082015260028201546001600160401b038116604083015260401c6001600160a01b0316606082015260038201546001600160a01b03811660808301528060a01c60ff1660a08301528060a81c60ff1660c083015260b01c5f0b60e0820152885260048101546001600160401b03811660208a015260401c60ff161515604089015260058101546060890152600601546001600160401b038116608089015260401c6001600160401b031660a088015281516001600160a01b0316613a0a90613027565b9383519060a08501918251996080870191825115159b613a2991612623565b8d5160e001515f0b613a3a916148ac565b90508d5160c0015160ff16825160c0015160ff1690613a5892614e9e565b9a1561404b578a925b5f8063ffffffff8b51166040516020810191825260208152613a84604082612541565b51906108085afa613a93612a61565b901561403c576020818051810103126120fa57898f9360ff60a0613ae19260206001600160401b03613ac982613ae799016130cc565b16950197885115155f14614031575051015116613449565b9061349f565b825115613fe75763ffffffff613b028160608d015116614bd6565b16620f4240019081620f42401161260f57613b38620f424091613ae16001600160401b039463ffffffff8f604001511690612623565b0416908351908115155f14613fdf5750915b5115613f9e576305f5e100811015613f5e57505f5b613b7f613b6c8651613485565b613b798a51885190612623565b90612623565b5f1981019190821161260f57613b79613ba592613b9f8b51895190612623565b90613467565b606480830283810482148415171561260f578211613ee45750505085518c5160e001515f0b613bd3916148ac565b9d8e8b9c929c516001600160401b031691516001600160401b031690613bf892613553565b15613e94579c8a9b9c9d85516001600160a01b03169387516001600160a01b0316935115159360405195613c2b8761250a565b86526020860190815260408601926001600160401b03169485845260608701926001600160401b0316968784526080810191825260a08101925f845260208d019586515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f2092516001600160a01b03166001600160a01b031683546001600160a01b0319161783556001830191516001600160a01b03166001600160a01b031682546001600160a01b0319161782555181549060a01b7bffffffffffffffff000000000000000000000000000000000000000016907fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1617905560020192516001600160401b03166001600160401b03166001600160401b0319845416178355511515613d8190839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151581549060481b69ff000000000000000000169069ff00000000000000000019161790555194516001600160a01b031695516001600160a01b03169651935160405194855260208501526001600160401b031660408401526060830152608082015260a07f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91891a4608001516001600160a01b03169283613e229261308b565b516001600160401b0316935160e001515f0b93813b156120fa57604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f92830b60648301529091908290608490829084905af180156120ef57612d0c5750565b5050505050945095505092505081612d16947f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c602080870151926001600160401b0360405191168152a35261492d565b999c5099509c50505050509450809650602091500151906001600160a01b038451169281810180911161260f575f19810190811161260f57811561347157612d16977fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e92604092049082519182526020820152a3526142b4565b6305f5e0ff19016001600160401b03811161260f57613f98613f8d6001600160401b036305f5e1009316613485565b6305f5e0ff90612623565b04613b5f565b6305f5e100811115613fb157505f613b5f565b6305f5e100036001600160401b03811161260f57613f98613f8d6001600160401b036305f5e1009316613485565b905091613b4a565b63ffffffff613ffb8160608d015116614bd6565b16620f42400390620f4240821161260f57613b38620f424091613ae16001600160401b039463ffffffff8f60400151169061345a565b905051015116613449565b635cffc5fb60e11b5f5260045ffd5b8261271003612710811161260f576127106140706001600160401b0392838f1661349f565b04169a92613a61565b60c01c81526020015f613939565b9260206001916001600160401b038560801c168152019301613933565b9260206001916001600160401b038560401c16815201930161392b565b9260206001916001600160401b0385168152019301613923565b935090916001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019084939261390f565b5090845f5260205f20905f915b8183106141525750509060206138f0928201016138e4565b602091935080600191548385880101520191019091839261413a565b602092506138f094915060ff191682840152151560051b8201016138e4565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c6060820152019401920184929391613793565b5090865f5260205f20905f915b81831061420457505090602061377392820101613767565b60209193508060019154838588010152019101909183926141ec565b6020925061377394915060ff191682840152151560051b820101613767565b6080840151909392501561426a576001600160a01b038351166320a2097d60e11b5f5260045260245ffd5b6001600160a01b0390612d16937f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60208481870151935116604051908152a216606082015261492d565b6060810151906001600160a01b035f921691825f525f516020615a2d5f395f51905f5260205260405f206040516142ea8161250a565b6040516142f681612525565b6040518354815f61430683612a0e565b80835292600181169081156148775750600114614836575b61432a92500382612541565b815260018301604051808260208294549384815201905f5260205f20925f905b8060038301106147e45761437c945491818110611ce457818110611cc757818110611caa5710611c9c57500382612541565b60208201526001600160a01b0360028401546001600160401b038116604084015260401c16606082015260038301546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528152600660048301549260ff60208401946001600160401b038116865260401c1615156040840152600581015460608401520154906001600160401b03821660808201526001600160401b0360a082019260401c168252604085019361444b6001600160a01b03865116612fa0565b156147745785519161271061447461446960a08a0195865190612623565b60808a01519061349f565b049383519480861161476c575b50846146ca575b6144c06144a7614499878b51612623565b60e0855101515f0b906148ac565b919092826001600160401b03808b511692511691613553565b1561467c5750846145c0575b61455d906001600160401b037fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c976145348c6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b61453f898254612623565b905551169360e06001600160a01b038a511694510151900b906148ac565b919092836145a4575b5050505061459f6001600160a01b0360208701519551169551915192604051938493846040919493926060820195825260208201520152565b0390a4565b6145b26145b794828c614cd4565b612c34565b5f808080614566565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001695863b156120fa5760405163f3fef3a360e01b81526001600160a01b038b16600482015260248101879052965f908890604490829084905af19687156120ef577fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c97614659575b5095506144cc565b61455d929194505f61466a91612541565b6001600160401b035f94919250614651565b9793505050507f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c9350602092508291506146b58161492d565b0151926001600160401b0360405191168152a3565b6146d9858a60208b0151614bf1565b614488576040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031660048201529094506020816024818c5afa9081156120ef575f9161473a575b5093614488565b90506020813d602011614764575b8161475560209383612541565b810103126120fa57515f614733565b3d9150614748565b94505f614481565b505050506080820151909250156147a2576001600160a01b038251166320a2097d60e11b5f5260045260245ffd5b612d16917f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b0381850151935116604051908152a261492d565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192018492939161434a565b5090855f5260205f20905f915b81831061485b57505090602061432a9282010161431e565b6020919350806001915483858801015201910190918392614843565b6020925061432a94915060ff191682840152151560051b82010161431e565b8181106148a1575050565b5f8155600101614896565b9190805f0b9081155f146148c95750506001600160401b03821690565b5f821315614904576148de915060ff16613449565b9182156134715761354f6148fd82856001600160401b0394069061345a565b9384613467565b505f0380805f0b0361260f5761354f6149276001600160401b039260ff16613449565b8461349f565b805161271061495061494560a0850193845190612623565b60808501519061349f565b0481518181115f14614b0f5750905b8190602084019161498283519460608701956001600160a01b0387511690614bf1565b15614b08575b80614a52575b7f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9161459f6001600160a01b03806149c7858a51612623565b966149df828a51169860408c0199848b51169061308b565b614a1c828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b614a27878254612623565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916001600160a01b0385511692803b156120fa5760405163f3fef3a360e01b81526001600160a01b03949094166004850152602484018390525f908490604490829084905af19283156120ef577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f93614af8575b50915061498e565b5f614b0291612541565b5f614af0565b505f614988565b90509061495f565b906001600160a01b03614b7892165f8060405193614b36604086612541565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602086015260208151910182855af1614b72612a61565b91614efd565b8051908115918215614b8e575b5050156120fa57565b81925090602091810103126120fa576020614ba99101612f93565b5f80614b85565b6001600160401b03166001609d1b01806001609d1b1161260f576001600160a01b031690565b63ffffffff60649116029063ffffffff821691820361260f57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa9182156120ef575f92614ca0575b5080821094851595614c61575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080614c5b565b9091506020813d602011614ccc575b81614cbc60209383612541565b810103126120fa5751905f614c4e565b3d9150614caf565b9091906001600160401b038316614e4d576001600160a01b03919250166040515f806020830163095ea7b360e01b8152736b9e773128f453f5c2c60935ee2de2cbc5390a24602485015285604485015260448452614d33606485612541565b83519082865af1614d42612a61565b81614e16575b5080614e0c575b15614db9575b5050736b9e773128f453f5c2c60935ee2de2cbc5390a243b156120fa5760405190630acb7f4b60e21b8252600482015263ffffffff60248201525f8160448183736b9e773128f453f5c2c60935ee2de2cbc5390a245af180156120ef57612d0c5750565b614e05916130c760405163095ea7b360e01b6020820152736b9e773128f453f5c2c60935ee2de2cbc5390a2460248201525f604482015260448152614dff606482612541565b82614b17565b5f80614d55565b50813b1515614d4f565b8051801592508215614e2b575b50505f614d48565b81925090602091810103126120fa576020614e469101612f93565b5f80614e23565b6001600160a01b03614e61612d1694614bb0565b911661308b565b51906001600160a01b03821682036120fa57565b519060ff821682036120fa57565b9060ff8091169116039060ff821161260f57565b9160ff811660ff83168181145f14614eb7575050505090565b6001600160401b039492911115614ee557614ede614ed961354f948693614e8a565b613449565b911661349f565b614ef6614ed9859261354f95614e8a565b9116613467565b91925015614f1957815115614f10575090565b3b156120fa5790565b5080519081156120fa57602001fdfe60a080604052346100455733608052610aba908161004a82396080518181816086015281816101b80152818161039a015281816104d50152818161054c01526106a40152f35b5f80fdfe60806040526004361015610011575f80fd5b5f5f3560e01c8063435354d31461050a57806390a0827b146104a55780639c45c34b146100ad578063a703334f1461033e578063de1a324a146100cb578063e94b77c1146100ad5763eba61c0e14610067575f80fd5b346100aa57806003193601126100aa5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b80fd5b50346100aa576100c86100bf366105ee565b92919091610699565b80f35b50346100aa57366003190161010081126103265760a0136100aa5760405160a0810181811067ffffffffffffffff82111761032a5760405261010b6105db565b91828252602435918215158303610326576020810192835260443563ffffffff8116810361032257604082015260643563ffffffff81168103610322576060820152608435906001600160a01b0382168203610322576080015260a43567ffffffffffffffff81168091036103265760c4359067ffffffffffffffff82168092036103225760e435946fffffffffffffffffffffffffffffffff861680960361031e576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361031e5763ffffffff6101ed911661066c565b9351151592811561030f57821561030057916102819391600493506040519263ffffffff60208501971687526040840152606083015260808201525f945f60a0830152600260c083015260e082015260e0815261024c61010082610636565b6020604051948592630100000160e01b83850152518091602485015e8201018281015f8152500301601f198101835282610636565b7333333333333333333333333333333333333333333b156102fc575f6102bb91604051809381926317938e1360e01b8352600483016108ff565b0381837333333333333333333333333333333333333333335af180156102f1576102e3575080f35b6102ef91505f90610636565b005b6040513d5f823e3d90fd5b5f80fd5b6313c0a8df60e01b8152600490fd5b63017461b760e71b8152600490fd5b8380fd5b8280fd5b5080fd5b634e487b7160e01b83526041600452602483fd5b50346100aa5760603660031901126100aa578060043567ffffffffffffffff811680910361049c57602435906001600160a01b0382168092036104a15760443567ffffffffffffffff811680910361049f576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361049f5761041f91600491604051916020830195865260408301526060820152606081526103eb608082610636565b60206040519485926280000360e11b83850152518091602485015e820101828101868152500301601f198101835282610636565b7333333333333333333333333333333333333333333b1561049c578161045991604051809381926317938e1360e01b8352600483016108ff565b0381837333333333333333333333333333333333333333335af18015610491576104805750f35b8161048a91610636565b6100aa5780f35b6040513d84823e3d90fd5b50fd5b505b5050fd5b50346100aa5760403660031901126100aa576004356001600160a01b038116809103610326576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163303610326576100c890602435903390610929565b50346102fc5760403660031901126102fc576105246105db565b602435906fffffffffffffffffffffffffffffffff82168092036102fc576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102fc5760046105806102819261066c565b926040519063ffffffff60208301951685526040820152604081526105a6606082610636565b6020604051948592630100000b60e01b83850152518091602485015e8201018281015f8152500301601f198101835282610636565b6004359063ffffffff821682036102fc57565b60809060031901126102fc576004356001600160a01b03811681036102fc579060243567ffffffffffffffff811681036102fc579060443590606435805f0b81036102fc5790565b90601f8019910116810190811067ffffffffffffffff82111761065857604052565b634e487b7160e01b5f52604160045260245ffd5b63ffffffff6127109116019063ffffffff821161068557565b634e487b7160e01b5f52601160045260245ffd5b926001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102fc57805f0b9081155f1461088f5750505b816106e357505050565b67ffffffffffffffff16806108685750906001600160a01b03166040515f806020830163095ea7b360e01b8152736b9e773128f453f5c2c60935ee2de2cbc5390a2460248501528560448501526044845261073f606485610636565b83519082865af161074e610a1a565b81610839575b508061082f575b156107d7575b5050736b9e773128f453f5c2c60935ee2de2cbc5390a243b156102fc5760405190630acb7f4b60e21b8252600482015263ffffffff60248201525f8160448183736b9e773128f453f5c2c60935ee2de2cbc5390a245af180156102f1576107c7575b505b565b5f6107d191610636565b5f6107c3565b6108289161082360405163095ea7b360e01b6020820152736b9e773128f453f5c2c60935ee2de2cbc5390a2460248201525f60448201526044815261081d606482610636565b82610978565b610978565b5f80610761565b50813b151561075b565b805180159250821561084e575b50505f610754565b6108619250602080918301019101610960565b5f80610846565b6001609d1b0191826001609d1b11610685576001600160a01b03806107c594169116610929565b5f8213156108d0576108a4915060ff16610a09565b80156108bc57808306830392831161068557506106d9565b634e487b7160e01b5f52601260045260245ffd5b505f0380805f0b03610685576108e89060ff16610a09565b8281810291818304149015171561068557506106d9565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b6107c5926001600160a01b036040519363a9059cbb60e01b6020860152166024840152604483015260448252610823606483610636565b908160209103126102fc575180151581036102fc5790565b906001600160a01b036109d992165f8060405193610997604086610636565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602086015260208151910182855af16109d3610a1a565b91610a59565b80519081159182156109ef575b5050156102fc57565b610a029250602080918301019101610960565b5f806109e6565b60ff16604d811161068557600a0a90565b3d15610a54573d9067ffffffffffffffff82116106585760405191610a49601f8201601f191660200184610636565b82523d5f602084013e565b606090565b91925015610a7557815115610a6c575090565b3b156102fc5790565b5080519081156102fc57602001fdfea26469706673582212208abf3b44431f4c61bdd7aa5253477149e50113939030a9816d0109335ec8e08364736f6c634300081e00336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0002dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800a26469706673582212203f720c6ea86c631282ab7132c9a7dc291402b5cc506847c33ef5f144f0f78ca764736f6c634300081e0033bd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ffb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680000000000000000000000000081d40f21f12a8f0e3252bccb954d722d4c464b640000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000bc217096db9eb6d2782c1d9e725d462077a4d1f6000000000000000000000000b88339cb7199b77e23db6e890353e22632ba630f0000000000000000000000005e7840e06faccb6d1c3b5f5e0d1d3d07f2829bba", + "nonce": "0x29d", "chainId": "0x3e7" }, "additionalContracts": [ { "transactionType": "CREATE", "contractName": "HyperCoreFlowExecutor", - "address": "0xc5b7162e6a0fde52cc180dc8ab273feb8b32f57d", - "initCode": "0x60c0346200010457601f62005d3e38819003918201601f19168301916001600160401b038311848410176200010857808492604094855283398101031262000104576200005a602062000052836200011c565b92016200011c565b6001600160a01b0390911660805260a052604051615c0c90816200013282396080518181816108b7015281816109ff0152818161158b0152818161182f01528181612a1701528181614a0501528181614b7c01528181614f68015261504e015260a051818181610670015281816107de01528181611a7b01528181613a6e01528181614188015281816141e401528181614288015281816142b40152818161435301526146990152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001045756fe6080604052600436101562000012575f80fd5b5f803560e01c806246912e146200257057806301ffc9a71462002515578063037a06a414620022c957806304c73f6014620022a9578063057f037014620021f45780631f74a0b5146200217257806321081d3c1462002135578063248a9ca314620020e75780632e748b211462001f7f5780632f2ff15d1462001f1a578063319adf9f1462001b6657806336568abe1462001b1757806337710e201462001abd57806339fff0981462001a225780633b1c6a0114620017275780633cf3a02514620016eb5780634265fe861462001699578063490e662f146200165c5780634b3b029b146200161f578063502a82e21462001554578063521c98ba1462000d9157806369b97ac71462000d7157806379c7b60b1462000d1757806379c7f2891462000c1557806390a0827b1462000bd457806391d148541462000b6657806396cc2cfb14620008f9578063a217fddf14620008db578063a4b672b61462000895578063af5de6f91462000802578063c55dae6314620007bc578063ccbedaec1462000542578063d06e28ed1462000443578063d547741f14620003da578063e38b73a914620003bc578063ea0aaf241462000359578063eb84e7f2146200024b5763ff3eae0014620001e2575f80fd5b34620002485760c0366003190112620002485762000200366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10054156200023657620002339062004e31565b80f35b60405163cd6d8f7d60e01b8152600490fd5b80fd5b503462000248576020366003190112620002485760408160c09260a0835162000274816200270b565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051620002c8816200270b565b6001600160a01b039060028284541693848352600181015490602084019185811683526001600160401b0393849283604088019360a01c1683520154956060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b50346200024857602036600319011262000248576020620003b36200037d620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b50346200024857806003193601126200024857602060405160068152f35b50346200024857604036600319011262000248576200043f600435620003ff620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262000439600160408620015462002e64565b620039b1565b5080f35b503462000248576040366003190112620002485762000461620025de565b602435906fffffffffffffffffffffffffffffffff82168092036200053e576200048a62002e08565b6200049581620032e8565b90836001600160a01b039263ffffffff84608083015116915116813b156200053a57829160448392604051948593849263435354d360e01b845260048401528a60248401525af180156200052f5762000513575b5050167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b6200051e90620026f7565b6200052b57835f620004e9565b8380fd5b6040513d84823e3d90fd5b8280fd5b5f80fd5b50346200024857606036600319011262000248578062000561620025de565b6200056b6200260c565b906200057662002623565b906200058162002d37565b620005bc816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200071a574362000601826001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0380600162000648846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b01541693816001600160401b03938483166200071f575b5050508183166200066e578480f35b7f00000000000000000000000000000000000000000000000000000000000000001684525f8051602062005bb783398151915260205260046040852001541690823b156200071a5760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156200052f5762000702575b8080808480f35b6200070d90620026f7565b6200024857805f620006fb565b505050fd5b1686525f8051602062005bb783398151915260205282600460408820015416853b15620007b85760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152858160648183895af1908115620007ad57869162000791575b82906200065f565b6200079c90620026f7565b620007a957845f62000789565b8480fd5b6040513d88823e3d90fd5b8680fd5b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503462000248576020366003190112620002485760206001600160a01b03620008346200082e620025de565b620031ff565b6109ab60405162000848858301826200277c565b818152848101916200520c8339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152620008898162002744565b51902016604051908152f35b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346200024857806003193601126200024857602090604051908152f35b503462000248576040366003190112620002485762000917620025de565b906200092262002e08565b6200092d8262002f58565b6200093883620032e8565b926200094e60e083510151840b60243562004da2565b9460208401906001600160401b036200097381845116898360a08a01511691620038d2565b1562000b3657869750620009b7856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b620009c48582546200287c565b90556001600160a01b03808616807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051898152a2817f000000000000000000000000000000000000000000000000000000000000000016803b1562000b325760405163f3fef3a360e01b81526001600160a01b038316600482015260248101889052908a908290604490829084905af1801562000b275787918b9162000b08575b505062000a8290608060e0960192848451169062003353565b511692511694510151850b93813b1562000b0457604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156200052f5762000af15750f35b62000afc90620026f7565b620002485780f35b8580fd5b62000b15919250620026f7565b62000b235785895f62000a69565b8880fd5b6040513d8c823e3d90fd5b8980fd5b6040516377e88bc960e11b81526001600160a01b03861660048201526001600160401b0389166024820152604490fd5b50346200024857604036600319011262000248576001600160a01b03604062000b8e620025f5565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020522091165f52602052602060ff60405f2054166040519015158152f35b50346200024857604036600319011262000248576200023362000bf6620025de565b62000c0062002d37565b602435906001600160a01b0333911662003353565b503462000248576020366003190112620002485760a062000c7a62000c39620025de565b62000c4362002d0b565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b0360016040519262000c9384620026db565b805463ffffffff90818116865260ff8160201c1615156020870152818160281c16604087015260481c166060850152015416608082015262000d1560405180926001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565bf35b50346200024857602036600319011262000248576020620003b362000d3b620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b503462000248578060031936011262000248576020604051620f42408152f35b5034620002485760a0366003190112620002485762000daf620025de565b62000db96200263a565b9062000dc46200264e565b6001600160401b0360643516606435036200053e57608435906001600160401b03821682036200053e5762000df862002db1565b62000e0262002c05565b508480604051602081019063ffffffff881682526020815262000e258162002760565b519061080c5afa62000e3662002cd7565b901562001542578051810190602081830312620007b85760208101516001600160401b038111620015055761010081830184031262001505576040519262000e7e8462002727565b602082840101516001600160401b03811162000b325760208201603f828587010101121562000b32576020818486010101519062000ebc8262002cbb565b9162000ecc60405193846200277c565b8083526020840160408284888a01010101116200152a5762000efa9160406020850191878901010162002691565b8452604082840101516001600160401b03811162000b325760208201603f828587010101121562000b3257602081848601010151916001600160401b0383116200152e578260051b6040519362000f5560208301866200277c565b84526020808501920160408285888a01010101116200152a576040838688010101915b60408285888a01010101831062001509575050505060208401526101009062000fa683820160600162003428565b604085015262000fbb6080828501016200512c565b606085015262000fd060a0828501016200512c565b9283608086015262000fe760c08383010162005141565b60a086015262000ffc60e08383010162005141565b60c08601520101519081880b820362001505578160e08401526001600160a01b03808716911603620007b8576200103890870b60643562003829565b506040519162001048836200270b565b825263ffffffff86166020830152821515604083015260608201526001600160401b036064351660808201526001600160401b03831660a08201526001600160a01b03841686525f8051602062005bb783398151915260205260408620815180518051906001600160401b03821162001438578190620010c9855462002c80565b601f8111620014c5575b50602090601f831160011462001458578b926200144c575b50508160011b915f199060031b1c19161782555b60208101518051906001600160401b038211620014385768010000000000000000821162001438576020906001850154836001870155808410620013e5575b500190600184018a5260208a20908a5b8160021c81106200139d5750600319811680820362001334575b50505050936001600160a01b03937f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd82244353969360068460809860026001600160401b039701876040830151168154907bffffffffffffffffffffffffffffffffffffffff0000000000000000606085015160401b169163ffffffff60e01b16171790556003820190898b820151169082549174ff000000000000000000000000000000000000000060a083015160a01b16907fffffffffffffffffff000000000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff00000000000000000000000000000000000000000000169416171717179055620012c5600482018760208601511688198254161781556040850151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b6060830151600582015501908488820151166fffffffffffffffff000000000000000060a0845493015160401b16916fffffffffffffffffffffffffffffffff19161717905563ffffffff6040519816885215156020880152816064351660408801521660608601521692a280f35b928b938c5b81840381106200135d5750505060021c0155838360066001600160401b0362001168565b9091946020620013926001926001600160401b03895116908560031b60031b916001600160401b03809116831b921b19161790565b960192910162001339565b8b8c5b60048110620013b75750838201556001016200114e565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501620013a0565b6200141890600187018d52838d20600380870160021c820192601888831b16806200141f575b500160021c019062004d8a565b5f6200113e565b5f1990818601918254918a03851b1c1690555f6200140b565b634e487b7160e01b8a52604160045260248afd5b015190505f80620010eb565b9250848b5260208b20908b935b601f1984168510620014a9576001945083601f1981161062001490575b505050811b018255620010ff565b01515f1960f88460031b161c191690555f808062001482565b8181015183556020948501946001909301929091019062001465565b620014f390868d5260208d20601f850160051c81019160208610620014fa575b601f0160051c019062004d8a565b5f620010d3565b9091508190620014e5565b8780fd5b906020806040936200151b8662003428565b81520193019290915062000f78565b8b80fd5b634e487b7160e01b8b52604160045260248bfd5b604051639b0c335d60e01b8152600490fd5b503462000248576040366003190112620002485762001572620025de565b6024356200157f62002d37565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156200161b5760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156200052f5762001603575b50506200023391339062003353565b6200160e90620026f7565b6200053a57825f620015f4565b5080fd5b5034620002485780600319360112620002485760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b5034620002485780600319360112620002485760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b5034620002485760c03660031901126200024857620016b8366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576200023390620046c4565b50346200024857602036600319011262000248576200170962002d37565b80808080600435335af16200171d62002cd7565b5015620002485780f35b503462000248576060366003190112620002485762001745620025f5565b6001600160a01b0360443581811692918382036200053e576200176762002e08565b620017728262002f58565b906200177e8462003257565b62000b045760408201511562000b04576001600160401b0391600183608083015116019083821162001a0e57620017bd60e082510151890b8362003829565b5094620017db602083019386855116908760a08601511691620038d2565b1562000b23576200181c906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b620018298682546200287c565b905587837f000000000000000000000000000000000000000000000000000000000000000016803b156200161b5760405163f3fef3a360e01b81526001600160a01b038a166004820152602481018890529082908290604490829084905af180156200052f57620019f0575b50509183826200192494620018bc60e062001913986024975116925101518c0b8962004da2565b508481620019d0575b505050511695604051916020830191169687825260408301526001606083015260608252620018f48262002744565b6040519485926280000360e11b60208501525180928585019062002691565b81010360048101845201826200277c565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062001970906024830190620026b4565b03925af18015620019c557620019b3575b506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b620019be90620026f7565b5f62001981565b6040513d5f823e3d90fd5b620019df620019e79362002847565b168b62003353565b5f8084620018c5565b620019ff9094929394620026f7565b62001505579190875f62001895565b634e487b7160e01b5f52601160045260245ffd5b5034620002485760e0366003190112620002485762001a41366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576001600160a01b0380606083015116907f000000000000000000000000000000000000000000000000000000000000000016145f1462001aae576200023390620046c4565b620002339060c4359062003a45565b50346200024857602036600319011262000248576020620003b362001ae1620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b503462000248576040366003190112620002485762001b35620025f5565b336001600160a01b0382160362001b54576200043f90600435620039b1565b60405163334bd91960e11b8152600490fd5b50346200024857602036600319011262000248576001600160a01b0362001b8c620025de565b62001b9662002c45565b501681525f8051602062005bb783398151915260205260408120906040519162001bc0836200270b565b60405162001bce8162002727565b6040518254818562001be08362002c80565b808352926001811690811562001ef9575060011462001eb4575b62001c08925003826200277c565b815260405160018301805480835290855260208086209083019186915b81600384011062001e72579360069593819362001c73936001600160401b039997549181811062001e5e575b81811062001e47575b81811062001e30575b1062001e21575b5003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c850b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a0830152604051906020825282519060c0602084015262001d31825161010060e08601526101e0850190620026b4565b60208084015185830360df190161010087015280518084529282019492910190835b81811062001e015750505083946001600160401b039260e08385604060a0960151166101208901526001600160a01b036060820151166101408901526001600160a01b0360808201511661016089015260ff858201511661018089015260ff60c0820151166101a08901520151900b6101c086015282602082015116604086015260408101511515606086015260608101516080860152826080820151168286015201511660c08301520390f35b82516001600160401b031686526020958601959092019160010162001d53565b60c01c81526020015f62001c6a565b9260206001918c8560801c16815201930162001c63565b9260206001918c8560401c16815201930162001c5a565b9260206001918c8516815201930162001c51565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162001c25565b5090848652602086209086915b81831062001edc57505090602062001c089282010162001bfa565b602091935080600191548385880101520191019091839262001ec1565b6020925062001c0894915060ff191682840152151560051b82010162001bfa565b50346200024857604036600319011262000248576200043f60043562001f3f620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262001f79600160408620015462002e64565b62003912565b503462000248576080366003190112620002485762001f9d620025de565b62001fa76200260c565b9062001fb262002623565b90606435926fffffffffffffffffffffffffffffffff84168094036200053e5762001fdc62002e08565b62001fe782620032e8565b856001600160a01b03928360808401511692833b156200053a57620020646101048492836040519586948593636f0d192560e11b855260048501906001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565b6001600160401b038091169b8c60a485015216978860c48401528c60e48401525af180156200052f57620020cb575b5050907f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c92604092835195865260208601521692a380f35b620020d990939293620026f7565b62000b045790855f62002093565b50346200024857602036600319011262000248576001604060209260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008452200154604051908152f35b5034620002485780600319360112620002485760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503462000248576060366003190112620002485762002190620025de565b6001600160401b036024358181116200052b57620021b39036906004016200265e565b91909360443591821162000248576020620021ec868686620021d936600489016200265e565b939092620021e662002e08565b6200288a565b604051908152f35b50346200053e5760403660031901126200053e5762002212620025de565b906024356200222062002d37565b6001600160a01b03928360806200223783620032e8565b015116803b156200053e576040516390a0827b60e01b81526001600160a01b038316600482015260248101849052905f908290604490829084905af18015620019c55762002290575b5062000233929333911662003353565b620002339350620022a190620026f7565b5f9262002280565b346200053e575f3660031901126200053e5760206040516305f5e1008152f35b346200053e5760a03660031901126200053e57620022e6620025de565b620022f06200263a565b620022fa6200264e565b6064359063ffffffff938483168093036200053e57608435908582168092036200053e57620023298162002f58565b506200233462002db1565b6001600160a01b03918260016200237b846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b0154168015620024bc575b8360018960405199620023998b620026db565b16988981526020810197151597888152604082018a81526060830190878252856080850197169d8e8852620023fe8a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b9451166cffffffff00000000000000000068ffffffff000000000064ff0000000087549651151560201b16935160281b16935160481b16936cffffffffffffffffffffffffff19161717171781550191511673ffffffffffffffffffffffffffffffffffffffff19825416179055620024778762003257565b156200053e577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9946080946040519788526020880152604087015260608601521692a3005b50620024c882620031ff565b6040516109ab8082018281106001600160401b03821117620025015782916200520c833903905ff58062002386576040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b346200053e5760203660031901126200053e5760043563ffffffff60e01b81168091036200053e57602090637965db0b60e01b81149081156200255e575b506040519015158152f35b6301ffc9a760e01b1490508262002553565b346200053e5760403660031901126200053e57620025dc62002591620025de565b6001600160a01b03620025a36200260c565b91620025ae62002d37565b165f525f8051602062005bb78339815191526020526001600160401b03600460405f20015416339062002ea6565b005b600435906001600160a01b03821682036200053e57565b602435906001600160a01b03821682036200053e57565b602435906001600160401b03821682036200053e57565b604435906001600160401b03821682036200053e57565b6024359063ffffffff821682036200053e57565b6044359081151582036200053e57565b9181601f840112156200053e578235916001600160401b0383116200053e576020808501948460051b0101116200053e57565b5f5b838110620026a35750505f910152565b818101518382015260200162002693565b90602091620026cf8151809281855285808601910162002691565b601f01601f1916010190565b60a081019081106001600160401b038211176200250157604052565b6001600160401b0381116200250157604052565b60c081019081106001600160401b038211176200250157604052565b61010081019081106001600160401b038211176200250157604052565b608081019081106001600160401b038211176200250157604052565b604081019081106001600160401b038211176200250157604052565b90601f801991011681019081106001600160401b038211176200250157604052565b60c09060031901126200053e5760405190620027ba826200270b565b60043582526024356020830152816001600160a01b0360443581811681036200053e57604083015260643590811681036200053e576060820152608435608082015260a060a435910152565b9190811015620028175760051b0190565b634e487b7160e01b5f52603260045260245ffd5b9190916001600160401b038080941691160191821162001a0e57565b9073200000000000000000000000000000000000000091820180921162001a0e57565b906305f5e0ff820180921162001a0e57565b9190820180921162001a0e57565b919392935f945f948083036200053e57620028d5856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200053e57869796939291620028ef8662002f58565b926080620028fd88620032e8565b01906001600160a01b0392838351169460208701976200292a6001600160401b0397888b5116906200343d565b845f9b5b1062002b94575b50508c15925062002b8791505057436200297f886001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5582861662002993575b5050505050505050565b620029c195620029ab60e0865101515f0b8262003829565b85889992995116908660a08901511691620038d2565b1562002b4d575062002a03876001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62002a108782546200287c565b90558682167f00000000000000000000000000000000000000000000000000000000000000008316803b156200053e5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101899052905f908290604490829084905af18015620019c55762002b29575b509062002a9587849360e096958451169062003353565b511693511691510151850b93823b1562000b0457604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156200052f5762002b11575b80808080808062002989565b62002b1d8291620026f7565b62000248578062002b05565b60e094939291995062002b3c90620026f7565b5f98929350909162002a9562002a7e565b8451608001516040516377e88bc960e11b81529084166001600160a01b031660048201526001600160401b03919091166024820152604490fd5b505f985050505050505050565b62002ba6859e9f809c95968462002806565b359062002bb58c858862002806565b359189831683036200053e578a62002bd2938a8a51169262003520565b9490911562002bfb5762002beb8f93926001926200282b565b9b019e9d8f95949192956200292e565b9a9e9d5062002935565b6040519062002c148262002727565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b6040519062002c54826200270b565b5f60a08362002c6262002c05565b81528260208201528260408201528260608201528260808201520152565b90600182811c9216801562002cb0575b602083101462002c9c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162002c90565b6001600160401b0381116200250157601f01601f191660200190565b3d1562002d06573d9062002ceb8262002cbb565b9162002cfb60405193846200277c565b82523d5f602084013e565b606090565b6040519062002d1a82620026db565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a60205260409020547f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f9060ff161562002d935750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161562002dea57565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd60205260409020547f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef69060ff161562002d935750565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f2054161562002d935750565b620019139262002eec92602492604051926001600160a01b0360208501931683526001600160401b03809216604085015216606083015260608252620018f48262002744565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062002f38906024830190620026b4565b03925af18015620019c55762002f4b5750565b62002f5690620026f7565b565b9062002f6362002c45565b506001600160a01b038092165f5260205f8051602062005bb78339815191528152604092835f2084519062002f98826200270b565b85519562002fa68762002727565b80518254905f8162002fb88462002c80565b918282526001948a86821691825f14620031e05750506001146200319f575b62002fe5925003826200277c565b88528083019082519087829384928282549586815201915f52825f20945f5b8160038201106200315a57620030469654928583831062003140575b83831062003124575b83831062003107575b505010620030f8575b50905003826200277c565b858801526006600283015492856001600160401b0394858116858c0152841c1660608a015260ff60038201549760808b019a888a168c52828a60a01c1660a082015260c0810199838160a81c168b5260b01c5f0b60e0820152875260048301549086821690880152841c161515838601526005810154606086015201549082821660808501521c1660a082015293511615159081620030e9575b50156200053e57565b60ff9150511615155f620030e0565b60c01c81520188905f6200303b565b9091946001600160401b038560801c1681520193015f8562003032565b8192956001600160401b03868d1c168152019401908562003029565b8192956001600160401b0386168152019401908562003020565b86546001600160401b038082168652818b1c811696860196909652608081811c9096168a86015260c01c6060850152958201958795508c949093019260040162003004565b5050845f528188805f20855f915b858310620031c657505062002fe5935082010162002fd7565b8091929450548385880101520191018990858593620031ad565b60ff19168482015262002fe594151560051b840101915062002fd79050565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152606081018181106001600160401b03821117620025015760405251902090565b519081151582036200053e57565b5f80916040516001600160a01b036020820192168252602081526200327c8162002760565b51906108105afa6200328d62002cd7565b9015620032d6576020818051810103126200053e5760405190602082018281106001600160401b038211176200250157604052620032ce9060200162003249565b809152151590565b6040516313dd7ccd60e31b8152600490fd5b620032f79062000c4362002d0b565b906001600160a01b036001604051936200331185620026db565b805463ffffffff90818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156200053e57565b60405163a9059cbb60e01b602082019081526001600160a01b039384166024830152604480830195909552938152620033ea9390925f92839291906200339b6064876200277c565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051620033ce8162002760565b8181520152519082855af1620033e362002cd7565b91620051de565b805190811591821562003402575b5050156200053e57565b81925090602091810103126200053e57602062003420910162003249565b5f80620033f8565b51906001600160401b03821682036200053e57565b604080516001600160a01b0392909216602083019081526001600160401b039390931682820152808252915f918291906200347a6060826200277c565b51906108015afa6200348b62002cd7565b9015620034f5576060818051810103126200053e5781516001600160401b0392606082018481118382101762002501578152620034ee6060620034d16020860162003428565b94858552620034e284820162003428565b60208601520162003428565b9101521690565b8151639d2c8fcb60e01b8152600490fd5b6001600160401b03918216908216039190821162001a0e57565b949192909394805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20906001600160a01b039485835416958615620037ab57600284019788549860ff8a60481c166200379957600186015483608086510151168482160362003780576001600160401b039060a01c166001600160401b038b1660ff8c60401c165f146200371f575080926001600160401b03811682115f146200371657620035d79162003506565b995b620035e58b8d6200282b565b9b6001600160401b038d166001600160401b0385161162003704575069010000000000000000009069ff00000000000000000019161790556200362b8160019b62003506565b976001600160401b036020846200364b8d60e0895101515f0b9062003829565b50991695015116843b156200053e5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152925f908490606490829084905af1918215620019c5577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4393604093620036f2575b5060018187541696015416956001600160401b038351921682526020820152a4565b620036fd90620026f7565b5f620036d0565b5f9c508c9b5099505050505050505050565b50505f620035d7565b9092809b93806001600160401b038316105f1462003773576200374f91620037479162003506565b809c6200282b565b816001600160401b038216115f146200376b57505b91620035d9565b905062003764565b50506200374f5f62003747565b60405163358d72d160e01b815260048101879052602490fd5b60405163f7348a7960e01b8152600490fd5b604051631a40316d60e01b8152600490fd5b60ff16604d811162001a0e57600a0a90565b9190820391821162001a0e57565b8115620037e8570490565b634e487b7160e01b5f52601260045260245ffd5b90620f42409182810292818404149015171562001a0e57565b8181029291811591840414171562001a0e57565b9190805f0b80155f14620038475750506001600160401b0382169190565b5f81131562003877575062003863620038739160ff16620037bd565b6001600160401b03841662003815565b9190565b9050607f19811462001a0e5762003893905f0360ff16620037bd565b91620038aa836001600160401b038093166200287c565b5f19810190811162001a0e57620038c684620038ce92620037dd565b938462003815565b1690565b6200390a6200390392936001600160401b0393836001600160a01b03620038fc8780971662002847565b166200343d565b946200282b565b169116101590565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f205416155f14620039aa57825f5260205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f2054165f14620039aa57825f5260205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b9062003a5e6001600160a01b0360408401511662003257565b1562004618576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165f525f8051602062005bb783398151915260205260405f20906040519162003ab6836200270b565b60405162003ac48162002727565b6040518254815f62003ad68362002c80565b8083529260018116908115620045f75750600114620045b2575b62003afe925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b81600384011062004570579360069593819362003b67936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a08301526001600160a01b036060840151165f525f8051602062005bb783398151915260205260405f206040519062003c2e826200270b565b60405162003c3c8162002727565b6040518254815f62003c4e8362002c80565b80835292600181169081156200454f57506001146200450a575b62003c76925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b816003840110620044bd579360069593819362003cdf936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015262003d896001600160a01b03606086015116620032e8565b9184519062003dd662003dbb62003dac60a089015160808a01511515956200287c565b60e0885101515f0b9062004da2565b905060ff60c0885101511660ff60c087510151169162005165565b91156200448c5781925b5f8063ffffffff875116604051602081019182526020815262003e038162002760565b51906108085afa9062003e1562002cd7565b91156200447a576020828051810103126200053e576001600160401b0362003e44602062003e6d940162003428565b60208801519116919015620044665760ff60a062003e669251015116620037bd565b9062003815565b602085015115620044175763ffffffff62003e8e8160608801511662005026565b16620f4240818101811162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c01511691016200287c565b0416906080870151908115155f146200440e5750905b602085015115620043c5576305f5e1008110156200437e57505f5b62003f2462003f0b60a0890151620037fc565b62003f1d895160a08b0151906200287c565b906200287c565b5f1981019190821162001a0e5762003f1d62003f539262003f4c8a5160a08c0151906200287c565b90620037dd565b606482028281046064148315171562001a0e578111620042df57505062003f84855160e0865101515f0b9062004da2565b92909562003faf6001600160401b03602088015116856001600160401b0360a08a01511691620038d2565b156200423b5791608093917f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91860a0856001600160401b036001600160a01b036040819b990151168a606084015116908a8401511515916040519162004014836200270b565b82526020820152828816604082015282871660608201528a81019182528481015f815262004118602086019384515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b03602052856060600260405f206001600160a01b0388511673ffffffffffffffffffffffffffffffffffffffff19825416178155600181016001600160a01b0360208a0151168154907bffffffffffffffff000000000000000000000000000000000000000060408c015160a01b169163ffffffff60e01b161717905501950151168619855416178455511515839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151569ff00000000000000000082549160481b169069ff00000000000000000019161790555194818b60408501511697818d6060870151169a87875197015160405197885260208801521660408601521660608401521688820152a4015116620041ae83826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001662003353565b60e06001600160401b03602084015116925101515f0b90803b156200053e57604051639c45c34b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160401b03909316602484015260448301939093525f90810b6064830152909182908183816084810162002f38565b94505050905062002f56925060208201516001600160401b03604051921682527f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c60206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b9350949350505060208201516001600160a01b0360608401511691606481019081811162001a0e5760630190811162001a0e5760407fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e9162002f569660648351920482526020820152a36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166060820152620046c4565b6001600160401b036305f5e0ff1982011162001a0e57620043be620043b86001600160401b036305f5e100936305f5e0ff190116620037fc565b6200286a565b0462003ef8565b6305f5e100811115620043da57505f62003ef8565b6001600160401b03816305f5e100031162001a0e57620043be620043b86001600160401b036305f5e10093840316620037fc565b90509062003edd565b63ffffffff6200442d8160608801511662005026565b16620f4240808281031162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c0151169103620037cf565b5062003e6660ff60a08951015116620037bd565b604051635cffc5fb60e11b8152600490fd5b6127108181031162001a0e576001600160401b03612710620044b383820383861662003815565b0416919262003de0565b926001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019162003c95565b5090845f5260205f20905f915b8183106200453257505090602062003c769282010162003c68565b602091935080600191548385880101520191019091839262004517565b6020925062003c7694915060ff191682840152151560051b82010162003c68565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162003b1d565b5090845f5260205f20905f915b818310620045da57505090602062003afe9282010162003af0565b6020919350806001915483858801015201910190918392620045bf565b6020925062003afe94915060ff191682840152151560051b82010162003af0565b506080810151909190156200464c5760246001600160a01b03604084015116604051906320a2097d60e11b82526004820152fd5b9080602062002f569201517f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b03604085015116604051908152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b6060906060810151905f6001600160a01b0380931692835f526020945f8051602062005bb78339815191528652604091825f2096835162004705816200270b565b845193620047138562002727565b85518a5491905f81620047268562002c80565b918282526001958887821691825f1462004d6b57505060011462004d2a575b62004756925094939403826200277c565b8652808b019184885193898586948484549283815201935f52845f20965f915b83600384011062004cde575085620047b998549484841062004cc4575b84841062004ca7575b5083831062004c8a575b50501062004c7b5750905003826200277c565b82850152600289015495836001600160401b039788811688880152871c16606086015260038a0154946080958581168783015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528252600660048b01549a60ff8585019c8d8b82169052891c1615158885015260058101546060850152015490878216868401528760a0840192881c168252868901976200485e868a511662003257565b1562004c0d57899a9b5f9a98999a508c612710620048958b519b8b6200488b60a083019e8f51906200287c565b9101519062003815565b04988a5199808b1162004c04575b508d8a8062004b2e575b620048c2620048d191620048e493516200287c565b60e08a5101515f0b9062004da2565b91909782878088511692511691620038d2565b1562004ae65750908c9695949392918a620049f9575b509260e0896200498f94620049c59b9997947fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b99976200496e906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6200497b8d82546200287c565b9055511696511694510151900b9062004da2565b9190928d84620049ca575b5050505050880151965116965192519351938493846040919493926060820195825260208201520152565b0390a4565b620049ee94620049e89188620049e08562002847565b169062003353565b62002ea6565b5f8080808d6200499a565b919293949596505087807f00000000000000000000000000000000000000000000000000000000000000001691875101511690803b156200053e578b5163f3fef3a360e01b81526001600160a01b03929092166004830152602482018a90525f908290604490829084905af1801562004adc578c948f91998098956200498f957fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b9995620049c59d62004ac0575b509497995094509950939597999a9b620048fa565b60e0945062004acf90620026f7565b6200496e5f945062004aab565b8b513d5f823e3d90fd5b9a50505050955050505093957f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c955084915062004b238162004e31565b0151945191168152a3565b62004b45908a8301518c858c51015116906200503f565b1562004b53575b8a620048ad565b509192939495968881999a50602492508389510151168c51928380926370a0823160e01b82528d7f00000000000000000000000000000000000000000000000000000000000000001660048301525afa90811562004adc57918e918e9a999897969594935f9162004bc7575b509962004b4c565b92505098508681813d831162004bfc575b62004be481836200277c565b810103126200053e57518c988e91620048d162004bbf565b503d62004bd8565b99505f620048a3565b505050509296509450839293015115155f1462004c405750505191516320a2097d60e11b81529116600482015260249150fd5b62002f56948294827f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d9401519451169051908152a262004e31565b60c01c81520185905f6200303b565b9091946001600160401b038560801c1681520193015f85620047a6565b956001600160401b03868495981c16815201940190858e6200479c565b6001600160401b0386168752958101959282019262004793565b95509360049296509284919388546001600160401b039283821683528d8483831c169084015260809382851c169083015260c01c8782015201960191019386948c9296938a9562004776565b50508c5f528186805f20865f915b85831062004d5157505062004756935082010162004745565b809192945054838588010152019101879086859362004d38565b60ff1916848201526200475694151560051b8401019150620047459050565b81811062004d96575050565b5f815560010162004d8a565b9190805f0b9081155f1462004dc05750506001600160401b03821690565b5f82131562004e035762004dd8915060ff16620037bd565b918215620037e857620038ce62004dfb82856001600160401b03940690620037cf565b9384620037dd565b505f0380805f0b0362001a0e57620038ce62004e2a6001600160401b039260ff16620037bd565b8462003815565b805161271062004e5862004e4c60a08501938451906200287c565b60808501519062003815565b0481518181115f146200501d5750905b8190602084019182519162004e8f60608701956001600160a01b039485885116906200503f565b1562005014575b8162004f61575b620049c5837f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9462004ed1858a516200287c565b9662004eeb828a51169860408c0199848b51169062003353565b62004f29828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62004f368782546200287c565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b84518316927f00000000000000000000000000000000000000000000000000000000000000008116803b156200053e5760405163f3fef3a360e01b81526001600160a01b03959095166004860152602485018490525f908590604490829084905af1908115620019c5577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f94620049c59262005002575b5093505062004e9d565b6200500d90620026f7565b5f62004ff8565b5f915062004e96565b90509062004e68565b90606463ffffffff8093160291821691820362001a0e57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa918215620019c5575f92620050f2575b5080821094851595620050b2575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080620050ac565b9091506020813d60201162005123575b8162005111602093836200277c565b810103126200053e5751905f6200509e565b3d915062005102565b51906001600160a01b03821682036200053e57565b519060ff821682036200053e57565b9060ff8091169116039060ff821162001a0e57565b919060ff821660ff82168181145f1462005180575050505090565b929391921015620051b857620038ce9192620051b0620051aa6001600160401b0395869362005150565b620037bd565b911662003815565b620038ce91620051d6620051aa6001600160401b0395869362005150565b9116620037dd565b9015620051fb57815115620051f1575090565b3b156200053e5790565b5080519081156200053e57602001fdfe60a080604052346100455733608052610961908161004a8239608051818181608b015281816101ac0152818161038e015281816104a20152818161051b015261069c0152f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f915f3560e01c908163435354d3146104d757816390a0827b1461047b5781639c45c34b14610469578163a703334f1461033c578163de1a324a146100d457508063e94b77c1146100b35763eba61c0e1461006d575f80fd5b346100af57816003193601126100af57602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b82346100d1576100ce6100c5366105b5565b9291909161068d565b80f35b80fd5b91905034610338576101009136600319018381126103345760a01361033057815167ffffffffffffffff939060a081018581118282101761031d5784526101196105a2565b91828252602435918215158303610319576020810192835263ffffffff9360443585811681036102ce578783015260643585811681036102ce5760608301526084356001600160a01b03928382168203610315576080015260a4358881168091036102ce5760c435918983168093036102ce5760e435936fffffffffffffffffffffffffffffffff85168095036102ce577f000000000000000000000000000000000000000000000000000000000000000016330361031557866101dd9116610663565b9451151581156103055782156102f5578a6102e557885196602088019616865288870152606086015260808501525f60a0850152600260c085015260e084015260e08352820194828610908611176102d25761026891602491868652630100000160e01b6101208301526102598251809261012485019061079c565b81010383810186520184610641565b73333333333333333333333333333333333333333392833b156102ce576102a5935f928385518097819582946317938e1360e01b845283016107bd565b03925af19081156102c557506102b9575080f35b6102c391506105fd565b005b513d5f823e3d90fd5b5f80fd5b604183634e487b7160e01b5f525260245ffd5b88516376d4929560e11b81528890fd5b88516313c0a8df60e01b81528890fd5b885163017461b760e71b81528890fd5b8a80fd5b8780fd5b604184634e487b7160e01b5f525260245ffd5b8380fd5b8480fd5b8280fd5b919050346103385760603660031901126103385781359067ffffffffffffffff928383168093036102ce576024356001600160a01b03948582168092036102ce576044359081168091036102ce5786957f000000000000000000000000000000000000000000000000000000000000000016330361046557610259946103fc9260249286519260208401928352878401526060830152606082526103df82610625565b85519687926280000360e11b60208501525180928585019061079c565b73333333333333333333333333333333333333333392833b15610334576104399385928385518097819582946317938e1360e01b845283016107bd565b03925af190811561045c575061044c5750f35b610455906105fd565b6100d15780f35b513d84823e3d90fd5b8580fd5b83346100d1576100ce6100c5366105b5565b91905034610338573660031901126100af57356001600160a01b038082168092036102ce577f00000000000000000000000000000000000000000000000000000000000000001633036100af576100ce906024359033906107e9565b919050346102ce57806003193601126102ce576104f26105a2565b91602435926fffffffffffffffffffffffffffffffff84168094036102ce576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102ce5761054a90610663565b9282519063ffffffff602083019516855283820152828152606081019381851067ffffffffffffffff8611176102d25761026891602491868652630100000b60e01b608083015261025982518092608485019061079c565b6004359063ffffffff821682036102ce57565b60809060031901126102ce576004356001600160a01b03811681036102ce579060243567ffffffffffffffff811681036102ce579060443590606435805f0b81036102ce5790565b67ffffffffffffffff811161061157604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761061157604052565b90601f8019910116810190811067ffffffffffffffff82111761061157604052565b9061271063ffffffff8093160191821161067957565b634e487b7160e01b5f52601160045260245ffd5b9092916001600160a01b0392837f00000000000000000000000000000000000000000000000000000000000000001633036102ce57805f0b80155f1461071c575050915b826106dd575b50505050565b67ffffffffffffffff732000000000000000000000000000000000000000941684018094116106795780610713941691166107e9565b5f8080806106d7565b5f81131561075c57506107319060ff166108f1565b8015610748578106810390811161067957916106d1565b634e487b7160e01b5f52601260045260245ffd5b90505f9391930380805f0b03610679576107789060ff166108f1565b9280938181810204149015176106d157634e487b7160e01b5f52601160045260245ffd5b5f5b8381106107ad5750505f910152565b818101518382015260200161079e565b604091602082526107dd815180928160208601526020868601910161079c565b601f01601f1916010190565b9190604051906020938483019363a9059cbb60e01b85526001600160a01b03809316602485015260448401526044835261082283610625565b169160405190604082019067ffffffffffffffff9383831085841117610611577f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564875f9586956040528181520152519082865af13d156108e4573d918211610611576108ac926040519261089f86601f19601f8401160185610641565b83523d5f8685013e610902565b80519182159182156108c4575b50509050156102ce57565b8092508193810103126102ce57015180151581036102ce57805f806108b9565b6108ac9260609250610902565b60ff16604d811161067957600a0a90565b901561091c57815115610913575090565b3b156102ce5790565b5080519081156102ce57602001fdfea2646970667358221220c909e7dbe656d7c973709e34eb673837a89b1ae88012e9262063134290e6f21f64736f6c634300081800336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00a2646970667358221220d7009235facd2afacf7dbd784aa03c97dd6444be46f4a33aa486147317b81a7764736f6c63430008180033000000000000000000000000002e76dc036a1eff1488ee5435ee66c6abf32674000000000000000000000000b88339cb7199b77e23db6e890353e22632ba630f" + "address": "0x2483711d260d346d8bc1dd69b6b5465dd18e84ea", + "initCode": "0x60c0346100d157601f615ba038819003918201601f19168301916001600160401b038311848410176100d55780849260409485528339810103126100d157610052602061004b836100e9565b92016100e9565b6001600160a01b0390911660805260a052604051615aa290816100fe8239608051818181610842015281816109820152818161143a015281816116c7015281816127bb015281816145ca015281816146ec01528181614a5c0152614c00015260a051818181610610015281816107740152818161191101526136c80152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036100d15756fe60806040526004361015610011575f80fd5b5f5f3560e01c806246912e146123be57806301ffc9a714612368578063037a06a41461211c57806304c73f60146120fe578063057f0370146120445780631f74a0b514611fd257806321081d3c14611f97578063248a9ca314611f575780632e748b2114611dfb5780632f2ff15d14611db0578063319adf9f146119e857806336568abe146119a357806337710e201461194e57806339fff098146118b75780633b1c6a01146115bc5780633cf3a025146115875780634265fe861461153c578063490e662f146115015780634b3b029b146114c6578063502a82e214611409578063521c98ba14610cd857806369b97ac714610cba57806379c7b60b14610c6557806379c7f28914610b6657806390a0827b14610b2d57806391d1485414610ad657806396cc2cfb14610882578063a217fddf14610866578063a4b672b614610822578063af5de6f914610798578063c55dae6314610754578063ccbedaec146104e9578063d06e28ed146103fc578063d547741f146103a8578063e38b73a91461038c578063ea0aaf241461032e578063eb84e7f21461021a5763ff3eae00146101bb575f80fd5b346102175760c0366003190112610217576101d536612562565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1005415610208576102059061492d565b80f35b63cd6d8f7d60e01b8252600482fd5b80fd5b50346102175760203660031901126102175760408160c09260a0835161023f8161250a565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051906102928261250a565b6001600160a01b03815416918281526001600160401b0360018301549281600260208501926001600160a01b038716845282604087019760a01c1687520154946001600160a01b036060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b503461021757602036600319011261021757602061038361034d612421565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b5034610217578060031936011261021757602060405160068152f35b5034610217576040366003190112610217576103f86004356103c8612437565b906103f36103ee825f525f516020615a4d5f395f51905f52602052600160405f20015490565b612be7565b613628565b5080f35b503461021757604036600319011261021757610416612421565b602435906fffffffffffffffffffffffffffffffff82168092036104da5761043c612b78565b8261044682613027565b63ffffffff6001600160a01b03608083015116915116813b156104da57829160448392604051948593849263435354d360e01b845260048401528960248401525af180156104de576104c5575b50506001600160a01b03167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b816104cf91612541565b6104da57825f610493565b8280fd5b6040513d84823e3d90fd5b50346102175760603660031901126102175780610504612421565b61050c61244d565b610514612463565b9061051d612aba565b610557836001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156106b2574361059a846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0360016105df856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b015416926001600160401b0382166106b7575b50506001600160401b038116610606575050f35b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001683525f516020615a2d5f395f51905f526020526001600160401b0360046040852001541690823b156106b25760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156104de576106a15750f35b816106ab91612541565b6102175780f35b505050fd5b6001600160a01b031684525f516020615a2d5f395f51905f526020526001600160401b03600460408620015416833b156107505760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152838160648183875af1908115610745578491156105f2578161073691612541565b61074157825f6105f2565b5050fd5b6040513d86823e3d90fd5b8480fd5b503461021757806003193601126102175760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346102175760203660031901126102175760206001600160a01b036107c46107bf612421565b612f58565b610b046040516107d685830182612541565b81815284810191614f298339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152610816607582612541565b51902016604051908152f35b503461021757806003193601126102175760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5034610217578060031936011261021757602090604051908152f35b50346102175760403660031901126102175761089c612421565b906108a5612b78565b6108ae82612d18565b6108b783613027565b926108cb60e083510151840b6024356148ac565b9460208401906108f46001600160401b03835116886001600160401b0360a08901511691613553565b15610aad57859650610936846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b610941848254612623565b90556001600160a01b038416807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051878152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b15610aa95760405163f3fef3a360e01b81526001600160a01b0383166004820152602481018690529088908290604490829084905af18015610a9e5785918991610a81575b50506001600160401b0391610a116001600160a01b0392608060e0960192848451169061308b565b511692511694510151850b93813b15610a7d57604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156104de576106a15750f35b8580fd5b81925090610a8e91612541565b610a9a5783875f6109e9565b8680fd5b6040513d8a823e3d90fd5b8780fd5b6377e88bc960e11b86526001600160a01b0384166004526001600160401b038716602452604486fd5b5034610217576040366003190112610217576001600160a01b036040610afa612437565b9260043581525f516020615a4d5f395f51905f526020522091165f52602052602060ff60405f2054166040519015158152f35b503461021757604036600319011261021757610205610b4a612421565b610b52612aba565b602435906001600160a01b0333911661308b565b50346102175760203660031901126102175760a0610bc4610b85612421565b610b8d612a90565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b03600160405192610bdb846124ef565b63ffffffff8154818116865260ff8160201c1615156020870152818160281c16604087015260481c1660608501520154166080820152610c6360405180926001600160a01b036080809263ffffffff815116855260208101511515602086015263ffffffff604082015116604086015263ffffffff6060820151166060860152015116910152565bf35b5034610217576020366003190112610217576020610383610c84612421565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b50346102175780600319360112610217576020604051620f42408152f35b50346102175760a036600319011261021757610cf2612421565b610cfa612479565b90610d0361248c565b6064356001600160401b038116810361075057608435916001600160401b0383168303610a7d57610d32612b29565b610d3a612999565b508580604051602081019063ffffffff8916825260208152610d5d604082612541565b519061080c5afa610d6c612a61565b90156113fa578051810160208101916020818303126113d6576020810151906001600160401b0382116113f65701906101009082900312610aa95760405191610db483612525565b60208201516001600160401b0381116113f65760209083010181601f820112156113f65789815191610de583612a46565b92610df36040519485612541565b80845284602082840101116104da578060208093018386015e83010152835260408201516001600160401b0381116113f6576020908301019080601f830112156113f6578151916001600160401b0383116113c2578260051b9060405193610e5e6020840186612541565b84526020808501928201019283116113f257602001905b8282106113da57505050602083015261010090610e94606082016130cc565b6040840152610ea560808201614e68565b6060840152610eb660a08201614e68565b6080840152610ec760c08201614e7c565b60a0840152610ed860e08201614e7c565b60c0840152015180880b8082036113d657610ef89160e0840152846134b2565b5060405191610f068361250a565b825263ffffffff87166020830152821515604083015260608201526001600160401b03831660808201526001600160401b03841660a08201526001600160a01b03851687525f516020615a2d5f395f51905f5260205260408720815180518051906001600160401b0382116113c25781908b610f828654612a0e565b601f8111611387575b5050602090601f8311600114611325578c9261131a575b50508160011b915f199060031b1c19161782555b6001820160208201518051906001600160401b03821161130657680100000000000000008211611306576020908c84548486558085106112ba575b505001918b5260208b20908b5b8160021c811061127557506003198116810380611219575b505050506001600160a01b03947f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd8224435397946001600160401b039460a06006868896600260809d9901888060408401511616891982541617815560608201517fffffffff0000000000000000000000000000000000000000ffffffffffffffff7bffffffffffffffffffffffffffffffffffffffff000000000000000083549260401b16911617905560038201908c808f83015116166001600160a01b0319835416178255848101519082547fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff000000000000000000000000000000000000000000001694891b16911617171790556111a76004820188806020880151161689198254161781556040860151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b60608401516005820155019185808c8301511616861984541617835501516fffffffffffffffff000000000000000082549160401b16906fffffffffffffffff0000000000000000191617905563ffffffff6040519a168a52151560208a01521660408801521660608601521692a280f35b928c938d5b8181106112375750505060021c015584848460a0611016565b909194602061126b6001926001600160401b03895116908560031b6001600160401b03809160031b9316831b921b19161790565b960192910161121e565b8c8d5b6004811061128d575083820155600101610ffe565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501611278565b8382876112e6945220600380870160021c820192601888831b16806112ed575b500160021c0190614896565b8c5f610ff1565b5f198501908154905f19908a03851b1c1690555f6112da565b634e487b7160e01b8c52604160045260248cfd5b015190505f80610fa2565b858d52818d209250601f1984168d5b81811061136f5750908460019594939210611357575b505050811b018255610fb6565b01515f1960f88460031b161c191690555f808061134a565b92936020600181928786015181550195019301611334565b602082886113b1945220601f850160051c810191602086106113b8575b601f0160051c0190614896565b8b5f610f8b565b90915081906113a4565b634e487b7160e01b8b52604160045260248bfd5b8880fd5b602080916113e7846130cc565b815201910190610e75565b8b80fd5b8980fd5b639b0c335d60e01b8752600487fd5b503461021757604036600319011261021757611423612421565b60243561142e612aba565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156114c25760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156104de576114ad575b505061020591339061308b565b816114b791612541565b6104da57825f6114a0565b5080fd5b503461021757806003193601126102175760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b503461021757806003193601126102175760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b50346102175760c03660031901126102175761155736612562565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541561020857610205906142b4565b5034610217576020366003190112610217576115a1612aba565b80808080600435335af16115b3612a61565b50156102175780f35b5034610217576060366003190112610217576115d6612437565b604435906001600160a01b0382169182810361186e576115f4612b78565b6115fd81612d18565b61160683612fa0565b610750576040810151156107505760016001600160401b03608083015116016001600160401b0381116118a35790859161164760e083510151840b826134b2565b509361167160208401926001600160401b03845116906001600160401b0360a08701511691613553565b1561186e576116b0816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b6116bb868254612623565b90556001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b156107505760405163f3fef3a360e01b81526001600160a01b0389166004820152602481018790529085908290604490829084905af1908115611898578591611883575b50506001600160401b039261174d60e08585511692510151860b876148ac565b509081611872575b5050505116926117bf60246040516001600160a01b036020820194169687855260408201526001606082015260608152611790608082612541565b6040519384916280000360e11b60208401525180918484015e810185838201520301601f198101835282612541565b7333333333333333333333333333333333333333333b156114c2578161180191604051809381926317938e1360e01b83526020600484015260248301906124cb565b0381837333333333333333333333333333333333333333335af180156104de57611859575b50506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b8161186391612541565b61186e57835f611826565b8380fd5b61187b92614cd4565b5f8080611755565b8161188d91612541565b61186e57835f61172d565b6040513d87823e3d90fd5b634e487b7160e01b86526011600452602486fd5b50346102175760e0366003190112610217576118d236612562565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1005415610208576001600160a01b036060820151166001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145f1461194157610205906142b4565b6102059060c435906136c5565b503461021757602036600319011261021757602061038361196d612421565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5034610217576040366003190112610217576119bd612437565b336001600160a01b038216036119d9576103f890600435613628565b63334bd91960e11b8252600482fd5b5034610217576020366003190112610217576001600160a01b03611a0a612421565b611a126129d7565b501681525f516020615a2d5f395f51905f52602052604081209060405190611a398261250a565b604051611a4581612525565b60405184548184611a5583612a0e565b8083529260018116908115611d915750600114611d50575b611a7992500382612541565b815260018401604051808260208294549384815201908652602086209286905b806003830110611cfe57611acf945491818110611ce4575b818110611cc7575b818110611caa575b10611c9c575b500382612541565b60208201526001600160a01b0360028501546001600160401b038116604084015260401c16606082015260038401546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c820b60e0820152825260048301549260208301906001600160401b038516825260ff604085019560401c161515855260066005820154916060860192835201549460808501926001600160401b03871684526001600160401b0360a087019760401c1687526040519560208752519460c06020880152611bb6865161010060e08a01526101e08901906124cb565b60208088015189830360df19016101008b015280518084529282019892910190835b818110611c7d57505050926001600160401b03809693899a969360e0878c610120866040819c0151169101528c6101406001600160a01b036060840151169101528c6101606001600160a01b036080840151169101528c61018060ff60a0840151169101528c6101a060ff60c0840151169101520151900b6101c08b0152511660408901525115156060880152516080870152511660a0850152511660c08301520390f35b82516001600160401b03168a526020998a019990920191600101611bd8565b60c01c81526020015f611ac7565b9260206001916001600160401b038560801c168152019301611ac1565b9260206001916001600160401b038560401c168152019301611ab9565b9260206001916001600160401b0385168152019301611ab1565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c6060820152019401920184929391611a99565b5090868552602085209085915b818310611d75575050906020611a7992820101611a6d565b6020919350806001915483858801015201910190918392611d5d565b60209250611a7994915060ff191682840152151560051b820101611a6d565b5034610217576040366003190112610217576103f8600435611dd0612437565b90611df66103ee825f525f516020615a4d5f395f51905f52602052600160405f20015490565b613581565b503461021757608036600319011261021757611e15612421565b611e1d61244d565b611e25612463565b90606435926fffffffffffffffffffffffffffffffff841680940361075057611e4c612b78565b84611e5682613027565b926001600160a01b0360808501511693843b156104da57611ee06101048492836001600160401b03806040519788968795636f0d192560e11b875260048701906001600160a01b036080809263ffffffff815116855260208101511515602086015263ffffffff604082015116604086015263ffffffff6060820151166060860152015116910152565b169a8b60a485015216988960c48401528b60e48401525af180156104de57611f42575b50506001600160a01b036040917f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c93835195865260208601521692a380f35b81611f4c91612541565b61075057845f611f03565b5034610217576020366003190112610217576020611f8f6004355f525f516020615a4d5f395f51905f52602052600160405f20015490565b604051908152f35b503461021757806003193601126102175760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503461021757606036600319011261021757611fec612421565b6024356001600160401b0381116104da5761200b90369060040161249b565b9092604435906001600160401b038211610217576020611f8f868686612034366004890161249b565b93909261203f612b78565b612630565b50346120fa5760403660031901126120fa5761205e612421565b9060243561206a612aba565b6001600160a01b03608061207d85613027565b015116803b156120fa576040516390a0827b60e01b81526001600160a01b038516600482015260248101839052905f908290604490829084905af180156120ef576120d9575b5061020591926001600160a01b0333911661308b565b61020592505f6120e891612541565b5f916120c3565b6040513d5f823e3d90fd5b5f80fd5b346120fa575f3660031901126120fa5760206040516305f5e1008152f35b346120fa5760a03660031901126120fa57612135612421565b61213d612479565b61214561248c565b9160643563ffffffff81168091036120fa576084359163ffffffff83168093036120fa5761217281612d18565b5061217b612b29565b6001600160a01b0360016121bf836001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b015416908115612317575b63ffffffff604051956121dc876124ef565b16948581526001600160a01b036001816020840199151595868b52604085019a888c52606086018a8152836080880193169c8d845263ffffffff6122508a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b985116926cffffffff00000000000000000068ffffffff000000000064ff000000008b549351151560201b16935160281b16935160481b16936cffffffff000000000000000000199168ffffffffffffffffff1916171617171785555116920191166001600160a01b03198254161790556122ca86612fa0565b156120fa577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9936080936001600160a01b03936040519788526020880152604087015260608601521692a3005b905061232281612f58565b604051610b048082018281106001600160401b03821117612354578291614f29833903905ff580156120ef57906121ca565b634e487b7160e01b5f52604160045260245ffd5b346120fa5760203660031901126120fa5760043563ffffffff60e01b81168091036120fa57602090637965db0b60e01b81149081156123ad575b506040519015158152f35b6301ffc9a760e01b149050826123a2565b346120fa5760403660031901126120fa5761241f6123da612421565b6001600160a01b036123ea61244d565b916123f3612aba565b165f525f516020615a2d5f395f51905f526020526001600160401b03600460405f200154163390612c34565b005b600435906001600160a01b03821682036120fa57565b602435906001600160a01b03821682036120fa57565b602435906001600160401b03821682036120fa57565b604435906001600160401b03821682036120fa57565b6024359063ffffffff821682036120fa57565b6044359081151582036120fa57565b9181601f840112156120fa578235916001600160401b0383116120fa576020808501948460051b0101116120fa57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60a081019081106001600160401b0382111761235457604052565b60c081019081106001600160401b0382111761235457604052565b61010081019081106001600160401b0382111761235457604052565b90601f801991011681019081106001600160401b0382111761235457604052565b60c09060031901126120fa576040519061257b8261250a565b81600435815260243560208201526044356001600160a01b03811681036120fa5760408201526064356001600160a01b03811681036120fa576060820152608435608082015260a060a435910152565b91908110156125db5760051b0190565b634e487b7160e01b5f52603260045260245ffd5b906001600160401b03809116911601906001600160401b03821161260f57565b634e487b7160e01b5f52601160045260245ffd5b9190820180921161260f57565b9194935f935f968281036120fa57612678856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156120fa57929061268b85612d18565b91608061269787613027565b01916001600160a01b03835116946126be60208601966001600160401b03885116906130e0565b965f975b818d1061292e575b50508a1592506129239150505743612712866001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160401b0384168061272c575b50505050505050565b6127416127649560e0855101515f0b906134b2565b6001600160401b03869792975116906001600160401b0360a08701511691613553565b1561290457506127a4856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6127af858254612623565b90556001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166001600160a01b03861690803b156120fa5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101879052905f908290604490829084905af180156120ef576128d8575b506001600160a01b038261284c876001600160401b03948460e09751169061308b565b511693511691510151850b93823b15610a7d57604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156104de576128c3575b8080808080612723565b6128ce828092612541565b61021757806128b9565b6001600160401b0391975060e0926128f85f6001600160a01b0393612541565b5f989093509150612829565b6001600160a01b03866377e88bc960e11b5f521660045260245260445ffd5b505f96505050505050565b61293d8d83879f9b96976125cb565b359061294a8a85886125cb565b356001600160401b03811681036120fa5761297392898d926001600160a01b038b5116926131c2565b9490911561299157600191612987916125ef565b98019b93926126c2565b989c506126ca565b604051906129a682612525565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b604051906129e48261250a565b5f60a0836129f0612999565b81528260208201528260408201528260608201528260808201520152565b90600182811c92168015612a3c575b6020831014612a2857565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612a1d565b6001600160401b03811161235457601f01601f191660200190565b3d15612a8b573d90612a7282612a46565b91612a806040519384612541565b82523d5f602084013e565b606090565b60405190612a9d826124ef565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a602052604090205460ff1615612af257565b63e2517d3f60e01b5f52336004527f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f60245260445ffd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff1615612b6157565b63e2517d3f60e01b5f52336004525f60245260445ffd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd602052604090205460ff1615612bb057565b63e2517d3f60e01b5f52336004527f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef660245260445ffd5b805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0333165f5260205260ff60405f20541615612c1e5750565b63e2517d3f60e01b5f523360045260245260445ffd5b6024906001600160401b03612ca3939481604051936001600160a01b03602086019816885216604084015216606082015260608152612c74608082612541565b6040519384916280000360e11b60208401525180918484015e81015f838201520301601f198101835282612541565b7333333333333333333333333333333333333333333b156120fa575f612ce591604051809381926317938e1360e01b83526020600484015260248301906124cb565b0381837333333333333333333333333333333333333333335af180156120ef57612d0c5750565b5f612d1691612541565b565b6001600160a01b0390612d296129d7565b50165f525f516020615a2d5f395f51905f5260205260405f2060405191612d4f8361250a565b60405191612d5c83612525565b6040518154815f612d6c83612a0e565b8083529260018116908115612f395750600114612ef8575b612d9092500382612541565b8352600181019360405180602087549182815201965f5260205f20905f915b816003840110612eac5797612df09284926001600160a01b039798999a5491818110611ce457818110611cc757818110611caa5710611c9c57500382612541565b60208601528160028401546001600160401b038116604088015260401c1660608601526001600160401b0360066003850154946080880197858716895260ff8760a01c1660a082015260c081019660ff8160a81c16885260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015293511615159081612e9e575b50156120fa57565b60ff9150511615155f612e96565b97600160806004928b546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c6060820152019901920191612daf565b5090835f5260205f20905f915b818310612f1d575050906020612d9092820101612d84565b6020919350806001915483858801015201910190918392612f05565b60209250612d9094915060ff191682840152151560051b820101612d84565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152612f8d604882612541565b51902090565b519081151582036120fa57565b5f80916040516001600160a01b03602082019216825260208152612fc5604082612541565b51906108105afa612fd4612a61565b9015613018576020818051810103126120fa5760405190602082018281106001600160401b038211176123545760405261301090602001612f93565b809152151590565b6313dd7ccd60e31b5f5260045ffd5b61303390610b8d612a90565b906001600160a01b0360016040519361304b856124ef565b63ffffffff8154818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156120fa57565b60405163a9059cbb60e01b60208201526001600160a01b03929092166024830152604480830193909352918152612d16916130c7606483612541565b614b17565b51906001600160401b03821682036120fa57565b5f919082916001600160401b03604051916001600160a01b03602084019416845216604082015260408152613116606082612541565b51906108015afa613125612a61565b9015613193576060818051810103126120fa576040519060608201918083106001600160401b03841117612354576001600160401b0392604052604061318c6060613172602086016130cc565b948585526131818482016130cc565b6020860152016130cc565b9101521690565b639d2c8fcb60e01b5f5260045ffd5b906001600160401b03809116911603906001600160401b03821161260f57565b95929190939495805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20916001600160a01b0383541695861561343a576002840180549860ff8a60481c1661342b576001600160a01b03600187015491166001600160a01b03821603613418576001600160401b039060a01c166001600160401b038a1660ff8b60401c165f146133c1575080926001600160401b03811682115f146133b957613279916131a2565b985b6132858a8c6125ef565b9a6001600160401b038c166001600160401b038516116133a8575069010000000000000000009069ff00000000000000000019161790556132c88160019a6131a2565b966001600160401b0360206001600160a01b036132ec60e0875101515f0b8d6134b2565b50981694015116833b156120fa5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152915f908390606490829084905af19081156120ef577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4392604092613398575b506001600160a01b0360018187541696015416956001600160401b038351921682526020820152a4565b5f6133a291612541565b5f61336e565b5f9b508b9a50985050505050505050565b50505f613279565b9092809a93806001600160401b038316105f1461340d576133ec916133e5916131a2565b809b6125ef565b816001600160401b038216115f1461340657505b9161327b565b9050613400565b50506133ec5f6133e5565b8463358d72d160e01b5f5260045260245ffd5b63f7348a7960e01b5f5260045ffd5b631a40316d60e01b5f5260045ffd5b60ff16604d811161260f57600a0a90565b9190820391821161260f57565b8115613471570490565b634e487b7160e01b5f52601260045260245ffd5b90620f4240820291808304620f4240149015171561260f57565b8181029291811591840414171561260f57565b9190805f0b80155f146134cf5750506001600160401b0382169190565b5f8113156134fa57506134e76134f69160ff16613449565b6001600160401b03841661349f565b9190565b9050607f19811461260f576001600160401b039261351f613528925f0360ff16613449565b93849116612623565b5f19810190811161260f5761354f613548846001600160401b0393613467565b938461349f565b1690565b6135796001600160401b039293613573849361356e81614bb0565b6130e0565b946125ef565b169116101590565b805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260ff60405f205416155f1461362257805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260405f20600160ff198254161790556001600160a01b03339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260ff60405f2054165f1461362257805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260405f2060ff1981541690556001600160a01b03339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b907f0000000000000000000000000000000000000000000000000000000000000000604083016136fe6001600160a01b03825116612fa0565b1561423f576001600160a01b038216805f525f516020615a2d5f395f51905f5260205260405f2090604051946137338661250a565b60405161373f81612525565b6040518454815f61374f83612a0e565b808352926001811690811561422057506001146141df575b61377392500382612541565b815260018401604051808260208294549384815201905f5260205f20925f905b80600383011061418d576137c5945491818110611ce457818110611cc757818110611caa5710611c9c57500382612541565b60208201526001600160a01b0360028501546001600160401b038116604084015260401c16606082015260038401546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528652600660048401549360ff60208901956001600160401b038116875260401c1615156040890152600581015460608901520154966001600160401b03881660808801526001600160401b0360a088019860401c168852606081016001600160a01b038151165f525f516020615a2d5f395f51905f5260205260405f2095604051966138b08861250a565b6040516138bc81612525565b6040518254815f6138cc83612a0e565b808352926001811690811561416e575060011461412d575b6138f092500382612541565b81526040516001830180548083525f9182526020808320849391840192905b8160038401106140db5754918181106140c1575b8181106140a4575b818110614087575b10614079575b50036139459082612541565b602082015260028201546001600160401b038116604083015260401c6001600160a01b0316606082015260038201546001600160a01b03811660808301528060a01c60ff1660a08301528060a81c60ff1660c083015260b01c5f0b60e0820152885260048101546001600160401b03811660208a015260401c60ff161515604089015260058101546060890152600601546001600160401b038116608089015260401c6001600160401b031660a088015281516001600160a01b0316613a0a90613027565b9383519060a08501918251996080870191825115159b613a2991612623565b8d5160e001515f0b613a3a916148ac565b90508d5160c0015160ff16825160c0015160ff1690613a5892614e9e565b9a1561404b578a925b5f8063ffffffff8b51166040516020810191825260208152613a84604082612541565b51906108085afa613a93612a61565b901561403c576020818051810103126120fa57898f9360ff60a0613ae19260206001600160401b03613ac982613ae799016130cc565b16950197885115155f14614031575051015116613449565b9061349f565b825115613fe75763ffffffff613b028160608d015116614bd6565b16620f4240019081620f42401161260f57613b38620f424091613ae16001600160401b039463ffffffff8f604001511690612623565b0416908351908115155f14613fdf5750915b5115613f9e576305f5e100811015613f5e57505f5b613b7f613b6c8651613485565b613b798a51885190612623565b90612623565b5f1981019190821161260f57613b79613ba592613b9f8b51895190612623565b90613467565b606480830283810482148415171561260f578211613ee45750505085518c5160e001515f0b613bd3916148ac565b9d8e8b9c929c516001600160401b031691516001600160401b031690613bf892613553565b15613e94579c8a9b9c9d85516001600160a01b03169387516001600160a01b0316935115159360405195613c2b8761250a565b86526020860190815260408601926001600160401b03169485845260608701926001600160401b0316968784526080810191825260a08101925f845260208d019586515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f2092516001600160a01b03166001600160a01b031683546001600160a01b0319161783556001830191516001600160a01b03166001600160a01b031682546001600160a01b0319161782555181549060a01b7bffffffffffffffff000000000000000000000000000000000000000016907fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1617905560020192516001600160401b03166001600160401b03166001600160401b0319845416178355511515613d8190839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151581549060481b69ff000000000000000000169069ff00000000000000000019161790555194516001600160a01b031695516001600160a01b03169651935160405194855260208501526001600160401b031660408401526060830152608082015260a07f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91891a4608001516001600160a01b03169283613e229261308b565b516001600160401b0316935160e001515f0b93813b156120fa57604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f92830b60648301529091908290608490829084905af180156120ef57612d0c5750565b5050505050945095505092505081612d16947f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c602080870151926001600160401b0360405191168152a35261492d565b999c5099509c50505050509450809650602091500151906001600160a01b038451169281810180911161260f575f19810190811161260f57811561347157612d16977fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e92604092049082519182526020820152a3526142b4565b6305f5e0ff19016001600160401b03811161260f57613f98613f8d6001600160401b036305f5e1009316613485565b6305f5e0ff90612623565b04613b5f565b6305f5e100811115613fb157505f613b5f565b6305f5e100036001600160401b03811161260f57613f98613f8d6001600160401b036305f5e1009316613485565b905091613b4a565b63ffffffff613ffb8160608d015116614bd6565b16620f42400390620f4240821161260f57613b38620f424091613ae16001600160401b039463ffffffff8f60400151169061345a565b905051015116613449565b635cffc5fb60e11b5f5260045ffd5b8261271003612710811161260f576127106140706001600160401b0392838f1661349f565b04169a92613a61565b60c01c81526020015f613939565b9260206001916001600160401b038560801c168152019301613933565b9260206001916001600160401b038560401c16815201930161392b565b9260206001916001600160401b0385168152019301613923565b935090916001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019084939261390f565b5090845f5260205f20905f915b8183106141525750509060206138f0928201016138e4565b602091935080600191548385880101520191019091839261413a565b602092506138f094915060ff191682840152151560051b8201016138e4565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c6060820152019401920184929391613793565b5090865f5260205f20905f915b81831061420457505090602061377392820101613767565b60209193508060019154838588010152019101909183926141ec565b6020925061377394915060ff191682840152151560051b820101613767565b6080840151909392501561426a576001600160a01b038351166320a2097d60e11b5f5260045260245ffd5b6001600160a01b0390612d16937f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60208481870151935116604051908152a216606082015261492d565b6060810151906001600160a01b035f921691825f525f516020615a2d5f395f51905f5260205260405f206040516142ea8161250a565b6040516142f681612525565b6040518354815f61430683612a0e565b80835292600181169081156148775750600114614836575b61432a92500382612541565b815260018301604051808260208294549384815201905f5260205f20925f905b8060038301106147e45761437c945491818110611ce457818110611cc757818110611caa5710611c9c57500382612541565b60208201526001600160a01b0360028401546001600160401b038116604084015260401c16606082015260038301546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528152600660048301549260ff60208401946001600160401b038116865260401c1615156040840152600581015460608401520154906001600160401b03821660808201526001600160401b0360a082019260401c168252604085019361444b6001600160a01b03865116612fa0565b156147745785519161271061447461446960a08a0195865190612623565b60808a01519061349f565b049383519480861161476c575b50846146ca575b6144c06144a7614499878b51612623565b60e0855101515f0b906148ac565b919092826001600160401b03808b511692511691613553565b1561467c5750846145c0575b61455d906001600160401b037fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c976145348c6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b61453f898254612623565b905551169360e06001600160a01b038a511694510151900b906148ac565b919092836145a4575b5050505061459f6001600160a01b0360208701519551169551915192604051938493846040919493926060820195825260208201520152565b0390a4565b6145b26145b794828c614cd4565b612c34565b5f808080614566565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001695863b156120fa5760405163f3fef3a360e01b81526001600160a01b038b16600482015260248101879052965f908890604490829084905af19687156120ef577fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c97614659575b5095506144cc565b61455d929194505f61466a91612541565b6001600160401b035f94919250614651565b9793505050507f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c9350602092508291506146b58161492d565b0151926001600160401b0360405191168152a3565b6146d9858a60208b0151614bf1565b614488576040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031660048201529094506020816024818c5afa9081156120ef575f9161473a575b5093614488565b90506020813d602011614764575b8161475560209383612541565b810103126120fa57515f614733565b3d9150614748565b94505f614481565b505050506080820151909250156147a2576001600160a01b038251166320a2097d60e11b5f5260045260245ffd5b612d16917f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b0381850151935116604051908152a261492d565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192018492939161434a565b5090855f5260205f20905f915b81831061485b57505090602061432a9282010161431e565b6020919350806001915483858801015201910190918392614843565b6020925061432a94915060ff191682840152151560051b82010161431e565b8181106148a1575050565b5f8155600101614896565b9190805f0b9081155f146148c95750506001600160401b03821690565b5f821315614904576148de915060ff16613449565b9182156134715761354f6148fd82856001600160401b0394069061345a565b9384613467565b505f0380805f0b0361260f5761354f6149276001600160401b039260ff16613449565b8461349f565b805161271061495061494560a0850193845190612623565b60808501519061349f565b0481518181115f14614b0f5750905b8190602084019161498283519460608701956001600160a01b0387511690614bf1565b15614b08575b80614a52575b7f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9161459f6001600160a01b03806149c7858a51612623565b966149df828a51169860408c0199848b51169061308b565b614a1c828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b614a27878254612623565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916001600160a01b0385511692803b156120fa5760405163f3fef3a360e01b81526001600160a01b03949094166004850152602484018390525f908490604490829084905af19283156120ef577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f93614af8575b50915061498e565b5f614b0291612541565b5f614af0565b505f614988565b90509061495f565b906001600160a01b03614b7892165f8060405193614b36604086612541565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602086015260208151910182855af1614b72612a61565b91614efd565b8051908115918215614b8e575b5050156120fa57565b81925090602091810103126120fa576020614ba99101612f93565b5f80614b85565b6001600160401b03166001609d1b01806001609d1b1161260f576001600160a01b031690565b63ffffffff60649116029063ffffffff821691820361260f57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa9182156120ef575f92614ca0575b5080821094851595614c61575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080614c5b565b9091506020813d602011614ccc575b81614cbc60209383612541565b810103126120fa5751905f614c4e565b3d9150614caf565b9091906001600160401b038316614e4d576001600160a01b03919250166040515f806020830163095ea7b360e01b8152736b9e773128f453f5c2c60935ee2de2cbc5390a24602485015285604485015260448452614d33606485612541565b83519082865af1614d42612a61565b81614e16575b5080614e0c575b15614db9575b5050736b9e773128f453f5c2c60935ee2de2cbc5390a243b156120fa5760405190630acb7f4b60e21b8252600482015263ffffffff60248201525f8160448183736b9e773128f453f5c2c60935ee2de2cbc5390a245af180156120ef57612d0c5750565b614e05916130c760405163095ea7b360e01b6020820152736b9e773128f453f5c2c60935ee2de2cbc5390a2460248201525f604482015260448152614dff606482612541565b82614b17565b5f80614d55565b50813b1515614d4f565b8051801592508215614e2b575b50505f614d48565b81925090602091810103126120fa576020614e469101612f93565b5f80614e23565b6001600160a01b03614e61612d1694614bb0565b911661308b565b51906001600160a01b03821682036120fa57565b519060ff821682036120fa57565b9060ff8091169116039060ff821161260f57565b9160ff811660ff83168181145f14614eb7575050505090565b6001600160401b039492911115614ee557614ede614ed961354f948693614e8a565b613449565b911661349f565b614ef6614ed9859261354f95614e8a565b9116613467565b91925015614f1957815115614f10575090565b3b156120fa5790565b5080519081156120fa57602001fdfe60a080604052346100455733608052610aba908161004a82396080518181816086015281816101b80152818161039a015281816104d50152818161054c01526106a40152f35b5f80fdfe60806040526004361015610011575f80fd5b5f5f3560e01c8063435354d31461050a57806390a0827b146104a55780639c45c34b146100ad578063a703334f1461033e578063de1a324a146100cb578063e94b77c1146100ad5763eba61c0e14610067575f80fd5b346100aa57806003193601126100aa5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b80fd5b50346100aa576100c86100bf366105ee565b92919091610699565b80f35b50346100aa57366003190161010081126103265760a0136100aa5760405160a0810181811067ffffffffffffffff82111761032a5760405261010b6105db565b91828252602435918215158303610326576020810192835260443563ffffffff8116810361032257604082015260643563ffffffff81168103610322576060820152608435906001600160a01b0382168203610322576080015260a43567ffffffffffffffff81168091036103265760c4359067ffffffffffffffff82168092036103225760e435946fffffffffffffffffffffffffffffffff861680960361031e576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361031e5763ffffffff6101ed911661066c565b9351151592811561030f57821561030057916102819391600493506040519263ffffffff60208501971687526040840152606083015260808201525f945f60a0830152600260c083015260e082015260e0815261024c61010082610636565b6020604051948592630100000160e01b83850152518091602485015e8201018281015f8152500301601f198101835282610636565b7333333333333333333333333333333333333333333b156102fc575f6102bb91604051809381926317938e1360e01b8352600483016108ff565b0381837333333333333333333333333333333333333333335af180156102f1576102e3575080f35b6102ef91505f90610636565b005b6040513d5f823e3d90fd5b5f80fd5b6313c0a8df60e01b8152600490fd5b63017461b760e71b8152600490fd5b8380fd5b8280fd5b5080fd5b634e487b7160e01b83526041600452602483fd5b50346100aa5760603660031901126100aa578060043567ffffffffffffffff811680910361049c57602435906001600160a01b0382168092036104a15760443567ffffffffffffffff811680910361049f576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361049f5761041f91600491604051916020830195865260408301526060820152606081526103eb608082610636565b60206040519485926280000360e11b83850152518091602485015e820101828101868152500301601f198101835282610636565b7333333333333333333333333333333333333333333b1561049c578161045991604051809381926317938e1360e01b8352600483016108ff565b0381837333333333333333333333333333333333333333335af18015610491576104805750f35b8161048a91610636565b6100aa5780f35b6040513d84823e3d90fd5b50fd5b505b5050fd5b50346100aa5760403660031901126100aa576004356001600160a01b038116809103610326576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163303610326576100c890602435903390610929565b50346102fc5760403660031901126102fc576105246105db565b602435906fffffffffffffffffffffffffffffffff82168092036102fc576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102fc5760046105806102819261066c565b926040519063ffffffff60208301951685526040820152604081526105a6606082610636565b6020604051948592630100000b60e01b83850152518091602485015e8201018281015f8152500301601f198101835282610636565b6004359063ffffffff821682036102fc57565b60809060031901126102fc576004356001600160a01b03811681036102fc579060243567ffffffffffffffff811681036102fc579060443590606435805f0b81036102fc5790565b90601f8019910116810190811067ffffffffffffffff82111761065857604052565b634e487b7160e01b5f52604160045260245ffd5b63ffffffff6127109116019063ffffffff821161068557565b634e487b7160e01b5f52601160045260245ffd5b926001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102fc57805f0b9081155f1461088f5750505b816106e357505050565b67ffffffffffffffff16806108685750906001600160a01b03166040515f806020830163095ea7b360e01b8152736b9e773128f453f5c2c60935ee2de2cbc5390a2460248501528560448501526044845261073f606485610636565b83519082865af161074e610a1a565b81610839575b508061082f575b156107d7575b5050736b9e773128f453f5c2c60935ee2de2cbc5390a243b156102fc5760405190630acb7f4b60e21b8252600482015263ffffffff60248201525f8160448183736b9e773128f453f5c2c60935ee2de2cbc5390a245af180156102f1576107c7575b505b565b5f6107d191610636565b5f6107c3565b6108289161082360405163095ea7b360e01b6020820152736b9e773128f453f5c2c60935ee2de2cbc5390a2460248201525f60448201526044815261081d606482610636565b82610978565b610978565b5f80610761565b50813b151561075b565b805180159250821561084e575b50505f610754565b6108619250602080918301019101610960565b5f80610846565b6001609d1b0191826001609d1b11610685576001600160a01b03806107c594169116610929565b5f8213156108d0576108a4915060ff16610a09565b80156108bc57808306830392831161068557506106d9565b634e487b7160e01b5f52601260045260245ffd5b505f0380805f0b03610685576108e89060ff16610a09565b8281810291818304149015171561068557506106d9565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b6107c5926001600160a01b036040519363a9059cbb60e01b6020860152166024840152604483015260448252610823606483610636565b908160209103126102fc575180151581036102fc5790565b906001600160a01b036109d992165f8060405193610997604086610636565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602086015260208151910182855af16109d3610a1a565b91610a59565b80519081159182156109ef575b5050156102fc57565b610a029250602080918301019101610960565b5f806109e6565b60ff16604d811161068557600a0a90565b3d15610a54573d9067ffffffffffffffff82116106585760405191610a49601f8201601f191660200184610636565b82523d5f602084013e565b606090565b91925015610a7557815115610a6c575090565b3b156102fc5790565b5080519081156102fc57602001fdfea26469706673582212208abf3b44431f4c61bdd7aa5253477149e50113939030a9816d0109335ec8e08364736f6c634300081e00336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0002dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800a26469706673582212203f720c6ea86c631282ab7132c9a7dc291402b5cc506847c33ef5f144f0f78ca764736f6c634300081e0033000000000000000000000000bc217096db9eb6d2782c1d9e725d462077a4d1f6000000000000000000000000b88339cb7199b77e23db6e890353e22632ba630f" } ], "isFixedGasLimit": false }, { - "hash": "0x2d31d35b8f8ed4e1f7f84b15c97c9c0e95798883948b4bcf349229e1ab62d82d", + "hash": "0x160e0989eab0766946934f34fa6a417808e6fb5065c5099ff4b6d71f88b46537", "transactionType": "CALL", "contractName": "DonationBox", - "contractAddress": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "contractAddress": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6", "function": "transferOwnership(address)", - "arguments": ["0x83e245941BefbDe29682dF068Bcda006A804eb0C"], + "arguments": ["0xb63c02e60C05F05975653edC83F876C334E07C6d"], "transaction": { "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", - "to": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", - "gas": "0x9922", + "to": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6", + "gas": "0x9925", "value": "0x0", - "input": "0xf2fde38b00000000000000000000000083e245941befbde29682df068bcda006a804eb0c", - "nonce": "0x245", + "input": "0xf2fde38b000000000000000000000000b63c02e60c05f05975653edc83f876c334e07c6d", + "nonce": "0x29e", "chainId": "0x3e7" }, "additionalContracts": [], @@ -72,43 +72,43 @@ "receipts": [ { "status": "0x1", - "cumulativeGasUsed": "0xc11a2", + "cumulativeGasUsed": "0x2b13f3", "logs": [ { - "address": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "address": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6", "topics": [ "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" ], "data": "0x", - "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", - "blockNumber": "0x13d47a9", - "blockTimestamp": "0x692f466c", - "transactionHash": "0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d", + "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", + "blockNumber": "0x1453728", + "blockTimestamp": "0x693714f0", + "transactionHash": "0xf8b97d99b6c68adf40a93b675eee70389c3c3d621d018dceb796c2cf58949678", "transactionIndex": "0x3", - "logIndex": "0x22", + "logIndex": "0x65", "removed": false } ], - "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000040000400000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000880000000000000000000000000000000000000000008020000001000000000000000000000000000000000000000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000800000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001000000000000000000000000000000000000020000000000000000000802000000000000000000000000040000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000", "type": "0x2", - "transactionHash": "0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d", + "transactionHash": "0xf8b97d99b6c68adf40a93b675eee70389c3c3d621d018dceb796c2cf58949678", "transactionIndex": "0x3", - "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", - "blockNumber": "0x13d47a9", - "gasUsed": "0x3f377", - "effectiveGasPrice": "0x6052340", + "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", + "blockNumber": "0x1453728", + "gasUsed": "0x3d4eb", + "effectiveGasPrice": "0x16a5a32d", "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", "to": null, - "contractAddress": "0x002e76dc036a1eff1488ee5435ee66c6abf32674" + "contractAddress": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6" }, { "status": "0x1", - "cumulativeGasUsed": "0x79e7fa", + "cumulativeGasUsed": "0x9a83e7", "logs": [ { - "address": "0x83e245941befbde29682df068bcda006a804eb0c", + "address": "0xb63c02e60c05f05975653edc83f876c334e07c6d", "topics": [ "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", "0x5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef6", @@ -116,16 +116,16 @@ "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x", - "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", - "blockNumber": "0x13d47a9", - "blockTimestamp": "0x692f466c", - "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", + "blockNumber": "0x1453728", + "blockTimestamp": "0x693714f0", + "transactionHash": "0x7ba663e21e7399474f6ce30190460110c285ff703b1bc1f661936550fd4997a5", "transactionIndex": "0x4", - "logIndex": "0x23", + "logIndex": "0x66", "removed": false }, { - "address": "0x83e245941befbde29682df068bcda006a804eb0c", + "address": "0xb63c02e60c05f05975653edc83f876c334e07c6d", "topics": [ "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", "0x880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f", @@ -133,16 +133,16 @@ "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x", - "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", - "blockNumber": "0x13d47a9", - "blockTimestamp": "0x692f466c", - "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", + "blockNumber": "0x1453728", + "blockTimestamp": "0x693714f0", + "transactionHash": "0x7ba663e21e7399474f6ce30190460110c285ff703b1bc1f661936550fd4997a5", "transactionIndex": "0x4", - "logIndex": "0x24", + "logIndex": "0x67", "removed": false }, { - "address": "0x83e245941befbde29682df068bcda006a804eb0c", + "address": "0xb63c02e60c05f05975653edc83f876c334e07c6d", "topics": [ "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -150,65 +150,65 @@ "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" ], "data": "0x", - "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", - "blockNumber": "0x13d47a9", - "blockTimestamp": "0x692f466c", - "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", + "blockNumber": "0x1453728", + "blockTimestamp": "0x693714f0", + "transactionHash": "0x7ba663e21e7399474f6ce30190460110c285ff703b1bc1f661936550fd4997a5", "transactionIndex": "0x4", - "logIndex": "0x25", + "logIndex": "0x68", "removed": false } ], - "logsBloom": "0x00000004000000000800000000000000080000000000000000000080000000000100000000000000000000000000000000000000001000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000020000400000000000000800000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000800000000000000000000000000000000100000000000020100001000000000000000000000000000100000004000000000000000001000000", + "logsBloom": "0x00000004000000000800000000000000080002000000000000000080000000000100000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000020000400000000000000800000000000000000000000000040000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000008000000000000000000000001000000000000000000000000000800000000000000000000000000000000100000000000020100001000000000000000000000000000100000000000000000000000000000000", "type": "0x2", - "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", + "transactionHash": "0x7ba663e21e7399474f6ce30190460110c285ff703b1bc1f661936550fd4997a5", "transactionIndex": "0x4", - "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", - "blockNumber": "0x13d47a9", - "gasUsed": "0x6dd658", - "effectiveGasPrice": "0x6052340", + "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", + "blockNumber": "0x1453728", + "gasUsed": "0x6f6ff4", + "effectiveGasPrice": "0x16a5a32d", "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", "to": null, - "contractAddress": "0x83e245941befbde29682df068bcda006a804eb0c" + "contractAddress": "0xb63c02e60c05f05975653edc83f876c334e07c6d" }, { "status": "0x1", - "cumulativeGasUsed": "0x7a56d9", + "cumulativeGasUsed": "0x9af2c7", "logs": [ { - "address": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "address": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6", "topics": [ "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", - "0x00000000000000000000000083e245941befbde29682df068bcda006a804eb0c" + "0x000000000000000000000000b63c02e60c05f05975653edc83f876c334e07c6d" ], "data": "0x", - "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", - "blockNumber": "0x13d47a9", - "blockTimestamp": "0x692f466c", - "transactionHash": "0x2d31d35b8f8ed4e1f7f84b15c97c9c0e95798883948b4bcf349229e1ab62d82d", + "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", + "blockNumber": "0x1453728", + "blockTimestamp": "0x693714f0", + "transactionHash": "0x160e0989eab0766946934f34fa6a417808e6fb5065c5099ff4b6d71f88b46537", "transactionIndex": "0x5", - "logIndex": "0x26", + "logIndex": "0x69", "removed": false } ], - "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000001000000000000100000000000000000000000000000000000000000000000000000000000000000000000040000400000000000000000000000200000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000880000000000000000000000000000000000000000008000000001000000000000000000000000000000000000000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000800000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001000000000000000000000000000000000000000000000020000000000002000000000000000000000000040000410040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000", "type": "0x2", - "transactionHash": "0x2d31d35b8f8ed4e1f7f84b15c97c9c0e95798883948b4bcf349229e1ab62d82d", + "transactionHash": "0x160e0989eab0766946934f34fa6a417808e6fb5065c5099ff4b6d71f88b46537", "transactionIndex": "0x5", - "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", - "blockNumber": "0x13d47a9", - "gasUsed": "0x6edf", - "effectiveGasPrice": "0x6052340", + "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", + "blockNumber": "0x1453728", + "gasUsed": "0x6ee0", + "effectiveGasPrice": "0x16a5a32d", "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", - "to": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "to": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6", "contractAddress": null } ], "libraries": [], "pending": [], "returns": {}, - "timestamp": 1764705991008, + "timestamp": 1765217588351, "chain": 999, - "commit": "4b103fe" + "commit": "5a4c408" } diff --git a/script/mintburn/cctp/createSponsoredDeposit.sol b/script/mintburn/cctp/createSponsoredDeposit.sol index ea8af75f3..73c9052aa 100644 --- a/script/mintburn/cctp/createSponsoredDeposit.sol +++ b/script/mintburn/cctp/createSponsoredDeposit.sol @@ -85,10 +85,10 @@ contract CreateSponsoredDeposit is DeploymentUtils { SponsoredCCTPInterface.SponsoredCCTPQuote memory quote = SponsoredCCTPInterface.SponsoredCCTPQuote({ sourceDomain: config.get("cctpDomainId").toUint32(), // Arbitrum CCTP domain destinationDomain: 19, // HyperEVM CCTP domain - mintRecipient: address(0x83e245941BefbDe29682dF068Bcda006A804eb0C).toBytes32(), // Destination handler contract + mintRecipient: address(0xb63c02e60C05F05975653edC83F876C334E07C6d).toBytes32(), // Destination handler contract amount: 10000, // 100 USDC (6 decimals) burnToken: config.get("usdc").toAddress().toBytes32(), // USDC on Arbitrum - destinationCaller: address(0x83e245941BefbDe29682dF068Bcda006A804eb0C).toBytes32(), // Destination handler contract + destinationCaller: address(0xb63c02e60C05F05975653edC83F876C334E07C6d).toBytes32(), // Destination handler contract maxFee: 1, // 0 max fee minFinalityThreshold: 1000, // Minimum finality threshold nonce: keccak256(abi.encodePacked(block.timestamp, deployer, vm.getNonce(deployer))), // Generate nonce @@ -97,8 +97,8 @@ contract CreateSponsoredDeposit is DeploymentUtils { maxUserSlippageBps: 0, // 4% max user slippage (400 basis points) finalRecipient: address(0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D).toBytes32(), // Final recipient finalToken: address(0xb88339CB7199b77E23DB6E890353E22632Ba630f).toBytes32(), // USDC on HyperEVM - executionMode: uint8(SponsoredCCTPInterface.ExecutionMode.ArbitraryActionsToEVM), // DirectToCore mode - actionData: actionDataEmpty // Empty for DirectToCore mode + executionMode: uint8(SponsoredCCTPInterface.ExecutionMode.DirectToCore), // DirectToCore mode + actionData: emptyActionData // Empty for DirectToCore mode }); console.log("SponsoredCCTPQuote created:"); From bc56c41e4544c0a7e720cda2deb0a70e1ace1b08 Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Mon, 8 Dec 2025 14:44:02 -0500 Subject: [PATCH 45/47] Added interface source Signed-off-by: Faisal Usmani --- contracts/external/interfaces/ICoreDepositWallet.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/external/interfaces/ICoreDepositWallet.sol b/contracts/external/interfaces/ICoreDepositWallet.sol index fb55df436..b92a4c9e4 100644 --- a/contracts/external/interfaces/ICoreDepositWallet.sol +++ b/contracts/external/interfaces/ICoreDepositWallet.sol @@ -20,6 +20,7 @@ pragma solidity ^0.8.0; /** * @title ICoreDepositWallet * @notice Interface for the core deposit wallet + * @dev Source: https://developers.circle.com/cctp/coredepositwallet-contract-interface#deposit-function */ interface ICoreDepositWallet { /** From c507c9eab8281f736d5b40741b63c9421c1b2e7e Mon Sep 17 00:00:00 2001 From: Faisal Usmani Date: Mon, 8 Dec 2025 16:25:32 -0500 Subject: [PATCH 46/47] undo deploy changes Signed-off-by: Faisal Usmani --- .../999/run-latest.json | 154 +++++++++--------- broadcast/deployed-addresses.json | 6 +- broadcast/deployed-addresses.md | 4 +- 3 files changed, 82 insertions(+), 82 deletions(-) diff --git a/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json b/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json index e3500d80e..24f6cc66c 100644 --- a/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json +++ b/broadcast/114DeploySponsoredCCTPDstPeriphery.sol/999/run-latest.json @@ -1,68 +1,68 @@ { "transactions": [ { - "hash": "0xf8b97d99b6c68adf40a93b675eee70389c3c3d621d018dceb796c2cf58949678", + "hash": "0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d", "transactionType": "CREATE", "contractName": "DonationBox", - "contractAddress": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6", + "contractAddress": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", "function": null, "arguments": null, "transaction": { "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", - "gas": "0x4fb31", + "gas": "0x522e7", "value": "0x0", - "input": "0x608080604052346059575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610320908161005e8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c908163715018a6146102265781638da5cb5b1461020457508063f2fde38b146101845763f3fef3a314610048575f80fd5b3461013c57604036600319011261013c576004356001600160a01b03811680910361013c5761007561028a565b6040515f806020830163a9059cbb60e01b81523360248501526024356044850152604484526100a560648561029d565b604051936100b460408661029d565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020860152519082865af13d15610177573d9067ffffffffffffffff821161016357604051610126949092610118601f8201601f19166020018561029d565b83523d5f602085013e6102bf565b8051908115918215610140575b50501561013c57005b5f80fd5b819250906020918101031261013c5760200151801515810361013c575f80610133565b634e487b7160e01b5f52604160045260245ffd5b91610126926060916102bf565b3461013c57602036600319011261013c576004356001600160a01b03811680910361013c576101b161028a565b801561013c576001600160a01b035f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b3461013c575f36600319011261013c576020906001600160a01b035f54168152f35b3461013c575f36600319011261013c5761023e61028a565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361013c57565b90601f8019910116810190811067ffffffffffffffff82111761016357604052565b919250156102db578151156102d2575090565b3b1561013c5790565b50805190811561013c57602001fdfea26469706673582212201599abe8d67ef129ea7b740727c3050bd5ff3fc0efd05707667feeec0709c1f864736f6c634300081e0033", - "nonce": "0x29c", + "input": "0x6080806040523461005a575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3610344908161005f8239f35b5f80fdfe608060409080825260049081361015610016575f80fd5b5f3560e01c908163715018a61461026e5781638da5cb5b1461024c57508063f2fde38b146101d45763f3fef3a31461004c575f80fd5b346101545781600319360112610154578035916001600160a01b0383168093036101545760249261007b6102d2565b8151916020830163a9059cbb60e01b815233868501528535604485015260448452608084019367ffffffffffffffff94818110868211176101c25760c08201818110878211176101b0578452602090527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152515f9182919082865af1923d1561019f573d9181831161018d57805195601f8401601f19908116603f011687019283118784101761017b575052835261013e93503d5f602085013e6102e5565b8051908115918215610158575b50501561015457005b5f80fd5b819250906020918101031261015457602001518015158103610154575f8061014b565b60418891634e487b7160e01b5f52525ffd5b86604187634e487b7160e01b5f52525ffd5b5050915061013e92506060916102e5565b88604189634e487b7160e01b5f52525ffd5b87604188634e487b7160e01b5f52525ffd5b503461015457602036600319011261015457356001600160a01b03808216809203610154576102016102d2565b8115610154575f548273ffffffffffffffffffffffffffffffffffffffff198216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b34610154575f366003190112610154576020906001600160a01b035f54168152f35b34610154575f366003190112610154576102866102d2565b5f6001600160a01b03815473ffffffffffffffffffffffffffffffffffffffff1981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b6001600160a01b035f5416330361015457565b90156102ff578151156102f6575090565b3b156101545790565b50805190811561015457602001fdfea2646970667358221220c55691de465342d56d68c31160f5d7d661422ef39ebccaa5223872c823ad6d6964736f6c63430008180033", + "nonce": "0x243", "chainId": "0x3e7" }, "additionalContracts": [], "isFixedGasLimit": false }, { - "hash": "0x7ba663e21e7399474f6ce30190460110c285ff703b1bc1f661936550fd4997a5", + "hash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", "transactionType": "CREATE", "contractName": "SponsoredCCTPDstPeriphery", - "contractAddress": "0xb63c02e60c05f05975653edc83f876c334e07c6d", + "contractAddress": "0x83e245941befbde29682df068bcda006a804eb0c", "function": null, "arguments": [ "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64", "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", - "0xbC217096db9EB6d2782c1d9E725D462077a4d1f6", + "0x002E76DC036A1efF1488ee5435eE66C6aBF32674", "0xb88339CB7199b77E23DB6E890353E22632Ba630f", "0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba" ], "transaction": { "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", - "gas": "0x90de56", + "gas": "0x8ec9d8", "value": "0x0", - "input": "0x610100346102b357601f61851338819003918201601f19168301916001600160401b0383118484101761029f5780849260a0946040528339810103126102b357610048816102b7565b610054602083016102b7565b91610061604082016102b7565b6100796080610072606085016102b7565b93016102b7565b60015f556040519091615ba08083016001600160401b0381118482101761029f57604092849261291384396001600160a01b039081168252861660208201520301905ff08015610294576001600160a01b03166080527f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef65f8181525f5160206184f35f395f51905f526020527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bace80549082905590915f5160206184b35f395f51905f528380a47f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f5f8181525f5160206184f35f395f51905f526020527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49b80549082905590915f5160206184b35f395f51905f528380a460a05260e0526001600160a01b0390811660c0527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0080546001600160a01b031916929091169190911790556107087fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0155610228336102cb565b506040516125be9081610355823960805181818161020e015261151d015260a0518181816106e30152818161169301526122f8015260c0518181816107cb01528181610b6c0152610e80015260e05181818161095c01528181610cbf01528181610d790152610f5c0152f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b51906001600160a01b03821682036102b357565b6001600160a01b0381165f9081525f5160206184d35f395f51905f52602052604090205460ff1661034f576001600160a01b03165f8181525f5160206184d35f395f51905f5260205260408120805460ff191660011790553391907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b505f9056fe60806040526004361015610030575b361561002e573461002a5761002236611036565b602081519101f35b5f80fd5b005b5f3560e01c806301ffc9a71461018f57806309cfd6751461018a5780631b1062b81461018557806321081d3c14610180578063238ac9331461017b578063248a9ca3146101765780632561efb214610171578063277e661d1461016c5780632f2ff15d1461016757806336568abe14610162578063490e662f1461015d5780634b3b029b146101585780634f7d9d2e14610153578063657cad8a1461014e5780636c19e783146101495780638c73eb041461014457806391d148541461013f57806393de31191461013a578063a217fddf14610135578063c55dae6314610130578063d547741f1461012b5763feb617240361000e576109cd565b610980565b61093d565b610923565b61084e565b6107ef565b6107ac565b610707565b6106c4565b610688565b61064e565b610614565b6105cc565b61057a565b610524565b6104d8565b610499565b610454565b61041a565b610347565b6101ef565b3461002a57602036600319011261002a5760043563ffffffff60e01b811680910361002a57602090637965db0b60e01b81149081156101d4575b506040519015158152f35b6301ffc9a760e01b1490505f6101c9565b5f91031261002a57565b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b634e487b7160e01b5f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761026257604052565b610232565b90601f8019910116810190811067ffffffffffffffff82111761026257604052565b6040519061029860c083610267565b565b60405190610298608083610267565b6040519061029861020083610267565b60405190610298606083610267565b60405190610298604083610267565b67ffffffffffffffff811161026257601f01601f191660200190565b9291926102ff826102d7565b9161030d6040519384610267565b82948184528183011161002a578281602093845f960137010152565b9080601f8301121561002a57816020610344933591016102f3565b90565b3461002a57606036600319011261002a5760043567ffffffffffffffff811161002a57610378903690600401610329565b60243567ffffffffffffffff811161002a57610398903690600401610329565b906044359167ffffffffffffffff831161002a576103bd6103ef933690600401610329565b916103c6611054565b60017fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10055610b3d565b5f7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1005560015f555f80f35b3461002a575f36600319011261002a5760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b3461002a575f36600319011261002a5760206001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f005416604051908152f35b3461002a57602036600319011261002a5760206104d06004355f525f5160206125695f395f51905f52602052600160405f20015490565b604051908152f35b3461002a57602036600319011261002a576104f1611054565b6104f96119cd565b6004357fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f015560015f55005b3461002a57602036600319011261002a5760043567ffffffffffffffff811161002a5761055f61055a6020923690600401610329565b611a70565b6040519015158152f35b6001600160a01b0381160361002a57565b3461002a57604036600319011261002a5761002e60243560043561059d82610569565b6105c76105c2825f525f5160206125695f395f51905f52602052600160405f20015490565b611a1c565b611b24565b3461002a57604036600319011261002a576004356024356105ec81610569565b336001600160a01b038216036106055761002e91611bd6565b63334bd91960e11b5f5260045ffd5b3461002a575f36600319011261002a5760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b3461002a575f36600319011261002a5760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b3461002a575f36600319011261002a5760207fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0154604051908152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57602036600319011261002a576001600160a01b0360043561072c81610569565b610734611054565b61073c6119cd565b167fffffffffffffffffffffffff00000000000000000000000000000000000000007fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f005416177fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f005560015f555f80f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57604036600319011261002a57602060ff61084260243560043561081682610569565b5f525f5160206125695f395f51905f52845260405f20906001600160a01b03165f5260205260405f2090565b54166040519015158152f35b3461002a57604036600319011261002a5760043567ffffffffffffffff811161002a5761087f903690600401610329565b60243567ffffffffffffffff811161002a5761089f903690600401610329565b6108a7611054565b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd602052604090205460ff16156108ec576108e691610e51565b60015f55005b63e2517d3f60e01b5f52336004527f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef660245260445ffd5b3461002a575f36600319011261002a5760206040515f8152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57604036600319011261002a5761002e6024356004356109a382610569565b6109c86105c2825f525f5160206125695f395f51905f52602052600160405f20015490565b611bd6565b3461002a57602036600319011261002a576004355f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f02602052602060ff60405f2054166040519015158152f35b9081602091031261002a5751801515810361002a5790565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9091610a6e61034493604084526040840190610a33565b916020818403910152610a33565b6040513d5f823e3d90fd5b906020610344928181520190610a33565b634e487b7160e01b5f52601160045260245ffd5b5f19810191908211610aba57565b610a98565b91908203918211610aba57565b634e487b7160e01b5f52602160045260245ffd5b60c09093929193610b398160e081019660a0809180518452602081015160208501526001600160a01b0360408201511660408501526001600160a01b036060820151166060850152608081015160808501520151910152565b0152565b906020610b5f9160405180938192630afd9fa560e31b83528660048401610a57565b03815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af1908115610e4c575f91610e2d575b5015610e1e5760405163277e661d60e01b815260208180610bc08560048301610a87565b0381305afa5f9181610ded575b50610bd757505050565b15610de957610be8610bf191611177565b919092836112a1565b80610d9e575b610c05826060850151610abf565b91610100840151610c74610c1d6101808701516114c0565b8415610d7457610c64610c346101a08901516114c0565b915b8615610d6d57610140890151945b610c4c610289565b98895260208901526001600160a01b03166040880152565b6001600160a01b03166060860152565b608084015260a083015280610d39575b15610cf757610cf2600160ff84610ca96101c06101e061029898015192015160ff1690565b90610cb261029a565b9586526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166020870152604086015216146060830152565b6115ac565b610d31610160610d3693015191610d2360405193849263073ffe1360e31b602085015260248401610ae0565b03601f198101835282610267565b611512565b50565b506101c08201600160ff610d4e835160ff1690565b1614908115610d5e575b50610c84565b5160ff1660021490505f610d58565b5f94610c44565b610c647f000000000000000000000000000000000000000000000000000000000000000091610c36565b610de4610dd76101008501515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b805460ff19166001179055565b610bf7565b5050565b610e1091925060203d602011610e17575b610e088183610267565b810190610a1b565b905f610bcd565b503d610dfe565b6368c2a52360e11b5f5260045ffd5b610e46915060203d602011610e1757610e088183610267565b5f610b9c565b610a7c565b906020610e739160405180938192630afd9fa560e31b83528660048401610a57565b03815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af1908115610e4c575f91611017575b5015610d365760405163277e661d60e01b815260208180610ed48560048301610a87565b0381305afa5f9181610ff6575b50610eea575050565b15610d3657610f197f1f46a1a2dc661ca8c3c13ebcec8d8dbefdf24d5f9c9b5fe909f1b8036c719e7e91611177565b610ff1610100830191610f57610dd784515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b610fc17f000000000000000000000000000000000000000000000000000000000000000091610fba610180870195610fb26060610f9489516114c0565b990198610fa2858b51610abf565b906001600160a01b038816611c80565b5195516114c0565b9551610abf565b9060405194859485909493926001600160a01b039081606094608085019885521660208401521660408201520152565b0390a1565b61101091925060203d602011610e1757610e088183610267565b905f610ee1565b611030915060203d602011610e1757610e088183610267565b5f610eb0565b610d3161104d91611045611054565b36905f6102f3565b9060015f55565b60025f541461002a5760025f55565b61106b6102a9565b905f82525f60208301525f60408301525f60608301525f60808301525f60a08301525f60c08301525f60e08301525f6101008301525f6101208301525f6101408301525f6101608301525f6101808301525f6101a08301525f6101c083015260606101e0830152565b81601f8201121561002a578051906110eb826102d7565b926110f96040519485610267565b8284526020838301011161002a57815f9260208093018386015e8301015290565b9190916101008184031261002a5780519260208201519260408301519260608101519260808201519260a08301519260c081015160ff8116810361002a579260e082015167ffffffffffffffff811161002a5761034492016110d4565b906111e2611183611063565b9261119a61119082611cfd565b63ffffffff168552565b6111b36111a682611d1c565b63ffffffff166020860152565b6111bc81611d3c565b60a08501526111da6111cd82611d2c565b63ffffffff1660e0860152565b805190611f26565b906111ec82611d4c565b60408401526111fa82611d5c565b606084015261120882611d6c565b608084015261121682611d7c565b60c084015261126061124561123661122d85611d8c565b94805190611f80565b6020808251830101910161111a565b6101e08c999394959697989901526101c08b019060ff169052565b6101a0890152610180880152610160870152610140860152610120850152610100840152565b9060018201809211610aba57565b91908201809211610aba57565b61142e6001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f005416926112de835163ffffffff1690565b602084015163ffffffff16611369604086015191610d236060880151608089015160a08a015160c08b01519161131b60e08d015163ffffffff1690565b93604051988997602089019b8c96929363ffffffff95919998948660e09894816101008c019d168b521660208a015260408901526060880152608087015260a086015260c085015216910152565b51902093610100840194610d2361142587516101208801976114068951610d2361014084015193610160810151906101808101516101a0820151906101e06113b66101c085015160ff1690565b9301516020815191012093604051988997602089019b8c9490989796929360ff9460e0979361010088019b8852602088015260408701526060860152608085015260a08401521660c08201520152565b5190206040805160208101958652908101919091529182906060820190565b51902090611fad565b9182611473575b508161143f575090565b61146d9150517fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f015490611294565b42111590565b6114b99192506114ae6114b591515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b5460ff1690565b1590565b905f611435565b8060a01c6114d4576001600160a01b031690565b6379ec0ed760e11b5f5260045ffd5b3d1561150d573d906114f4826102d7565b916115026040519384610267565b82523d5f602084013e565b606090565b5f80916020815191017f00000000000000000000000000000000000000000000000000000000000000005af46115466114e3565b901561154f5790565b602081519101fd5b6102989092919260c081019360a0809180518452602081015160208501526001600160a01b0360408201511660408501526001600160a01b036060820151166060850152608081015160808501520151910152565b6115b4611dac565b506115cc604082015160208082518301019101611e05565b90602081016115f46115e86115e883516001600160a01b031690565b6001600160a01b031690565b6040516370a0823160e01b815230600482015290602090829060249082905afa908115610e4c575f916119ae575b5061163e6115e86115e86060865101516001600160a01b031690565b6040516370a0823160e01b81523060048201529490602090869060249082905afa948515610e4c575f9561198d575b506001600160a01b036116d561168d6115e886516001600160a01b031690565b926116bd7f000000000000000000000000000000000000000000000000000000000000000094858951519161207e565b8651606001513091906001600160a01b03169061227c565b91166116e884516001600160a01b031690565b9185515191803b1561002a5761171a935f809460405196879586948593633a5be8cb60e01b8552309160048601611ef8565b03925af18015610e4c57611973575b506117416115e86115e884516001600160a01b031690565b6040516370a0823160e01b81523060048201529190602090839060249082905afa918215610e4c575f92611952575b50036118b257610d3692506117a261178f82516001600160a01b031690565b6060845101906001600160a01b03169052565b815151905b6117ba82845160a08151910151906123ee565b60a084510152818351528251907fb88fc27be67e678ffb77faf8f8bb00d39b66b4845e4f7ec1e623b0f15abd52136001600160a01b03611806602085015193516001600160a01b031690565b9461183f611820606087519701516001600160a01b031690565b9183604051948594169816968360209093929193604081019481520152565b0390a48051606082015115611874575051604051632132ff4360e11b602082015290610d31908290610d239060248301611557565b6040517fff3eae000000000000000000000000000000000000000000000000000000000060208201529150610d31908290610d239060248301611557565b6118ce6115e86115e860608551016001600160a01b0390511690565b6040516370a0823160e01b81523060048201529390602090859060249082905afa908115610e4c57610d36945f92611921575b508082106119185761191291610abf565b906117a7565b50505f906117a7565b61194491925060203d60201161194b575b61193c8183610267565b810190611ee9565b905f611901565b503d611932565b61196c91925060203d60201161194b5761193c8183610267565b905f611770565b806119815f61198793610267565b806101e5565b5f611729565b6119a791955060203d60201161194b5761193c8183610267565b935f61166d565b6119c7915060203d60201161194b5761193c8183610267565b5f611622565b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff1615611a0557565b63e2517d3f60e01b5f52336004525f60245260445ffd5b805f525f5160206125695f395f51905f5260205260ff611a503360405f20906001600160a01b03165f5260205260405f2090565b541615611a5a5750565b63e2517d3f60e01b5f523360045260245260445ffd5b610298815110611b1f57611a8b611a8682611d9c565b6114c0565b6001600160a01b0330911603611b1f578051806094108160941802811891828203918211610aba57611b0c9282611236926020611aca611afc966102d7565b93611ad86040519586610267565b838552611ae4846102d7565b8583019390601f19013685370101905e805190611f80565b50509594509250505060a01c1590565b9081611b16575090565b60a01c15905090565b505f90565b805f525f5160206125695f395f51905f5260205260ff611b588360405f20906001600160a01b03165f5260205260405f2090565b5416611bd057805f525f5160206125695f395f51905f52602052611b908260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916600117905533916001600160a01b0316907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f5160206125695f395f51905f5260205260ff611c0a8360405f20906001600160a01b03165f5260205260405f2090565b541615611bd057805f525f5160206125695f395f51905f52602052611c438260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916905533916001600160a01b0316907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b916001600160a01b036040519263a9059cbb60e01b5f521660045260245260205f60448180865af160015f5114811615611cde575b60409190915215611cc35750565b635274afe760e01b5f526001600160a01b031660045260245ffd5b6001811516611cf4573d15833b15151616611cb5565b503d5f823e3d90fd5b6008815110611d0d576008015190565b632d0483c560e21b5f5260045ffd5b600c815110611d0d57600c015190565b6090815110611d0d576090015190565b608c815110611d0d57608c015190565b6044815110611d0d576044015190565b6064815110611d0d576064015190565b6024815110611d0d576024015190565b60a4815110611d0d5760a4015190565b60c4815110611d0d5760c4015190565b60d8815110611d0d5760d8015190565b6040519060c0820182811067ffffffffffffffff821117610262576040525f60a0838281528260208201528260408201528260608201528260808201520152565b67ffffffffffffffff81116102625760051b60200190565b60208183031261002a5780519067ffffffffffffffff821161002a57019080601f8301121561002a57815191611e3a83611ded565b92611e486040519485610267565b80845260208085019160051b8301019183831161002a5760208101915b838310611e7457505050505090565b825167ffffffffffffffff811161002a578201906040828703601f19011261002a5760405190611ea382610246565b6020830151611eb181610569565b825260408301519167ffffffffffffffff831161002a57611eda886020809695819601016110d4565b83820152815201920191611e65565b9081602091031261002a575190565b9061034494936080936001600160a01b03809316845260208401521660408201528160608201520190610a33565b908151908180821091180218806094108160941802811891828203918211610aba576020611f53836102d7565b93611f616040519586610267565b838552611f6d846102d7565b8583019390601f19013685370101905e90565b9081519081808210911802188060e4108160e41802811891828203918211610aba576020611f53836102d7565b90611fb8838261248d565b600581959295101561207957159384612063575b508315611fda575b50505090565b5f935090610d236120128594936040519283916020830195630b135d3f60e11b87526024840152604060448401526064830190610a33565b51915afa61201e6114e3565b81612055575b81612033575b505f8080611fd4565b905060208180518101031261002a5760200151630b135d3f60e11b145f61202a565b905060208151101590612024565b6001600160a01b0384811691161493505f611fcc565b610acc565b905f806001600160a01b0361210b95946040519582602088019663a9059cbb60e01b88521660248801526044870152604486526120bc606487610267565b1692604051946120cd604087610267565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020870152519082855af16121056114e3565b9161253d565b8051908115918215612121575b50501561002a57565b6121349250602080918301019101610a1b565b5f80612118565b9061214582611ded565b6121526040519182610267565b8281528092612163601f1991611ded565b015f5b81811061217257505050565b60405190606082019180831067ffffffffffffffff841117610262576020926040525f81526060838201525f604082015282828601015201612166565b80518210156121c35760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b6020815260608101918051926040602084015283518091526080830190602060808260051b8601019501915f905b82821061222b575050505090604060206103449301519101906001600160a01b03169052565b90919295602080600192607f198982030185528951906001600160a01b038251168152604080612268858501516060878601526060850190610a33565b930151910152980192019201909291612205565b9291835161229161228c82611286565b61213b565b925f5b828110612362575060405163ef8738d360e01b60208201526001600160a01b0391821660248201529083166044820152939450610d2393610344939261235392909161233791906122e681606481018a565b6122ee6102b9565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681529060208201525f604082015261233082876121af565b52846121af565b506123406102c8565b9283526001600160a01b03166020830152565b604051928391602083016121d7565b806123806123726001938a6121af565b51516001600160a01b031690565b602061238c838b6121af565b5101516123a961239a6102b9565b6001600160a01b039093168352565b60208201525f60408201526123be82886121af565b526123c981876121af565b5001612294565b81156123da570490565b634e487b7160e01b5f52601260045260245ffd5b818101809111610aba57670de0b6b3a7640000820291808304670de0b6b3a76400001490151715610aba57808201809211610aba575f198201918211610aba57612437916123d0565b670de0b6b3a76400000390670de0b6b3a76400008211610aba57670de0b6b3a7640000808202908282041482151715610aba578261248361247e6103449561248894611294565b610aac565b6123d0565b610abf565b9060418151145f146124b9576124b591602082015190606060408401519301515f1a906124c2565b9091565b50505f90600290565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411612532576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15610e4c575f516001600160a01b0381161561252a57905f90565b505f90600190565b505050505f90600390565b9192501561255957815115612550575090565b3b1561002a5790565b50805190811561002a57602001fdfe02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800a2646970667358221220a6c072dd21a55a10b5685da20ea8a228aeae229a429258b65acadb2705eaa3b864736f6c634300081e003360c0346100d157601f615ba038819003918201601f19168301916001600160401b038311848410176100d55780849260409485528339810103126100d157610052602061004b836100e9565b92016100e9565b6001600160a01b0390911660805260a052604051615aa290816100fe8239608051818181610842015281816109820152818161143a015281816116c7015281816127bb015281816145ca015281816146ec01528181614a5c0152614c00015260a051818181610610015281816107740152818161191101526136c80152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036100d15756fe60806040526004361015610011575f80fd5b5f5f3560e01c806246912e146123be57806301ffc9a714612368578063037a06a41461211c57806304c73f60146120fe578063057f0370146120445780631f74a0b514611fd257806321081d3c14611f97578063248a9ca314611f575780632e748b2114611dfb5780632f2ff15d14611db0578063319adf9f146119e857806336568abe146119a357806337710e201461194e57806339fff098146118b75780633b1c6a01146115bc5780633cf3a025146115875780634265fe861461153c578063490e662f146115015780634b3b029b146114c6578063502a82e214611409578063521c98ba14610cd857806369b97ac714610cba57806379c7b60b14610c6557806379c7f28914610b6657806390a0827b14610b2d57806391d1485414610ad657806396cc2cfb14610882578063a217fddf14610866578063a4b672b614610822578063af5de6f914610798578063c55dae6314610754578063ccbedaec146104e9578063d06e28ed146103fc578063d547741f146103a8578063e38b73a91461038c578063ea0aaf241461032e578063eb84e7f21461021a5763ff3eae00146101bb575f80fd5b346102175760c0366003190112610217576101d536612562565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1005415610208576102059061492d565b80f35b63cd6d8f7d60e01b8252600482fd5b80fd5b50346102175760203660031901126102175760408160c09260a0835161023f8161250a565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051906102928261250a565b6001600160a01b03815416918281526001600160401b0360018301549281600260208501926001600160a01b038716845282604087019760a01c1687520154946001600160a01b036060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b503461021757602036600319011261021757602061038361034d612421565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b5034610217578060031936011261021757602060405160068152f35b5034610217576040366003190112610217576103f86004356103c8612437565b906103f36103ee825f525f516020615a4d5f395f51905f52602052600160405f20015490565b612be7565b613628565b5080f35b503461021757604036600319011261021757610416612421565b602435906fffffffffffffffffffffffffffffffff82168092036104da5761043c612b78565b8261044682613027565b63ffffffff6001600160a01b03608083015116915116813b156104da57829160448392604051948593849263435354d360e01b845260048401528960248401525af180156104de576104c5575b50506001600160a01b03167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b816104cf91612541565b6104da57825f610493565b8280fd5b6040513d84823e3d90fd5b50346102175760603660031901126102175780610504612421565b61050c61244d565b610514612463565b9061051d612aba565b610557836001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156106b2574361059a846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0360016105df856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b015416926001600160401b0382166106b7575b50506001600160401b038116610606575050f35b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001683525f516020615a2d5f395f51905f526020526001600160401b0360046040852001541690823b156106b25760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156104de576106a15750f35b816106ab91612541565b6102175780f35b505050fd5b6001600160a01b031684525f516020615a2d5f395f51905f526020526001600160401b03600460408620015416833b156107505760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152838160648183875af1908115610745578491156105f2578161073691612541565b61074157825f6105f2565b5050fd5b6040513d86823e3d90fd5b8480fd5b503461021757806003193601126102175760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346102175760203660031901126102175760206001600160a01b036107c46107bf612421565b612f58565b610b046040516107d685830182612541565b81815284810191614f298339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152610816607582612541565b51902016604051908152f35b503461021757806003193601126102175760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5034610217578060031936011261021757602090604051908152f35b50346102175760403660031901126102175761089c612421565b906108a5612b78565b6108ae82612d18565b6108b783613027565b926108cb60e083510151840b6024356148ac565b9460208401906108f46001600160401b03835116886001600160401b0360a08901511691613553565b15610aad57859650610936846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b610941848254612623565b90556001600160a01b038416807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051878152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b15610aa95760405163f3fef3a360e01b81526001600160a01b0383166004820152602481018690529088908290604490829084905af18015610a9e5785918991610a81575b50506001600160401b0391610a116001600160a01b0392608060e0960192848451169061308b565b511692511694510151850b93813b15610a7d57604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156104de576106a15750f35b8580fd5b81925090610a8e91612541565b610a9a5783875f6109e9565b8680fd5b6040513d8a823e3d90fd5b8780fd5b6377e88bc960e11b86526001600160a01b0384166004526001600160401b038716602452604486fd5b5034610217576040366003190112610217576001600160a01b036040610afa612437565b9260043581525f516020615a4d5f395f51905f526020522091165f52602052602060ff60405f2054166040519015158152f35b503461021757604036600319011261021757610205610b4a612421565b610b52612aba565b602435906001600160a01b0333911661308b565b50346102175760203660031901126102175760a0610bc4610b85612421565b610b8d612a90565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b03600160405192610bdb846124ef565b63ffffffff8154818116865260ff8160201c1615156020870152818160281c16604087015260481c1660608501520154166080820152610c6360405180926001600160a01b036080809263ffffffff815116855260208101511515602086015263ffffffff604082015116604086015263ffffffff6060820151166060860152015116910152565bf35b5034610217576020366003190112610217576020610383610c84612421565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b50346102175780600319360112610217576020604051620f42408152f35b50346102175760a036600319011261021757610cf2612421565b610cfa612479565b90610d0361248c565b6064356001600160401b038116810361075057608435916001600160401b0383168303610a7d57610d32612b29565b610d3a612999565b508580604051602081019063ffffffff8916825260208152610d5d604082612541565b519061080c5afa610d6c612a61565b90156113fa578051810160208101916020818303126113d6576020810151906001600160401b0382116113f65701906101009082900312610aa95760405191610db483612525565b60208201516001600160401b0381116113f65760209083010181601f820112156113f65789815191610de583612a46565b92610df36040519485612541565b80845284602082840101116104da578060208093018386015e83010152835260408201516001600160401b0381116113f6576020908301019080601f830112156113f6578151916001600160401b0383116113c2578260051b9060405193610e5e6020840186612541565b84526020808501928201019283116113f257602001905b8282106113da57505050602083015261010090610e94606082016130cc565b6040840152610ea560808201614e68565b6060840152610eb660a08201614e68565b6080840152610ec760c08201614e7c565b60a0840152610ed860e08201614e7c565b60c0840152015180880b8082036113d657610ef89160e0840152846134b2565b5060405191610f068361250a565b825263ffffffff87166020830152821515604083015260608201526001600160401b03831660808201526001600160401b03841660a08201526001600160a01b03851687525f516020615a2d5f395f51905f5260205260408720815180518051906001600160401b0382116113c25781908b610f828654612a0e565b601f8111611387575b5050602090601f8311600114611325578c9261131a575b50508160011b915f199060031b1c19161782555b6001820160208201518051906001600160401b03821161130657680100000000000000008211611306576020908c84548486558085106112ba575b505001918b5260208b20908b5b8160021c811061127557506003198116810380611219575b505050506001600160a01b03947f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd8224435397946001600160401b039460a06006868896600260809d9901888060408401511616891982541617815560608201517fffffffff0000000000000000000000000000000000000000ffffffffffffffff7bffffffffffffffffffffffffffffffffffffffff000000000000000083549260401b16911617905560038201908c808f83015116166001600160a01b0319835416178255848101519082547fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff000000000000000000000000000000000000000000001694891b16911617171790556111a76004820188806020880151161689198254161781556040860151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b60608401516005820155019185808c8301511616861984541617835501516fffffffffffffffff000000000000000082549160401b16906fffffffffffffffff0000000000000000191617905563ffffffff6040519a168a52151560208a01521660408801521660608601521692a280f35b928c938d5b8181106112375750505060021c015584848460a0611016565b909194602061126b6001926001600160401b03895116908560031b6001600160401b03809160031b9316831b921b19161790565b960192910161121e565b8c8d5b6004811061128d575083820155600101610ffe565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501611278565b8382876112e6945220600380870160021c820192601888831b16806112ed575b500160021c0190614896565b8c5f610ff1565b5f198501908154905f19908a03851b1c1690555f6112da565b634e487b7160e01b8c52604160045260248cfd5b015190505f80610fa2565b858d52818d209250601f1984168d5b81811061136f5750908460019594939210611357575b505050811b018255610fb6565b01515f1960f88460031b161c191690555f808061134a565b92936020600181928786015181550195019301611334565b602082886113b1945220601f850160051c810191602086106113b8575b601f0160051c0190614896565b8b5f610f8b565b90915081906113a4565b634e487b7160e01b8b52604160045260248bfd5b8880fd5b602080916113e7846130cc565b815201910190610e75565b8b80fd5b8980fd5b639b0c335d60e01b8752600487fd5b503461021757604036600319011261021757611423612421565b60243561142e612aba565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156114c25760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156104de576114ad575b505061020591339061308b565b816114b791612541565b6104da57825f6114a0565b5080fd5b503461021757806003193601126102175760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b503461021757806003193601126102175760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b50346102175760c03660031901126102175761155736612562565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541561020857610205906142b4565b5034610217576020366003190112610217576115a1612aba565b80808080600435335af16115b3612a61565b50156102175780f35b5034610217576060366003190112610217576115d6612437565b604435906001600160a01b0382169182810361186e576115f4612b78565b6115fd81612d18565b61160683612fa0565b610750576040810151156107505760016001600160401b03608083015116016001600160401b0381116118a35790859161164760e083510151840b826134b2565b509361167160208401926001600160401b03845116906001600160401b0360a08701511691613553565b1561186e576116b0816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b6116bb868254612623565b90556001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b156107505760405163f3fef3a360e01b81526001600160a01b0389166004820152602481018790529085908290604490829084905af1908115611898578591611883575b50506001600160401b039261174d60e08585511692510151860b876148ac565b509081611872575b5050505116926117bf60246040516001600160a01b036020820194169687855260408201526001606082015260608152611790608082612541565b6040519384916280000360e11b60208401525180918484015e810185838201520301601f198101835282612541565b7333333333333333333333333333333333333333333b156114c2578161180191604051809381926317938e1360e01b83526020600484015260248301906124cb565b0381837333333333333333333333333333333333333333335af180156104de57611859575b50506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b8161186391612541565b61186e57835f611826565b8380fd5b61187b92614cd4565b5f8080611755565b8161188d91612541565b61186e57835f61172d565b6040513d87823e3d90fd5b634e487b7160e01b86526011600452602486fd5b50346102175760e0366003190112610217576118d236612562565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1005415610208576001600160a01b036060820151166001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145f1461194157610205906142b4565b6102059060c435906136c5565b503461021757602036600319011261021757602061038361196d612421565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5034610217576040366003190112610217576119bd612437565b336001600160a01b038216036119d9576103f890600435613628565b63334bd91960e11b8252600482fd5b5034610217576020366003190112610217576001600160a01b03611a0a612421565b611a126129d7565b501681525f516020615a2d5f395f51905f52602052604081209060405190611a398261250a565b604051611a4581612525565b60405184548184611a5583612a0e565b8083529260018116908115611d915750600114611d50575b611a7992500382612541565b815260018401604051808260208294549384815201908652602086209286905b806003830110611cfe57611acf945491818110611ce4575b818110611cc7575b818110611caa575b10611c9c575b500382612541565b60208201526001600160a01b0360028501546001600160401b038116604084015260401c16606082015260038401546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c820b60e0820152825260048301549260208301906001600160401b038516825260ff604085019560401c161515855260066005820154916060860192835201549460808501926001600160401b03871684526001600160401b0360a087019760401c1687526040519560208752519460c06020880152611bb6865161010060e08a01526101e08901906124cb565b60208088015189830360df19016101008b015280518084529282019892910190835b818110611c7d57505050926001600160401b03809693899a969360e0878c610120866040819c0151169101528c6101406001600160a01b036060840151169101528c6101606001600160a01b036080840151169101528c61018060ff60a0840151169101528c6101a060ff60c0840151169101520151900b6101c08b0152511660408901525115156060880152516080870152511660a0850152511660c08301520390f35b82516001600160401b03168a526020998a019990920191600101611bd8565b60c01c81526020015f611ac7565b9260206001916001600160401b038560801c168152019301611ac1565b9260206001916001600160401b038560401c168152019301611ab9565b9260206001916001600160401b0385168152019301611ab1565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c6060820152019401920184929391611a99565b5090868552602085209085915b818310611d75575050906020611a7992820101611a6d565b6020919350806001915483858801015201910190918392611d5d565b60209250611a7994915060ff191682840152151560051b820101611a6d565b5034610217576040366003190112610217576103f8600435611dd0612437565b90611df66103ee825f525f516020615a4d5f395f51905f52602052600160405f20015490565b613581565b503461021757608036600319011261021757611e15612421565b611e1d61244d565b611e25612463565b90606435926fffffffffffffffffffffffffffffffff841680940361075057611e4c612b78565b84611e5682613027565b926001600160a01b0360808501511693843b156104da57611ee06101048492836001600160401b03806040519788968795636f0d192560e11b875260048701906001600160a01b036080809263ffffffff815116855260208101511515602086015263ffffffff604082015116604086015263ffffffff6060820151166060860152015116910152565b169a8b60a485015216988960c48401528b60e48401525af180156104de57611f42575b50506001600160a01b036040917f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c93835195865260208601521692a380f35b81611f4c91612541565b61075057845f611f03565b5034610217576020366003190112610217576020611f8f6004355f525f516020615a4d5f395f51905f52602052600160405f20015490565b604051908152f35b503461021757806003193601126102175760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503461021757606036600319011261021757611fec612421565b6024356001600160401b0381116104da5761200b90369060040161249b565b9092604435906001600160401b038211610217576020611f8f868686612034366004890161249b565b93909261203f612b78565b612630565b50346120fa5760403660031901126120fa5761205e612421565b9060243561206a612aba565b6001600160a01b03608061207d85613027565b015116803b156120fa576040516390a0827b60e01b81526001600160a01b038516600482015260248101839052905f908290604490829084905af180156120ef576120d9575b5061020591926001600160a01b0333911661308b565b61020592505f6120e891612541565b5f916120c3565b6040513d5f823e3d90fd5b5f80fd5b346120fa575f3660031901126120fa5760206040516305f5e1008152f35b346120fa5760a03660031901126120fa57612135612421565b61213d612479565b61214561248c565b9160643563ffffffff81168091036120fa576084359163ffffffff83168093036120fa5761217281612d18565b5061217b612b29565b6001600160a01b0360016121bf836001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b015416908115612317575b63ffffffff604051956121dc876124ef565b16948581526001600160a01b036001816020840199151595868b52604085019a888c52606086018a8152836080880193169c8d845263ffffffff6122508a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b985116926cffffffff00000000000000000068ffffffff000000000064ff000000008b549351151560201b16935160281b16935160481b16936cffffffff000000000000000000199168ffffffffffffffffff1916171617171785555116920191166001600160a01b03198254161790556122ca86612fa0565b156120fa577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9936080936001600160a01b03936040519788526020880152604087015260608601521692a3005b905061232281612f58565b604051610b048082018281106001600160401b03821117612354578291614f29833903905ff580156120ef57906121ca565b634e487b7160e01b5f52604160045260245ffd5b346120fa5760203660031901126120fa5760043563ffffffff60e01b81168091036120fa57602090637965db0b60e01b81149081156123ad575b506040519015158152f35b6301ffc9a760e01b149050826123a2565b346120fa5760403660031901126120fa5761241f6123da612421565b6001600160a01b036123ea61244d565b916123f3612aba565b165f525f516020615a2d5f395f51905f526020526001600160401b03600460405f200154163390612c34565b005b600435906001600160a01b03821682036120fa57565b602435906001600160a01b03821682036120fa57565b602435906001600160401b03821682036120fa57565b604435906001600160401b03821682036120fa57565b6024359063ffffffff821682036120fa57565b6044359081151582036120fa57565b9181601f840112156120fa578235916001600160401b0383116120fa576020808501948460051b0101116120fa57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60a081019081106001600160401b0382111761235457604052565b60c081019081106001600160401b0382111761235457604052565b61010081019081106001600160401b0382111761235457604052565b90601f801991011681019081106001600160401b0382111761235457604052565b60c09060031901126120fa576040519061257b8261250a565b81600435815260243560208201526044356001600160a01b03811681036120fa5760408201526064356001600160a01b03811681036120fa576060820152608435608082015260a060a435910152565b91908110156125db5760051b0190565b634e487b7160e01b5f52603260045260245ffd5b906001600160401b03809116911601906001600160401b03821161260f57565b634e487b7160e01b5f52601160045260245ffd5b9190820180921161260f57565b9194935f935f968281036120fa57612678856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156120fa57929061268b85612d18565b91608061269787613027565b01916001600160a01b03835116946126be60208601966001600160401b03885116906130e0565b965f975b818d1061292e575b50508a1592506129239150505743612712866001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160401b0384168061272c575b50505050505050565b6127416127649560e0855101515f0b906134b2565b6001600160401b03869792975116906001600160401b0360a08701511691613553565b1561290457506127a4856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6127af858254612623565b90556001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166001600160a01b03861690803b156120fa5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101879052905f908290604490829084905af180156120ef576128d8575b506001600160a01b038261284c876001600160401b03948460e09751169061308b565b511693511691510151850b93823b15610a7d57604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156104de576128c3575b8080808080612723565b6128ce828092612541565b61021757806128b9565b6001600160401b0391975060e0926128f85f6001600160a01b0393612541565b5f989093509150612829565b6001600160a01b03866377e88bc960e11b5f521660045260245260445ffd5b505f96505050505050565b61293d8d83879f9b96976125cb565b359061294a8a85886125cb565b356001600160401b03811681036120fa5761297392898d926001600160a01b038b5116926131c2565b9490911561299157600191612987916125ef565b98019b93926126c2565b989c506126ca565b604051906129a682612525565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b604051906129e48261250a565b5f60a0836129f0612999565b81528260208201528260408201528260608201528260808201520152565b90600182811c92168015612a3c575b6020831014612a2857565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612a1d565b6001600160401b03811161235457601f01601f191660200190565b3d15612a8b573d90612a7282612a46565b91612a806040519384612541565b82523d5f602084013e565b606090565b60405190612a9d826124ef565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a602052604090205460ff1615612af257565b63e2517d3f60e01b5f52336004527f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f60245260445ffd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff1615612b6157565b63e2517d3f60e01b5f52336004525f60245260445ffd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd602052604090205460ff1615612bb057565b63e2517d3f60e01b5f52336004527f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef660245260445ffd5b805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0333165f5260205260ff60405f20541615612c1e5750565b63e2517d3f60e01b5f523360045260245260445ffd5b6024906001600160401b03612ca3939481604051936001600160a01b03602086019816885216604084015216606082015260608152612c74608082612541565b6040519384916280000360e11b60208401525180918484015e81015f838201520301601f198101835282612541565b7333333333333333333333333333333333333333333b156120fa575f612ce591604051809381926317938e1360e01b83526020600484015260248301906124cb565b0381837333333333333333333333333333333333333333335af180156120ef57612d0c5750565b5f612d1691612541565b565b6001600160a01b0390612d296129d7565b50165f525f516020615a2d5f395f51905f5260205260405f2060405191612d4f8361250a565b60405191612d5c83612525565b6040518154815f612d6c83612a0e565b8083529260018116908115612f395750600114612ef8575b612d9092500382612541565b8352600181019360405180602087549182815201965f5260205f20905f915b816003840110612eac5797612df09284926001600160a01b039798999a5491818110611ce457818110611cc757818110611caa5710611c9c57500382612541565b60208601528160028401546001600160401b038116604088015260401c1660608601526001600160401b0360066003850154946080880197858716895260ff8760a01c1660a082015260c081019660ff8160a81c16885260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015293511615159081612e9e575b50156120fa57565b60ff9150511615155f612e96565b97600160806004928b546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c6060820152019901920191612daf565b5090835f5260205f20905f915b818310612f1d575050906020612d9092820101612d84565b6020919350806001915483858801015201910190918392612f05565b60209250612d9094915060ff191682840152151560051b820101612d84565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152612f8d604882612541565b51902090565b519081151582036120fa57565b5f80916040516001600160a01b03602082019216825260208152612fc5604082612541565b51906108105afa612fd4612a61565b9015613018576020818051810103126120fa5760405190602082018281106001600160401b038211176123545760405261301090602001612f93565b809152151590565b6313dd7ccd60e31b5f5260045ffd5b61303390610b8d612a90565b906001600160a01b0360016040519361304b856124ef565b63ffffffff8154818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156120fa57565b60405163a9059cbb60e01b60208201526001600160a01b03929092166024830152604480830193909352918152612d16916130c7606483612541565b614b17565b51906001600160401b03821682036120fa57565b5f919082916001600160401b03604051916001600160a01b03602084019416845216604082015260408152613116606082612541565b51906108015afa613125612a61565b9015613193576060818051810103126120fa576040519060608201918083106001600160401b03841117612354576001600160401b0392604052604061318c6060613172602086016130cc565b948585526131818482016130cc565b6020860152016130cc565b9101521690565b639d2c8fcb60e01b5f5260045ffd5b906001600160401b03809116911603906001600160401b03821161260f57565b95929190939495805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20916001600160a01b0383541695861561343a576002840180549860ff8a60481c1661342b576001600160a01b03600187015491166001600160a01b03821603613418576001600160401b039060a01c166001600160401b038a1660ff8b60401c165f146133c1575080926001600160401b03811682115f146133b957613279916131a2565b985b6132858a8c6125ef565b9a6001600160401b038c166001600160401b038516116133a8575069010000000000000000009069ff00000000000000000019161790556132c88160019a6131a2565b966001600160401b0360206001600160a01b036132ec60e0875101515f0b8d6134b2565b50981694015116833b156120fa5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152915f908390606490829084905af19081156120ef577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4392604092613398575b506001600160a01b0360018187541696015416956001600160401b038351921682526020820152a4565b5f6133a291612541565b5f61336e565b5f9b508b9a50985050505050505050565b50505f613279565b9092809a93806001600160401b038316105f1461340d576133ec916133e5916131a2565b809b6125ef565b816001600160401b038216115f1461340657505b9161327b565b9050613400565b50506133ec5f6133e5565b8463358d72d160e01b5f5260045260245ffd5b63f7348a7960e01b5f5260045ffd5b631a40316d60e01b5f5260045ffd5b60ff16604d811161260f57600a0a90565b9190820391821161260f57565b8115613471570490565b634e487b7160e01b5f52601260045260245ffd5b90620f4240820291808304620f4240149015171561260f57565b8181029291811591840414171561260f57565b9190805f0b80155f146134cf5750506001600160401b0382169190565b5f8113156134fa57506134e76134f69160ff16613449565b6001600160401b03841661349f565b9190565b9050607f19811461260f576001600160401b039261351f613528925f0360ff16613449565b93849116612623565b5f19810190811161260f5761354f613548846001600160401b0393613467565b938461349f565b1690565b6135796001600160401b039293613573849361356e81614bb0565b6130e0565b946125ef565b169116101590565b805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260ff60405f205416155f1461362257805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260405f20600160ff198254161790556001600160a01b03339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260ff60405f2054165f1461362257805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260405f2060ff1981541690556001600160a01b03339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b907f0000000000000000000000000000000000000000000000000000000000000000604083016136fe6001600160a01b03825116612fa0565b1561423f576001600160a01b038216805f525f516020615a2d5f395f51905f5260205260405f2090604051946137338661250a565b60405161373f81612525565b6040518454815f61374f83612a0e565b808352926001811690811561422057506001146141df575b61377392500382612541565b815260018401604051808260208294549384815201905f5260205f20925f905b80600383011061418d576137c5945491818110611ce457818110611cc757818110611caa5710611c9c57500382612541565b60208201526001600160a01b0360028501546001600160401b038116604084015260401c16606082015260038401546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528652600660048401549360ff60208901956001600160401b038116875260401c1615156040890152600581015460608901520154966001600160401b03881660808801526001600160401b0360a088019860401c168852606081016001600160a01b038151165f525f516020615a2d5f395f51905f5260205260405f2095604051966138b08861250a565b6040516138bc81612525565b6040518254815f6138cc83612a0e565b808352926001811690811561416e575060011461412d575b6138f092500382612541565b81526040516001830180548083525f9182526020808320849391840192905b8160038401106140db5754918181106140c1575b8181106140a4575b818110614087575b10614079575b50036139459082612541565b602082015260028201546001600160401b038116604083015260401c6001600160a01b0316606082015260038201546001600160a01b03811660808301528060a01c60ff1660a08301528060a81c60ff1660c083015260b01c5f0b60e0820152885260048101546001600160401b03811660208a015260401c60ff161515604089015260058101546060890152600601546001600160401b038116608089015260401c6001600160401b031660a088015281516001600160a01b0316613a0a90613027565b9383519060a08501918251996080870191825115159b613a2991612623565b8d5160e001515f0b613a3a916148ac565b90508d5160c0015160ff16825160c0015160ff1690613a5892614e9e565b9a1561404b578a925b5f8063ffffffff8b51166040516020810191825260208152613a84604082612541565b51906108085afa613a93612a61565b901561403c576020818051810103126120fa57898f9360ff60a0613ae19260206001600160401b03613ac982613ae799016130cc565b16950197885115155f14614031575051015116613449565b9061349f565b825115613fe75763ffffffff613b028160608d015116614bd6565b16620f4240019081620f42401161260f57613b38620f424091613ae16001600160401b039463ffffffff8f604001511690612623565b0416908351908115155f14613fdf5750915b5115613f9e576305f5e100811015613f5e57505f5b613b7f613b6c8651613485565b613b798a51885190612623565b90612623565b5f1981019190821161260f57613b79613ba592613b9f8b51895190612623565b90613467565b606480830283810482148415171561260f578211613ee45750505085518c5160e001515f0b613bd3916148ac565b9d8e8b9c929c516001600160401b031691516001600160401b031690613bf892613553565b15613e94579c8a9b9c9d85516001600160a01b03169387516001600160a01b0316935115159360405195613c2b8761250a565b86526020860190815260408601926001600160401b03169485845260608701926001600160401b0316968784526080810191825260a08101925f845260208d019586515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f2092516001600160a01b03166001600160a01b031683546001600160a01b0319161783556001830191516001600160a01b03166001600160a01b031682546001600160a01b0319161782555181549060a01b7bffffffffffffffff000000000000000000000000000000000000000016907fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1617905560020192516001600160401b03166001600160401b03166001600160401b0319845416178355511515613d8190839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151581549060481b69ff000000000000000000169069ff00000000000000000019161790555194516001600160a01b031695516001600160a01b03169651935160405194855260208501526001600160401b031660408401526060830152608082015260a07f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91891a4608001516001600160a01b03169283613e229261308b565b516001600160401b0316935160e001515f0b93813b156120fa57604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f92830b60648301529091908290608490829084905af180156120ef57612d0c5750565b5050505050945095505092505081612d16947f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c602080870151926001600160401b0360405191168152a35261492d565b999c5099509c50505050509450809650602091500151906001600160a01b038451169281810180911161260f575f19810190811161260f57811561347157612d16977fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e92604092049082519182526020820152a3526142b4565b6305f5e0ff19016001600160401b03811161260f57613f98613f8d6001600160401b036305f5e1009316613485565b6305f5e0ff90612623565b04613b5f565b6305f5e100811115613fb157505f613b5f565b6305f5e100036001600160401b03811161260f57613f98613f8d6001600160401b036305f5e1009316613485565b905091613b4a565b63ffffffff613ffb8160608d015116614bd6565b16620f42400390620f4240821161260f57613b38620f424091613ae16001600160401b039463ffffffff8f60400151169061345a565b905051015116613449565b635cffc5fb60e11b5f5260045ffd5b8261271003612710811161260f576127106140706001600160401b0392838f1661349f565b04169a92613a61565b60c01c81526020015f613939565b9260206001916001600160401b038560801c168152019301613933565b9260206001916001600160401b038560401c16815201930161392b565b9260206001916001600160401b0385168152019301613923565b935090916001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019084939261390f565b5090845f5260205f20905f915b8183106141525750509060206138f0928201016138e4565b602091935080600191548385880101520191019091839261413a565b602092506138f094915060ff191682840152151560051b8201016138e4565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c6060820152019401920184929391613793565b5090865f5260205f20905f915b81831061420457505090602061377392820101613767565b60209193508060019154838588010152019101909183926141ec565b6020925061377394915060ff191682840152151560051b820101613767565b6080840151909392501561426a576001600160a01b038351166320a2097d60e11b5f5260045260245ffd5b6001600160a01b0390612d16937f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60208481870151935116604051908152a216606082015261492d565b6060810151906001600160a01b035f921691825f525f516020615a2d5f395f51905f5260205260405f206040516142ea8161250a565b6040516142f681612525565b6040518354815f61430683612a0e565b80835292600181169081156148775750600114614836575b61432a92500382612541565b815260018301604051808260208294549384815201905f5260205f20925f905b8060038301106147e45761437c945491818110611ce457818110611cc757818110611caa5710611c9c57500382612541565b60208201526001600160a01b0360028401546001600160401b038116604084015260401c16606082015260038301546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528152600660048301549260ff60208401946001600160401b038116865260401c1615156040840152600581015460608401520154906001600160401b03821660808201526001600160401b0360a082019260401c168252604085019361444b6001600160a01b03865116612fa0565b156147745785519161271061447461446960a08a0195865190612623565b60808a01519061349f565b049383519480861161476c575b50846146ca575b6144c06144a7614499878b51612623565b60e0855101515f0b906148ac565b919092826001600160401b03808b511692511691613553565b1561467c5750846145c0575b61455d906001600160401b037fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c976145348c6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b61453f898254612623565b905551169360e06001600160a01b038a511694510151900b906148ac565b919092836145a4575b5050505061459f6001600160a01b0360208701519551169551915192604051938493846040919493926060820195825260208201520152565b0390a4565b6145b26145b794828c614cd4565b612c34565b5f808080614566565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001695863b156120fa5760405163f3fef3a360e01b81526001600160a01b038b16600482015260248101879052965f908890604490829084905af19687156120ef577fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c97614659575b5095506144cc565b61455d929194505f61466a91612541565b6001600160401b035f94919250614651565b9793505050507f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c9350602092508291506146b58161492d565b0151926001600160401b0360405191168152a3565b6146d9858a60208b0151614bf1565b614488576040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031660048201529094506020816024818c5afa9081156120ef575f9161473a575b5093614488565b90506020813d602011614764575b8161475560209383612541565b810103126120fa57515f614733565b3d9150614748565b94505f614481565b505050506080820151909250156147a2576001600160a01b038251166320a2097d60e11b5f5260045260245ffd5b612d16917f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b0381850151935116604051908152a261492d565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192018492939161434a565b5090855f5260205f20905f915b81831061485b57505090602061432a9282010161431e565b6020919350806001915483858801015201910190918392614843565b6020925061432a94915060ff191682840152151560051b82010161431e565b8181106148a1575050565b5f8155600101614896565b9190805f0b9081155f146148c95750506001600160401b03821690565b5f821315614904576148de915060ff16613449565b9182156134715761354f6148fd82856001600160401b0394069061345a565b9384613467565b505f0380805f0b0361260f5761354f6149276001600160401b039260ff16613449565b8461349f565b805161271061495061494560a0850193845190612623565b60808501519061349f565b0481518181115f14614b0f5750905b8190602084019161498283519460608701956001600160a01b0387511690614bf1565b15614b08575b80614a52575b7f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9161459f6001600160a01b03806149c7858a51612623565b966149df828a51169860408c0199848b51169061308b565b614a1c828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b614a27878254612623565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916001600160a01b0385511692803b156120fa5760405163f3fef3a360e01b81526001600160a01b03949094166004850152602484018390525f908490604490829084905af19283156120ef577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f93614af8575b50915061498e565b5f614b0291612541565b5f614af0565b505f614988565b90509061495f565b906001600160a01b03614b7892165f8060405193614b36604086612541565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602086015260208151910182855af1614b72612a61565b91614efd565b8051908115918215614b8e575b5050156120fa57565b81925090602091810103126120fa576020614ba99101612f93565b5f80614b85565b6001600160401b03166001609d1b01806001609d1b1161260f576001600160a01b031690565b63ffffffff60649116029063ffffffff821691820361260f57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa9182156120ef575f92614ca0575b5080821094851595614c61575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080614c5b565b9091506020813d602011614ccc575b81614cbc60209383612541565b810103126120fa5751905f614c4e565b3d9150614caf565b9091906001600160401b038316614e4d576001600160a01b03919250166040515f806020830163095ea7b360e01b8152736b9e773128f453f5c2c60935ee2de2cbc5390a24602485015285604485015260448452614d33606485612541565b83519082865af1614d42612a61565b81614e16575b5080614e0c575b15614db9575b5050736b9e773128f453f5c2c60935ee2de2cbc5390a243b156120fa5760405190630acb7f4b60e21b8252600482015263ffffffff60248201525f8160448183736b9e773128f453f5c2c60935ee2de2cbc5390a245af180156120ef57612d0c5750565b614e05916130c760405163095ea7b360e01b6020820152736b9e773128f453f5c2c60935ee2de2cbc5390a2460248201525f604482015260448152614dff606482612541565b82614b17565b5f80614d55565b50813b1515614d4f565b8051801592508215614e2b575b50505f614d48565b81925090602091810103126120fa576020614e469101612f93565b5f80614e23565b6001600160a01b03614e61612d1694614bb0565b911661308b565b51906001600160a01b03821682036120fa57565b519060ff821682036120fa57565b9060ff8091169116039060ff821161260f57565b9160ff811660ff83168181145f14614eb7575050505090565b6001600160401b039492911115614ee557614ede614ed961354f948693614e8a565b613449565b911661349f565b614ef6614ed9859261354f95614e8a565b9116613467565b91925015614f1957815115614f10575090565b3b156120fa5790565b5080519081156120fa57602001fdfe60a080604052346100455733608052610aba908161004a82396080518181816086015281816101b80152818161039a015281816104d50152818161054c01526106a40152f35b5f80fdfe60806040526004361015610011575f80fd5b5f5f3560e01c8063435354d31461050a57806390a0827b146104a55780639c45c34b146100ad578063a703334f1461033e578063de1a324a146100cb578063e94b77c1146100ad5763eba61c0e14610067575f80fd5b346100aa57806003193601126100aa5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b80fd5b50346100aa576100c86100bf366105ee565b92919091610699565b80f35b50346100aa57366003190161010081126103265760a0136100aa5760405160a0810181811067ffffffffffffffff82111761032a5760405261010b6105db565b91828252602435918215158303610326576020810192835260443563ffffffff8116810361032257604082015260643563ffffffff81168103610322576060820152608435906001600160a01b0382168203610322576080015260a43567ffffffffffffffff81168091036103265760c4359067ffffffffffffffff82168092036103225760e435946fffffffffffffffffffffffffffffffff861680960361031e576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361031e5763ffffffff6101ed911661066c565b9351151592811561030f57821561030057916102819391600493506040519263ffffffff60208501971687526040840152606083015260808201525f945f60a0830152600260c083015260e082015260e0815261024c61010082610636565b6020604051948592630100000160e01b83850152518091602485015e8201018281015f8152500301601f198101835282610636565b7333333333333333333333333333333333333333333b156102fc575f6102bb91604051809381926317938e1360e01b8352600483016108ff565b0381837333333333333333333333333333333333333333335af180156102f1576102e3575080f35b6102ef91505f90610636565b005b6040513d5f823e3d90fd5b5f80fd5b6313c0a8df60e01b8152600490fd5b63017461b760e71b8152600490fd5b8380fd5b8280fd5b5080fd5b634e487b7160e01b83526041600452602483fd5b50346100aa5760603660031901126100aa578060043567ffffffffffffffff811680910361049c57602435906001600160a01b0382168092036104a15760443567ffffffffffffffff811680910361049f576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361049f5761041f91600491604051916020830195865260408301526060820152606081526103eb608082610636565b60206040519485926280000360e11b83850152518091602485015e820101828101868152500301601f198101835282610636565b7333333333333333333333333333333333333333333b1561049c578161045991604051809381926317938e1360e01b8352600483016108ff565b0381837333333333333333333333333333333333333333335af18015610491576104805750f35b8161048a91610636565b6100aa5780f35b6040513d84823e3d90fd5b50fd5b505b5050fd5b50346100aa5760403660031901126100aa576004356001600160a01b038116809103610326576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163303610326576100c890602435903390610929565b50346102fc5760403660031901126102fc576105246105db565b602435906fffffffffffffffffffffffffffffffff82168092036102fc576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102fc5760046105806102819261066c565b926040519063ffffffff60208301951685526040820152604081526105a6606082610636565b6020604051948592630100000b60e01b83850152518091602485015e8201018281015f8152500301601f198101835282610636565b6004359063ffffffff821682036102fc57565b60809060031901126102fc576004356001600160a01b03811681036102fc579060243567ffffffffffffffff811681036102fc579060443590606435805f0b81036102fc5790565b90601f8019910116810190811067ffffffffffffffff82111761065857604052565b634e487b7160e01b5f52604160045260245ffd5b63ffffffff6127109116019063ffffffff821161068557565b634e487b7160e01b5f52601160045260245ffd5b926001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102fc57805f0b9081155f1461088f5750505b816106e357505050565b67ffffffffffffffff16806108685750906001600160a01b03166040515f806020830163095ea7b360e01b8152736b9e773128f453f5c2c60935ee2de2cbc5390a2460248501528560448501526044845261073f606485610636565b83519082865af161074e610a1a565b81610839575b508061082f575b156107d7575b5050736b9e773128f453f5c2c60935ee2de2cbc5390a243b156102fc5760405190630acb7f4b60e21b8252600482015263ffffffff60248201525f8160448183736b9e773128f453f5c2c60935ee2de2cbc5390a245af180156102f1576107c7575b505b565b5f6107d191610636565b5f6107c3565b6108289161082360405163095ea7b360e01b6020820152736b9e773128f453f5c2c60935ee2de2cbc5390a2460248201525f60448201526044815261081d606482610636565b82610978565b610978565b5f80610761565b50813b151561075b565b805180159250821561084e575b50505f610754565b6108619250602080918301019101610960565b5f80610846565b6001609d1b0191826001609d1b11610685576001600160a01b03806107c594169116610929565b5f8213156108d0576108a4915060ff16610a09565b80156108bc57808306830392831161068557506106d9565b634e487b7160e01b5f52601260045260245ffd5b505f0380805f0b03610685576108e89060ff16610a09565b8281810291818304149015171561068557506106d9565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b6107c5926001600160a01b036040519363a9059cbb60e01b6020860152166024840152604483015260448252610823606483610636565b908160209103126102fc575180151581036102fc5790565b906001600160a01b036109d992165f8060405193610997604086610636565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602086015260208151910182855af16109d3610a1a565b91610a59565b80519081159182156109ef575b5050156102fc57565b610a029250602080918301019101610960565b5f806109e6565b60ff16604d811161068557600a0a90565b3d15610a54573d9067ffffffffffffffff82116106585760405191610a49601f8201601f191660200184610636565b82523d5f602084013e565b606090565b91925015610a7557815115610a6c575090565b3b156102fc5790565b5080519081156102fc57602001fdfea26469706673582212208abf3b44431f4c61bdd7aa5253477149e50113939030a9816d0109335ec8e08364736f6c634300081e00336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0002dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800a26469706673582212203f720c6ea86c631282ab7132c9a7dc291402b5cc506847c33ef5f144f0f78ca764736f6c634300081e0033bd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ffb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680000000000000000000000000081d40f21f12a8f0e3252bccb954d722d4c464b640000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000bc217096db9eb6d2782c1d9e725d462077a4d1f6000000000000000000000000b88339cb7199b77e23db6e890353e22632ba630f0000000000000000000000005e7840e06faccb6d1c3b5f5e0d1d3d07f2829bba", - "nonce": "0x29d", + "input": "0x610100346200027257601f6200833338819003918201601f19168301926001600160401b03929091838511838610176200025e578160a092849260409788528339810103126200027257620000548162000276565b91620000636020830162000276565b926200007185840162000276565b926200008e6080620000866060840162000276565b920162000276565b60015f5586519093615d3e808301918211838310176200025e5788918391620025f583396001600160a01b03978816815284881660208201520301905ff09283156200025457848094166080525f7f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68082527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020528160018a82200181815491557fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff92838380a47f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f80835260018a8420019183835493558380a460a05260e0521660c0527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0080546001600160a01b031916919092161790556107087fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0155620001f7336200028b565b50516122b890816200033d82396080518181816101fe01526112fa015260a051818181610711015281816114650152612030015260c0518181816107db0152610aef015260e0518181816108aa01528181610c3f0152610cf50152f35b86513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b51906001600160a01b03821682036200027257565b6001600160a01b03165f8181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260409020547f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268009060ff1662000336575f805260205260405f20815f5260205260405f20600160ff1982541617905533905f7f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b50505f9056fe60806040526004361015610030575b361561002e573461002a5761002236610dbb565b602081519101f35b5f80fd5b005b5f3560e01c806301ffc9a71461017f57806309cfd6751461017a5780631b1062b81461017557806321081d3c14610170578063238ac9331461016b578063248a9ca3146101665780632561efb214610161578063277e661d1461015c5780632f2ff15d1461015757806336568abe14610152578063490e662f1461014d5780634b3b029b146101485780634f7d9d2e14610143578063657cad8a1461013e5780636c19e783146101395780638c73eb041461013457806391d148541461012f578063a217fddf1461012a578063c55dae6314610125578063d547741f146101205763feb617240361000e5761092d565b6108ce565b61088b565b610871565b6107ff565b6107bc565b610735565b6106f2565b6106b6565b61067c565b610642565b6105f7565b610598565b610542565b6104f6565b6104aa565b610465565b61042b565b610388565b6101df565b3461002a57602036600319011261002a5760043563ffffffff60e01b811680910361002a57602090637965db0b60e01b81149081156101c4575b506040519015158152f35b6301ffc9a760e01b1490505f6101b9565b5f91031261002a57565b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff811161024a57604052565b610222565b6040810190811067ffffffffffffffff82111761024a57604052565b6080810190811067ffffffffffffffff82111761024a57604052565b90601f8019910116810190811067ffffffffffffffff82111761024a57604052565b6040519060c0820182811067ffffffffffffffff82111761024a57604052565b604051906102d68261026b565b565b60405190610200820182811067ffffffffffffffff82111761024a57604052565b604051906060820182811067ffffffffffffffff82111761024a57604052565b604051906102d68261024f565b67ffffffffffffffff811161024a57601f01601f191660200190565b81601f8201121561002a5780359061035982610326565b926103676040519485610287565b8284526020838301011161002a57815f926020809301838601378301015290565b3461002a57606036600319011261002a5767ffffffffffffffff60043581811161002a576103ba903690600401610342565b60243582811161002a576103d2903690600401610342565b9160443590811161002a575f926103f0610424923690600401610342565b906103f9610e00565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1009360018555610abc565b5560015f55005b3461002a575f36600319011261002a5760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b3461002a575f36600319011261002a5760206001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f005416604051908152f35b3461002a57602036600319011261002a576004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526020600160405f200154604051908152f35b3461002a57602036600319011261002a5761050f610e00565b61051761175e565b6004357fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f015560015f55005b3461002a57602036600319011261002a5760043567ffffffffffffffff811161002a5761057d6105786020923690600401610342565b611823565b6040519015158152f35b6001600160a01b0381160361002a57565b3461002a57604036600319011261002a5761002e6024356004356105bb82610587565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526105f2600160405f2001546117b4565b6118cf565b3461002a57604036600319011261002a5760243561061481610587565b336001600160a01b038216036106305761002e90600435611988565b60405163334bd91960e11b8152600490fd5b3461002a575f36600319011261002a5760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b3461002a575f36600319011261002a5760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b3461002a575f36600319011261002a5760207fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0154604051908152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57602036600319011261002a5760043561075281610587565b61075a610e00565b61076261175e565b6001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0091167fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905560015f555f80f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57604036600319011261002a57602060ff61086560243561082381610587565b6004355f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800845260405f20906001600160a01b03165f5260205260405f2090565b54166040519015158152f35b3461002a575f36600319011261002a5760206040515f8152f35b3461002a575f36600319011261002a5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002a57604036600319011261002a5761002e6024356004356108f182610587565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052610928600160405f2001546117b4565b611988565b3461002a57602036600319011261002a576004355f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f02602052602060ff60405f2054166040519015158152f35b9081602091031261002a5751801515810361002a5790565b5f5b8381106109a45750505f910152565b8181015183820152602001610995565b906020916109cd81518092818552858086019101610993565b601f01601f1916010190565b90916109f06109fe936040845260408401906109b4565b9160208184039101526109b4565b90565b6040513d5f823e3d90fd5b9060206109fe9281815201906109b4565b634e487b7160e01b5f52601160045260245ffd5b5f19810191908211610a3f57565b610a1d565b91908203918211610a3f57565b634e487b7160e01b5f52602160045260245ffd5b60c09093929193610ab88160e081019660a08091805184526020810151602085015260408101516001600160a01b0380911660408601526060820151166060850152608081015160808501520151910152565b0152565b604051630afd9fa560e31b81526020939192849082908190610ae29087600484016109d9565b03815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af18015610db657610d99575b5060405163277e661d60e01b8152838180610b3a8660048301610a0c565b0381305afa5f9181610d6a575b50610b525750505050565b15610d6557610b63610b6c92610f1f565b92909182611049565b80610d1a575b610b80836060840151610a44565b92610100830151610bee610b9861018086015161129a565b8415610cf057610bde610baf6101a088015161129a565b915b8615610ce957610140880151945b610bc76102a9565b998a528a8a01526001600160a01b03166040890152565b6001600160a01b03166060870152565b608085015260a084015280610cb9575b15610c78576102d69281610c65600160ff610c276101c06101e0610c7398015195015160ff1690565b161492610c326102c9565b9586526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690860152565b604084015215156060830152565b611383565b610cb692610ca3610160610cb193015160405194859363073ffe1360e31b9085015260248401610a65565b03601f198101835282610287565b6112ef565b50565b5060ff610ccb6101c083015160ff1690565b1660018114908115610cde575b50610bfe565b60029150145f610cd8565b5f94610bbf565b610bde7f000000000000000000000000000000000000000000000000000000000000000091610bb1565b610d60610d536101008401515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b805460ff19166001179055565b610b72565b505050565b610d8b919250853d8711610d92575b610d838183610287565b81019061097b565b905f610b47565b503d610d79565b610daf90843d8611610d9257610d838183610287565b505f610b1c565b610a01565b610dc3610e00565b610dcc81610326565b610dd96040519182610287565b81815236821161002a575f602083610df9948383860137830101526112ef565b9060015f55565b60025f541461002a5760025f55565b610e176102d8565b905f82525f60208301525f60408301525f60608301525f60808301525f60a08301525f60c08301525f60e08301525f6101008301525f6101208301525f6101408301525f6101608301525f6101808301525f6101a08301525f6101c083015260606101e0830152565b81601f8201121561002a578051610e9681610326565b92610ea46040519485610287565b8184526020828401011161002a576109fe9160208085019101610993565b9190916101008184031261002a5780519260208201519260408301519260608101519260808201519260a08301519260c081015160ff8116810361002a579260e082015167ffffffffffffffff811161002a576109fe9201610e80565b90610f8a610f2b610e0f565b92610f42610f3882611a38565b63ffffffff168552565b610f5b610f4e82611a5a565b63ffffffff166020860152565b610f6481611a7a565b60a0850152610f82610f7582611a6a565b63ffffffff1660e0860152565b805190611c56565b90610f9482611a8a565b6040840152610fa282611a9a565b6060840152610fb082611aaa565b6080840152610fbe82611aba565b60c0840152611008610fed610fde610fd585611aca565b94805190611cb0565b60208082518301019101610ec2565b6101e08c999394959697989901526101c08b019060ff169052565b6101a0890152610180880152610160870152610140860152610120850152610100840152565b9060018201809211610a3f57565b91908201809211610a3f57565b6112086001600160a01b037fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f00541692611086835163ffffffff1690565b93611098602085015163ffffffff1690565b946040850151956111186060870151608088015160a089015160c08a0151916110c860e08c015163ffffffff1690565b936040519c8d9760208901998a96929360e09692959199989461010089019a63ffffffff97888092168b521660208a015260408901526060880152608087015260a086015260c085015216910152565b039561112c601f1997888101835282610287565b5190206111f36111ff61010087019788516101208901986111d48a516111c861014084015193610160810151906101808101516101a0820151906101e06111786101c085015160ff1690565b9301516020815191012093604051988997602089019b8c9490989796929360ff9460e0979361010088019b8852602088015260408701526060860152608085015260a08401521660c08201520152565b03848101835282610287565b5190206040805160208101968752908101919091529283906060820190565b03908101835282610287565b51902090611cdd565b918261124d575b5081611219575090565b6112479150517fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f01549061103c565b42111590565b61129391925061128861128f91515f527fb788edf5b6d001c4df53cb371352fd225afa05a1712075d5f89a08d6b6f79f0260205260405f2090565b5460ff1690565b1590565b905f61120f565b8060a01c6112ae576001600160a01b031690565b6040516379ec0ed760e11b8152600490fd5b3d156112ea573d906112d182610326565b916112df6040519384610287565b82523d5f602084013e565b606090565b5f80916020815191017f00000000000000000000000000000000000000000000000000000000000000005af46113236112c0565b901561132c5790565b602081519101fd5b6102d69092919260c081019360a08091805184526020810151602085015260408101516001600160a01b0380911660408601526060820151166060850152608081015160808501520151910152565b61138b611aea565b5060409081810151916113a983519360208080968301019101611b43565b92808301936113d16113c56113c587516001600160a01b031690565b6001600160a01b031690565b83516370a0823160e01b80825230600483015296909290918490849060249082905afa928315610db6575f9361173f575b506060926114216113c56113c5868a51016001600160a01b0390511690565b865189815230600482015291908690839060249082905afa918215610db6575f92611720575b506114a661145f6113c586516001600160a01b031690565b9361148f7f000000000000000000000000000000000000000000000000000000000000000095868c515191611da9565b89518701513091906001600160a01b031690611fab565b6001600160a01b038094166114c286516001600160a01b031690565b918a515191803b1561002a576114f3935f80948d5196879586948593633a5be8cb60e01b8552309160048601611c28565b03925af18015610db657611707575b5061151a6113c56113c586516001600160a01b031690565b87518a815230600482015291908790839060249082905afa918215610db6575f926116e8575b50036116575750610cb6965061157261156083516001600160a01b031690565b84885101906001600160a01b03169052565b855151915b61158a83885160a0815191015190612126565b60a088510152828751527fb88fc27be67e678ffb77faf8f8bb00d39b66b4845e4f7ec1e623b0f15abd52138751926115cd8785015193516001600160a01b031690565b946116046115e68887519701516001600160a01b031690565b91838b51948594169816968360209093929193604081019481520152565b0390a4835190840151156116365750610ca3610cb19293519351938492632132ff4360e11b9084015260248301611334565b9250610ca3610cb19251938492627f9f5760e91b9084015260248301611334565b846116736113c56113c5878b51016001600160a01b0390511690565b8751998a523060048b0152899060249082905afa908115610db657610cb6985f926116b9575b508082106116b0576116aa91610a44565b91611577565b50505f91611577565b6116da919250863d88116116e1575b6116d28183610287565b810190611c19565b905f611699565b503d6116c8565b611700919250873d89116116e1576116d28183610287565b905f611540565b8061171461171a92610236565b806101d5565b5f611502565b611738919250863d88116116e1576116d28183610287565b905f611447565b611757919350843d86116116e1576116d28183610287565b915f611402565b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161561179657565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260ff6117fb3360405f20906001600160a01b03165f5260205260405f2090565b5416156118055750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b6102988151106118ca5761183e61183982611ada565b61129a565b6001600160a01b03309116036118ca578051908160941082609418028218808303928311610a3f5782610fde9260206118796118ab96610326565b936118876040519586610287565b83855261189384610326565b8583019390601f19013685370101905e805190611cb0565b505094509250505060a01c1590816118c1575090565b905060a01c1590565b505f90565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205260ff6119178460405f20906001600160a01b03165f5260205260405f2090565b541661198157815f526020526119418260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916600117905533916001600160a01b0316907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008060205260ff6119d08460405f20906001600160a01b03165f5260205260405f2090565b54161561198157815f526020526119fb8260405f20906001600160a01b03165f5260205260405f2090565b805460ff1916905533916001600160a01b0316907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b6008815110611a48576008015190565b604051632d0483c560e21b8152600490fd5b600c815110611a4857600c015190565b6090815110611a48576090015190565b608c815110611a4857608c015190565b6044815110611a48576044015190565b6064815110611a48576064015190565b6024815110611a48576024015190565b60a4815110611a485760a4015190565b60c4815110611a485760c4015190565b60d8815110611a485760d8015190565b6040519060c0820182811067ffffffffffffffff82111761024a576040525f60a0838281528260208201528260408201528260608201528260808201520152565b67ffffffffffffffff811161024a5760051b60200190565b90602090818382031261002a57825167ffffffffffffffff9384821161002a57019080601f8301121561002a578151611b7b81611b2b565b94604090611b8c6040519788610287565b828752858088019360051b8601019484861161002a57868101935b868510611bb957505050505050505090565b845183811161002a5782019084601f19838903011261002a57845190611bde8261024f565b89830151611beb81610587565b8252858301519185831161002a57611c0a898c80969581960101610e80565b83820152815201940193611ba7565b9081602091031261002a575190565b906109fe94936080936001600160a01b038093168452602084015216604082015281606082015201906109b4565b908151908180821091180218806094108160941802811891828203918211610a3f576020611c8383610326565b93611c916040519586610287565b838552611c9d84610326565b8583019390601f19013685370101905e90565b9081519081808210911802188060e4108160e41802811891828203918211610a3f576020611c8383610326565b611ce783836121a9565b6005819592951015611da457159384611d8e575b508315611d09575b50505090565b5f929350908291604051611d4181610ca36020820194630b135d3f60e11b998a875260248401526040604484015260648301906109b4565b51915afa90611d4e6112c0565b82611d80575b82611d64575b50505f8080611d03565b90915060208180518101031261002a5760200151145f80611d5a565b915060208251101591611d54565b6001600160a01b0383811691161493505f611cfb565b610a51565b905f611e3093819260405194602086019263a9059cbb60e01b84526001600160a01b038093166024880152604487015260448652611de68661026b565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051611e178161024f565b8181520152519082855af1611e2a6112c0565b91612259565b8051908115918215611e46575b50501561002a57565b611e59925060208091830101910161097b565b5f80611e3d565b90611e6a82611b2b565b6040611e796040519283610287565b8382528193611e8a601f1991611b2b565b01905f5b828110611e9b5750505050565b8151906060918281019281841067ffffffffffffffff85111761024a5760209385525f82528390818301525f85830152828701015201611e8e565b8051821015611eea5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b9190916020908181526060916060820192855193604080848601528551809252608085018460808460051b8801019701935f925b848410611f5a575050505050506040906109fe93949501519101906001600160a01b03169052565b9091929394978680600192607f198b82030187528b51906001600160a01b0382511681528580611f95858501518a878601528a8501906109b4565b9301519101529a01940194019294939190611f32565b92918351611fc0611fbb8261102e565b611e60565b925f5b82811061209a575060405163ef8738d360e01b60208201526001600160a01b03918216602482015290831660448083019190915281529394506111f3936109fe93601f1993909261208b9261206f919061201e606482610287565b6120266102f9565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681529060208201525f60408201526120688287611ed6565b5284611ed6565b50612078610319565b9283526001600160a01b03166020830152565b60405193849160208301611efe565b806120b86120aa6001938a611ed6565b51516001600160a01b031690565b6020806120c5848c611ed6565b510151906120e36120d46102f9565b6001600160a01b039094168452565b8201525f60408201526120f68288611ed6565b526121018187611ed6565b5001611fc3565b8115612112570490565b634e487b7160e01b5f52601260045260245ffd5b9190808301809311610a3f57670de0b6b3a76400009283820291808304851490151715610a3f57808201809211610a3f575f198201918211610a3f5761216b91612108565b820391808311610a3f57808202908282041482151715610a3f57828101809111610a3f576109fe9261219f6121a492610a31565b612108565b610a44565b9060418151145f146121d5576121d191602082015190606060408401519301515f1a906121de565b9091565b50505f90600290565b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161224e576020935f9360ff60809460405194855216868401526040830152606082015282805260015afa15610db6575f516001600160a01b0381161561224657905f90565b505f90600190565b505050505f90600390565b90156122735781511561226a575090565b3b1561002a5790565b50805190811561002a57602001fdfea26469706673582212205aa57543ac0ca275e5f6cf9aea12226da06ca1a789701807a553fd671792689c64736f6c6343000818003360c0346200010457601f62005d3e38819003918201601f19168301916001600160401b038311848410176200010857808492604094855283398101031262000104576200005a602062000052836200011c565b92016200011c565b6001600160a01b0390911660805260a052604051615c0c90816200013282396080518181816108b7015281816109ff0152818161158b0152818161182f01528181612a1701528181614a0501528181614b7c01528181614f68015261504e015260a051818181610670015281816107de01528181611a7b01528181613a6e01528181614188015281816141e401528181614288015281816142b40152818161435301526146990152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001045756fe6080604052600436101562000012575f80fd5b5f803560e01c806246912e146200257057806301ffc9a71462002515578063037a06a414620022c957806304c73f6014620022a9578063057f037014620021f45780631f74a0b5146200217257806321081d3c1462002135578063248a9ca314620020e75780632e748b211462001f7f5780632f2ff15d1462001f1a578063319adf9f1462001b6657806336568abe1462001b1757806337710e201462001abd57806339fff0981462001a225780633b1c6a0114620017275780633cf3a02514620016eb5780634265fe861462001699578063490e662f146200165c5780634b3b029b146200161f578063502a82e21462001554578063521c98ba1462000d9157806369b97ac71462000d7157806379c7b60b1462000d1757806379c7f2891462000c1557806390a0827b1462000bd457806391d148541462000b6657806396cc2cfb14620008f9578063a217fddf14620008db578063a4b672b61462000895578063af5de6f91462000802578063c55dae6314620007bc578063ccbedaec1462000542578063d06e28ed1462000443578063d547741f14620003da578063e38b73a914620003bc578063ea0aaf241462000359578063eb84e7f2146200024b5763ff3eae0014620001e2575f80fd5b34620002485760c0366003190112620002485762000200366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10054156200023657620002339062004e31565b80f35b60405163cd6d8f7d60e01b8152600490fd5b80fd5b503462000248576020366003190112620002485760408160c09260a0835162000274816200270b565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051620002c8816200270b565b6001600160a01b039060028284541693848352600181015490602084019185811683526001600160401b0393849283604088019360a01c1683520154956060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b50346200024857602036600319011262000248576020620003b36200037d620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b50346200024857806003193601126200024857602060405160068152f35b50346200024857604036600319011262000248576200043f600435620003ff620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262000439600160408620015462002e64565b620039b1565b5080f35b503462000248576040366003190112620002485762000461620025de565b602435906fffffffffffffffffffffffffffffffff82168092036200053e576200048a62002e08565b6200049581620032e8565b90836001600160a01b039263ffffffff84608083015116915116813b156200053a57829160448392604051948593849263435354d360e01b845260048401528a60248401525af180156200052f5762000513575b5050167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b6200051e90620026f7565b6200052b57835f620004e9565b8380fd5b6040513d84823e3d90fd5b8280fd5b5f80fd5b50346200024857606036600319011262000248578062000561620025de565b6200056b6200260c565b906200057662002623565b906200058162002d37565b620005bc816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200071a574362000601826001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0380600162000648846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b01541693816001600160401b03938483166200071f575b5050508183166200066e578480f35b7f00000000000000000000000000000000000000000000000000000000000000001684525f8051602062005bb783398151915260205260046040852001541690823b156200071a5760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156200052f5762000702575b8080808480f35b6200070d90620026f7565b6200024857805f620006fb565b505050fd5b1686525f8051602062005bb783398151915260205282600460408820015416853b15620007b85760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152858160648183895af1908115620007ad57869162000791575b82906200065f565b6200079c90620026f7565b620007a957845f62000789565b8480fd5b6040513d88823e3d90fd5b8680fd5b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503462000248576020366003190112620002485760206001600160a01b03620008346200082e620025de565b620031ff565b6109ab60405162000848858301826200277c565b818152848101916200520c8339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152620008898162002744565b51902016604051908152f35b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346200024857806003193601126200024857602090604051908152f35b503462000248576040366003190112620002485762000917620025de565b906200092262002e08565b6200092d8262002f58565b6200093883620032e8565b926200094e60e083510151840b60243562004da2565b9460208401906001600160401b036200097381845116898360a08a01511691620038d2565b1562000b3657869750620009b7856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b620009c48582546200287c565b90556001600160a01b03808616807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051898152a2817f000000000000000000000000000000000000000000000000000000000000000016803b1562000b325760405163f3fef3a360e01b81526001600160a01b038316600482015260248101889052908a908290604490829084905af1801562000b275787918b9162000b08575b505062000a8290608060e0960192848451169062003353565b511692511694510151850b93813b1562000b0457604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156200052f5762000af15750f35b62000afc90620026f7565b620002485780f35b8580fd5b62000b15919250620026f7565b62000b235785895f62000a69565b8880fd5b6040513d8c823e3d90fd5b8980fd5b6040516377e88bc960e11b81526001600160a01b03861660048201526001600160401b0389166024820152604490fd5b50346200024857604036600319011262000248576001600160a01b03604062000b8e620025f5565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020522091165f52602052602060ff60405f2054166040519015158152f35b50346200024857604036600319011262000248576200023362000bf6620025de565b62000c0062002d37565b602435906001600160a01b0333911662003353565b503462000248576020366003190112620002485760a062000c7a62000c39620025de565b62000c4362002d0b565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b0360016040519262000c9384620026db565b805463ffffffff90818116865260ff8160201c1615156020870152818160281c16604087015260481c166060850152015416608082015262000d1560405180926001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565bf35b50346200024857602036600319011262000248576020620003b362000d3b620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b503462000248578060031936011262000248576020604051620f42408152f35b5034620002485760a0366003190112620002485762000daf620025de565b62000db96200263a565b9062000dc46200264e565b6001600160401b0360643516606435036200053e57608435906001600160401b03821682036200053e5762000df862002db1565b62000e0262002c05565b508480604051602081019063ffffffff881682526020815262000e258162002760565b519061080c5afa62000e3662002cd7565b901562001542578051810190602081830312620007b85760208101516001600160401b038111620015055761010081830184031262001505576040519262000e7e8462002727565b602082840101516001600160401b03811162000b325760208201603f828587010101121562000b32576020818486010101519062000ebc8262002cbb565b9162000ecc60405193846200277c565b8083526020840160408284888a01010101116200152a5762000efa9160406020850191878901010162002691565b8452604082840101516001600160401b03811162000b325760208201603f828587010101121562000b3257602081848601010151916001600160401b0383116200152e578260051b6040519362000f5560208301866200277c565b84526020808501920160408285888a01010101116200152a576040838688010101915b60408285888a01010101831062001509575050505060208401526101009062000fa683820160600162003428565b604085015262000fbb6080828501016200512c565b606085015262000fd060a0828501016200512c565b9283608086015262000fe760c08383010162005141565b60a086015262000ffc60e08383010162005141565b60c08601520101519081880b820362001505578160e08401526001600160a01b03808716911603620007b8576200103890870b60643562003829565b506040519162001048836200270b565b825263ffffffff86166020830152821515604083015260608201526001600160401b036064351660808201526001600160401b03831660a08201526001600160a01b03841686525f8051602062005bb783398151915260205260408620815180518051906001600160401b03821162001438578190620010c9855462002c80565b601f8111620014c5575b50602090601f831160011462001458578b926200144c575b50508160011b915f199060031b1c19161782555b60208101518051906001600160401b038211620014385768010000000000000000821162001438576020906001850154836001870155808410620013e5575b500190600184018a5260208a20908a5b8160021c81106200139d5750600319811680820362001334575b50505050936001600160a01b03937f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd82244353969360068460809860026001600160401b039701876040830151168154907bffffffffffffffffffffffffffffffffffffffff0000000000000000606085015160401b169163ffffffff60e01b16171790556003820190898b820151169082549174ff000000000000000000000000000000000000000060a083015160a01b16907fffffffffffffffffff000000000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff00000000000000000000000000000000000000000000169416171717179055620012c5600482018760208601511688198254161781556040850151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b6060830151600582015501908488820151166fffffffffffffffff000000000000000060a0845493015160401b16916fffffffffffffffffffffffffffffffff19161717905563ffffffff6040519816885215156020880152816064351660408801521660608601521692a280f35b928b938c5b81840381106200135d5750505060021c0155838360066001600160401b0362001168565b9091946020620013926001926001600160401b03895116908560031b60031b916001600160401b03809116831b921b19161790565b960192910162001339565b8b8c5b60048110620013b75750838201556001016200114e565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501620013a0565b6200141890600187018d52838d20600380870160021c820192601888831b16806200141f575b500160021c019062004d8a565b5f6200113e565b5f1990818601918254918a03851b1c1690555f6200140b565b634e487b7160e01b8a52604160045260248afd5b015190505f80620010eb565b9250848b5260208b20908b935b601f1984168510620014a9576001945083601f1981161062001490575b505050811b018255620010ff565b01515f1960f88460031b161c191690555f808062001482565b8181015183556020948501946001909301929091019062001465565b620014f390868d5260208d20601f850160051c81019160208610620014fa575b601f0160051c019062004d8a565b5f620010d3565b9091508190620014e5565b8780fd5b906020806040936200151b8662003428565b81520193019290915062000f78565b8b80fd5b634e487b7160e01b8b52604160045260248bfd5b604051639b0c335d60e01b8152600490fd5b503462000248576040366003190112620002485762001572620025de565b6024356200157f62002d37565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156200161b5760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156200052f5762001603575b50506200023391339062003353565b6200160e90620026f7565b6200053a57825f620015f4565b5080fd5b5034620002485780600319360112620002485760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b5034620002485780600319360112620002485760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b5034620002485760c03660031901126200024857620016b8366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576200023390620046c4565b50346200024857602036600319011262000248576200170962002d37565b80808080600435335af16200171d62002cd7565b5015620002485780f35b503462000248576060366003190112620002485762001745620025f5565b6001600160a01b0360443581811692918382036200053e576200176762002e08565b620017728262002f58565b906200177e8462003257565b62000b045760408201511562000b04576001600160401b0391600183608083015116019083821162001a0e57620017bd60e082510151890b8362003829565b5094620017db602083019386855116908760a08601511691620038d2565b1562000b23576200181c906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b620018298682546200287c565b905587837f000000000000000000000000000000000000000000000000000000000000000016803b156200161b5760405163f3fef3a360e01b81526001600160a01b038a166004820152602481018890529082908290604490829084905af180156200052f57620019f0575b50509183826200192494620018bc60e062001913986024975116925101518c0b8962004da2565b508481620019d0575b505050511695604051916020830191169687825260408301526001606083015260608252620018f48262002744565b6040519485926280000360e11b60208501525180928585019062002691565b81010360048101845201826200277c565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062001970906024830190620026b4565b03925af18015620019c557620019b3575b506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b620019be90620026f7565b5f62001981565b6040513d5f823e3d90fd5b620019df620019e79362002847565b168b62003353565b5f8084620018c5565b620019ff9094929394620026f7565b62001505579190875f62001895565b634e487b7160e01b5f52601160045260245ffd5b5034620002485760e0366003190112620002485762001a41366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576001600160a01b0380606083015116907f000000000000000000000000000000000000000000000000000000000000000016145f1462001aae576200023390620046c4565b620002339060c4359062003a45565b50346200024857602036600319011262000248576020620003b362001ae1620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b503462000248576040366003190112620002485762001b35620025f5565b336001600160a01b0382160362001b54576200043f90600435620039b1565b60405163334bd91960e11b8152600490fd5b50346200024857602036600319011262000248576001600160a01b0362001b8c620025de565b62001b9662002c45565b501681525f8051602062005bb783398151915260205260408120906040519162001bc0836200270b565b60405162001bce8162002727565b6040518254818562001be08362002c80565b808352926001811690811562001ef9575060011462001eb4575b62001c08925003826200277c565b815260405160018301805480835290855260208086209083019186915b81600384011062001e72579360069593819362001c73936001600160401b039997549181811062001e5e575b81811062001e47575b81811062001e30575b1062001e21575b5003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c850b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a0830152604051906020825282519060c0602084015262001d31825161010060e08601526101e0850190620026b4565b60208084015185830360df190161010087015280518084529282019492910190835b81811062001e015750505083946001600160401b039260e08385604060a0960151166101208901526001600160a01b036060820151166101408901526001600160a01b0360808201511661016089015260ff858201511661018089015260ff60c0820151166101a08901520151900b6101c086015282602082015116604086015260408101511515606086015260608101516080860152826080820151168286015201511660c08301520390f35b82516001600160401b031686526020958601959092019160010162001d53565b60c01c81526020015f62001c6a565b9260206001918c8560801c16815201930162001c63565b9260206001918c8560401c16815201930162001c5a565b9260206001918c8516815201930162001c51565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162001c25565b5090848652602086209086915b81831062001edc57505090602062001c089282010162001bfa565b602091935080600191548385880101520191019091839262001ec1565b6020925062001c0894915060ff191682840152151560051b82010162001bfa565b50346200024857604036600319011262000248576200043f60043562001f3f620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262001f79600160408620015462002e64565b62003912565b503462000248576080366003190112620002485762001f9d620025de565b62001fa76200260c565b9062001fb262002623565b90606435926fffffffffffffffffffffffffffffffff84168094036200053e5762001fdc62002e08565b62001fe782620032e8565b856001600160a01b03928360808401511692833b156200053a57620020646101048492836040519586948593636f0d192560e11b855260048501906001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565b6001600160401b038091169b8c60a485015216978860c48401528c60e48401525af180156200052f57620020cb575b5050907f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c92604092835195865260208601521692a380f35b620020d990939293620026f7565b62000b045790855f62002093565b50346200024857602036600319011262000248576001604060209260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008452200154604051908152f35b5034620002485780600319360112620002485760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503462000248576060366003190112620002485762002190620025de565b6001600160401b036024358181116200052b57620021b39036906004016200265e565b91909360443591821162000248576020620021ec868686620021d936600489016200265e565b939092620021e662002e08565b6200288a565b604051908152f35b50346200053e5760403660031901126200053e5762002212620025de565b906024356200222062002d37565b6001600160a01b03928360806200223783620032e8565b015116803b156200053e576040516390a0827b60e01b81526001600160a01b038316600482015260248101849052905f908290604490829084905af18015620019c55762002290575b5062000233929333911662003353565b620002339350620022a190620026f7565b5f9262002280565b346200053e575f3660031901126200053e5760206040516305f5e1008152f35b346200053e5760a03660031901126200053e57620022e6620025de565b620022f06200263a565b620022fa6200264e565b6064359063ffffffff938483168093036200053e57608435908582168092036200053e57620023298162002f58565b506200233462002db1565b6001600160a01b03918260016200237b846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b0154168015620024bc575b8360018960405199620023998b620026db565b16988981526020810197151597888152604082018a81526060830190878252856080850197169d8e8852620023fe8a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b9451166cffffffff00000000000000000068ffffffff000000000064ff0000000087549651151560201b16935160281b16935160481b16936cffffffffffffffffffffffffff19161717171781550191511673ffffffffffffffffffffffffffffffffffffffff19825416179055620024778762003257565b156200053e577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9946080946040519788526020880152604087015260608601521692a3005b50620024c882620031ff565b6040516109ab8082018281106001600160401b03821117620025015782916200520c833903905ff58062002386576040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b346200053e5760203660031901126200053e5760043563ffffffff60e01b81168091036200053e57602090637965db0b60e01b81149081156200255e575b506040519015158152f35b6301ffc9a760e01b1490508262002553565b346200053e5760403660031901126200053e57620025dc62002591620025de565b6001600160a01b03620025a36200260c565b91620025ae62002d37565b165f525f8051602062005bb78339815191526020526001600160401b03600460405f20015416339062002ea6565b005b600435906001600160a01b03821682036200053e57565b602435906001600160a01b03821682036200053e57565b602435906001600160401b03821682036200053e57565b604435906001600160401b03821682036200053e57565b6024359063ffffffff821682036200053e57565b6044359081151582036200053e57565b9181601f840112156200053e578235916001600160401b0383116200053e576020808501948460051b0101116200053e57565b5f5b838110620026a35750505f910152565b818101518382015260200162002693565b90602091620026cf8151809281855285808601910162002691565b601f01601f1916010190565b60a081019081106001600160401b038211176200250157604052565b6001600160401b0381116200250157604052565b60c081019081106001600160401b038211176200250157604052565b61010081019081106001600160401b038211176200250157604052565b608081019081106001600160401b038211176200250157604052565b604081019081106001600160401b038211176200250157604052565b90601f801991011681019081106001600160401b038211176200250157604052565b60c09060031901126200053e5760405190620027ba826200270b565b60043582526024356020830152816001600160a01b0360443581811681036200053e57604083015260643590811681036200053e576060820152608435608082015260a060a435910152565b9190811015620028175760051b0190565b634e487b7160e01b5f52603260045260245ffd5b9190916001600160401b038080941691160191821162001a0e57565b9073200000000000000000000000000000000000000091820180921162001a0e57565b906305f5e0ff820180921162001a0e57565b9190820180921162001a0e57565b919392935f945f948083036200053e57620028d5856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200053e57869796939291620028ef8662002f58565b926080620028fd88620032e8565b01906001600160a01b0392838351169460208701976200292a6001600160401b0397888b5116906200343d565b845f9b5b1062002b94575b50508c15925062002b8791505057436200297f886001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5582861662002993575b5050505050505050565b620029c195620029ab60e0865101515f0b8262003829565b85889992995116908660a08901511691620038d2565b1562002b4d575062002a03876001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62002a108782546200287c565b90558682167f00000000000000000000000000000000000000000000000000000000000000008316803b156200053e5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101899052905f908290604490829084905af18015620019c55762002b29575b509062002a9587849360e096958451169062003353565b511693511691510151850b93823b1562000b0457604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156200052f5762002b11575b80808080808062002989565b62002b1d8291620026f7565b62000248578062002b05565b60e094939291995062002b3c90620026f7565b5f98929350909162002a9562002a7e565b8451608001516040516377e88bc960e11b81529084166001600160a01b031660048201526001600160401b03919091166024820152604490fd5b505f985050505050505050565b62002ba6859e9f809c95968462002806565b359062002bb58c858862002806565b359189831683036200053e578a62002bd2938a8a51169262003520565b9490911562002bfb5762002beb8f93926001926200282b565b9b019e9d8f95949192956200292e565b9a9e9d5062002935565b6040519062002c148262002727565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b6040519062002c54826200270b565b5f60a08362002c6262002c05565b81528260208201528260408201528260608201528260808201520152565b90600182811c9216801562002cb0575b602083101462002c9c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162002c90565b6001600160401b0381116200250157601f01601f191660200190565b3d1562002d06573d9062002ceb8262002cbb565b9162002cfb60405193846200277c565b82523d5f602084013e565b606090565b6040519062002d1a82620026db565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a60205260409020547f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f9060ff161562002d935750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161562002dea57565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd60205260409020547f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef69060ff161562002d935750565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f2054161562002d935750565b620019139262002eec92602492604051926001600160a01b0360208501931683526001600160401b03809216604085015216606083015260608252620018f48262002744565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062002f38906024830190620026b4565b03925af18015620019c55762002f4b5750565b62002f5690620026f7565b565b9062002f6362002c45565b506001600160a01b038092165f5260205f8051602062005bb78339815191528152604092835f2084519062002f98826200270b565b85519562002fa68762002727565b80518254905f8162002fb88462002c80565b918282526001948a86821691825f14620031e05750506001146200319f575b62002fe5925003826200277c565b88528083019082519087829384928282549586815201915f52825f20945f5b8160038201106200315a57620030469654928583831062003140575b83831062003124575b83831062003107575b505010620030f8575b50905003826200277c565b858801526006600283015492856001600160401b0394858116858c0152841c1660608a015260ff60038201549760808b019a888a168c52828a60a01c1660a082015260c0810199838160a81c168b5260b01c5f0b60e0820152875260048301549086821690880152841c161515838601526005810154606086015201549082821660808501521c1660a082015293511615159081620030e9575b50156200053e57565b60ff9150511615155f620030e0565b60c01c81520188905f6200303b565b9091946001600160401b038560801c1681520193015f8562003032565b8192956001600160401b03868d1c168152019401908562003029565b8192956001600160401b0386168152019401908562003020565b86546001600160401b038082168652818b1c811696860196909652608081811c9096168a86015260c01c6060850152958201958795508c949093019260040162003004565b5050845f528188805f20855f915b858310620031c657505062002fe5935082010162002fd7565b8091929450548385880101520191018990858593620031ad565b60ff19168482015262002fe594151560051b840101915062002fd79050565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152606081018181106001600160401b03821117620025015760405251902090565b519081151582036200053e57565b5f80916040516001600160a01b036020820192168252602081526200327c8162002760565b51906108105afa6200328d62002cd7565b9015620032d6576020818051810103126200053e5760405190602082018281106001600160401b038211176200250157604052620032ce9060200162003249565b809152151590565b6040516313dd7ccd60e31b8152600490fd5b620032f79062000c4362002d0b565b906001600160a01b036001604051936200331185620026db565b805463ffffffff90818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156200053e57565b60405163a9059cbb60e01b602082019081526001600160a01b039384166024830152604480830195909552938152620033ea9390925f92839291906200339b6064876200277c565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051620033ce8162002760565b8181520152519082855af1620033e362002cd7565b91620051de565b805190811591821562003402575b5050156200053e57565b81925090602091810103126200053e57602062003420910162003249565b5f80620033f8565b51906001600160401b03821682036200053e57565b604080516001600160a01b0392909216602083019081526001600160401b039390931682820152808252915f918291906200347a6060826200277c565b51906108015afa6200348b62002cd7565b9015620034f5576060818051810103126200053e5781516001600160401b0392606082018481118382101762002501578152620034ee6060620034d16020860162003428565b94858552620034e284820162003428565b60208601520162003428565b9101521690565b8151639d2c8fcb60e01b8152600490fd5b6001600160401b03918216908216039190821162001a0e57565b949192909394805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20906001600160a01b039485835416958615620037ab57600284019788549860ff8a60481c166200379957600186015483608086510151168482160362003780576001600160401b039060a01c166001600160401b038b1660ff8c60401c165f146200371f575080926001600160401b03811682115f146200371657620035d79162003506565b995b620035e58b8d6200282b565b9b6001600160401b038d166001600160401b0385161162003704575069010000000000000000009069ff00000000000000000019161790556200362b8160019b62003506565b976001600160401b036020846200364b8d60e0895101515f0b9062003829565b50991695015116843b156200053e5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152925f908490606490829084905af1918215620019c5577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4393604093620036f2575b5060018187541696015416956001600160401b038351921682526020820152a4565b620036fd90620026f7565b5f620036d0565b5f9c508c9b5099505050505050505050565b50505f620035d7565b9092809b93806001600160401b038316105f1462003773576200374f91620037479162003506565b809c6200282b565b816001600160401b038216115f146200376b57505b91620035d9565b905062003764565b50506200374f5f62003747565b60405163358d72d160e01b815260048101879052602490fd5b60405163f7348a7960e01b8152600490fd5b604051631a40316d60e01b8152600490fd5b60ff16604d811162001a0e57600a0a90565b9190820391821162001a0e57565b8115620037e8570490565b634e487b7160e01b5f52601260045260245ffd5b90620f42409182810292818404149015171562001a0e57565b8181029291811591840414171562001a0e57565b9190805f0b80155f14620038475750506001600160401b0382169190565b5f81131562003877575062003863620038739160ff16620037bd565b6001600160401b03841662003815565b9190565b9050607f19811462001a0e5762003893905f0360ff16620037bd565b91620038aa836001600160401b038093166200287c565b5f19810190811162001a0e57620038c684620038ce92620037dd565b938462003815565b1690565b6200390a6200390392936001600160401b0393836001600160a01b03620038fc8780971662002847565b166200343d565b946200282b565b169116101590565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f205416155f14620039aa57825f5260205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f2054165f14620039aa57825f5260205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b9062003a5e6001600160a01b0360408401511662003257565b1562004618576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165f525f8051602062005bb783398151915260205260405f20906040519162003ab6836200270b565b60405162003ac48162002727565b6040518254815f62003ad68362002c80565b8083529260018116908115620045f75750600114620045b2575b62003afe925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b81600384011062004570579360069593819362003b67936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a08301526001600160a01b036060840151165f525f8051602062005bb783398151915260205260405f206040519062003c2e826200270b565b60405162003c3c8162002727565b6040518254815f62003c4e8362002c80565b80835292600181169081156200454f57506001146200450a575b62003c76925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b816003840110620044bd579360069593819362003cdf936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015262003d896001600160a01b03606086015116620032e8565b9184519062003dd662003dbb62003dac60a089015160808a01511515956200287c565b60e0885101515f0b9062004da2565b905060ff60c0885101511660ff60c087510151169162005165565b91156200448c5781925b5f8063ffffffff875116604051602081019182526020815262003e038162002760565b51906108085afa9062003e1562002cd7565b91156200447a576020828051810103126200053e576001600160401b0362003e44602062003e6d940162003428565b60208801519116919015620044665760ff60a062003e669251015116620037bd565b9062003815565b602085015115620044175763ffffffff62003e8e8160608801511662005026565b16620f4240818101811162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c01511691016200287c565b0416906080870151908115155f146200440e5750905b602085015115620043c5576305f5e1008110156200437e57505f5b62003f2462003f0b60a0890151620037fc565b62003f1d895160a08b0151906200287c565b906200287c565b5f1981019190821162001a0e5762003f1d62003f539262003f4c8a5160a08c0151906200287c565b90620037dd565b606482028281046064148315171562001a0e578111620042df57505062003f84855160e0865101515f0b9062004da2565b92909562003faf6001600160401b03602088015116856001600160401b0360a08a01511691620038d2565b156200423b5791608093917f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91860a0856001600160401b036001600160a01b036040819b990151168a606084015116908a8401511515916040519162004014836200270b565b82526020820152828816604082015282871660608201528a81019182528481015f815262004118602086019384515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b03602052856060600260405f206001600160a01b0388511673ffffffffffffffffffffffffffffffffffffffff19825416178155600181016001600160a01b0360208a0151168154907bffffffffffffffff000000000000000000000000000000000000000060408c015160a01b169163ffffffff60e01b161717905501950151168619855416178455511515839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151569ff00000000000000000082549160481b169069ff00000000000000000019161790555194818b60408501511697818d6060870151169a87875197015160405197885260208801521660408601521660608401521688820152a4015116620041ae83826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001662003353565b60e06001600160401b03602084015116925101515f0b90803b156200053e57604051639c45c34b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160401b03909316602484015260448301939093525f90810b6064830152909182908183816084810162002f38565b94505050905062002f56925060208201516001600160401b03604051921682527f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c60206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b9350949350505060208201516001600160a01b0360608401511691606481019081811162001a0e5760630190811162001a0e5760407fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e9162002f569660648351920482526020820152a36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166060820152620046c4565b6001600160401b036305f5e0ff1982011162001a0e57620043be620043b86001600160401b036305f5e100936305f5e0ff190116620037fc565b6200286a565b0462003ef8565b6305f5e100811115620043da57505f62003ef8565b6001600160401b03816305f5e100031162001a0e57620043be620043b86001600160401b036305f5e10093840316620037fc565b90509062003edd565b63ffffffff6200442d8160608801511662005026565b16620f4240808281031162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c0151169103620037cf565b5062003e6660ff60a08951015116620037bd565b604051635cffc5fb60e11b8152600490fd5b6127108181031162001a0e576001600160401b03612710620044b383820383861662003815565b0416919262003de0565b926001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019162003c95565b5090845f5260205f20905f915b8183106200453257505090602062003c769282010162003c68565b602091935080600191548385880101520191019091839262004517565b6020925062003c7694915060ff191682840152151560051b82010162003c68565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162003b1d565b5090845f5260205f20905f915b818310620045da57505090602062003afe9282010162003af0565b6020919350806001915483858801015201910190918392620045bf565b6020925062003afe94915060ff191682840152151560051b82010162003af0565b506080810151909190156200464c5760246001600160a01b03604084015116604051906320a2097d60e11b82526004820152fd5b9080602062002f569201517f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b03604085015116604051908152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b6060906060810151905f6001600160a01b0380931692835f526020945f8051602062005bb78339815191528652604091825f2096835162004705816200270b565b845193620047138562002727565b85518a5491905f81620047268562002c80565b918282526001958887821691825f1462004d6b57505060011462004d2a575b62004756925094939403826200277c565b8652808b019184885193898586948484549283815201935f52845f20965f915b83600384011062004cde575085620047b998549484841062004cc4575b84841062004ca7575b5083831062004c8a575b50501062004c7b5750905003826200277c565b82850152600289015495836001600160401b039788811688880152871c16606086015260038a0154946080958581168783015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528252600660048b01549a60ff8585019c8d8b82169052891c1615158885015260058101546060850152015490878216868401528760a0840192881c168252868901976200485e868a511662003257565b1562004c0d57899a9b5f9a98999a508c612710620048958b519b8b6200488b60a083019e8f51906200287c565b9101519062003815565b04988a5199808b1162004c04575b508d8a8062004b2e575b620048c2620048d191620048e493516200287c565b60e08a5101515f0b9062004da2565b91909782878088511692511691620038d2565b1562004ae65750908c9695949392918a620049f9575b509260e0896200498f94620049c59b9997947fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b99976200496e906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6200497b8d82546200287c565b9055511696511694510151900b9062004da2565b9190928d84620049ca575b5050505050880151965116965192519351938493846040919493926060820195825260208201520152565b0390a4565b620049ee94620049e89188620049e08562002847565b169062003353565b62002ea6565b5f8080808d6200499a565b919293949596505087807f00000000000000000000000000000000000000000000000000000000000000001691875101511690803b156200053e578b5163f3fef3a360e01b81526001600160a01b03929092166004830152602482018a90525f908290604490829084905af1801562004adc578c948f91998098956200498f957fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b9995620049c59d62004ac0575b509497995094509950939597999a9b620048fa565b60e0945062004acf90620026f7565b6200496e5f945062004aab565b8b513d5f823e3d90fd5b9a50505050955050505093957f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c955084915062004b238162004e31565b0151945191168152a3565b62004b45908a8301518c858c51015116906200503f565b1562004b53575b8a620048ad565b509192939495968881999a50602492508389510151168c51928380926370a0823160e01b82528d7f00000000000000000000000000000000000000000000000000000000000000001660048301525afa90811562004adc57918e918e9a999897969594935f9162004bc7575b509962004b4c565b92505098508681813d831162004bfc575b62004be481836200277c565b810103126200053e57518c988e91620048d162004bbf565b503d62004bd8565b99505f620048a3565b505050509296509450839293015115155f1462004c405750505191516320a2097d60e11b81529116600482015260249150fd5b62002f56948294827f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d9401519451169051908152a262004e31565b60c01c81520185905f6200303b565b9091946001600160401b038560801c1681520193015f85620047a6565b956001600160401b03868495981c16815201940190858e6200479c565b6001600160401b0386168752958101959282019262004793565b95509360049296509284919388546001600160401b039283821683528d8483831c169084015260809382851c169083015260c01c8782015201960191019386948c9296938a9562004776565b50508c5f528186805f20865f915b85831062004d5157505062004756935082010162004745565b809192945054838588010152019101879086859362004d38565b60ff1916848201526200475694151560051b8401019150620047459050565b81811062004d96575050565b5f815560010162004d8a565b9190805f0b9081155f1462004dc05750506001600160401b03821690565b5f82131562004e035762004dd8915060ff16620037bd565b918215620037e857620038ce62004dfb82856001600160401b03940690620037cf565b9384620037dd565b505f0380805f0b0362001a0e57620038ce62004e2a6001600160401b039260ff16620037bd565b8462003815565b805161271062004e5862004e4c60a08501938451906200287c565b60808501519062003815565b0481518181115f146200501d5750905b8190602084019182519162004e8f60608701956001600160a01b039485885116906200503f565b1562005014575b8162004f61575b620049c5837f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9462004ed1858a516200287c565b9662004eeb828a51169860408c0199848b51169062003353565b62004f29828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62004f368782546200287c565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b84518316927f00000000000000000000000000000000000000000000000000000000000000008116803b156200053e5760405163f3fef3a360e01b81526001600160a01b03959095166004860152602485018490525f908590604490829084905af1908115620019c5577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f94620049c59262005002575b5093505062004e9d565b6200500d90620026f7565b5f62004ff8565b5f915062004e96565b90509062004e68565b90606463ffffffff8093160291821691820362001a0e57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa918215620019c5575f92620050f2575b5080821094851595620050b2575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080620050ac565b9091506020813d60201162005123575b8162005111602093836200277c565b810103126200053e5751905f6200509e565b3d915062005102565b51906001600160a01b03821682036200053e57565b519060ff821682036200053e57565b9060ff8091169116039060ff821162001a0e57565b919060ff821660ff82168181145f1462005180575050505090565b929391921015620051b857620038ce9192620051b0620051aa6001600160401b0395869362005150565b620037bd565b911662003815565b620038ce91620051d6620051aa6001600160401b0395869362005150565b9116620037dd565b9015620051fb57815115620051f1575090565b3b156200053e5790565b5080519081156200053e57602001fdfe60a080604052346100455733608052610961908161004a8239608051818181608b015281816101ac0152818161038e015281816104a20152818161051b015261069c0152f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f915f3560e01c908163435354d3146104d757816390a0827b1461047b5781639c45c34b14610469578163a703334f1461033c578163de1a324a146100d457508063e94b77c1146100b35763eba61c0e1461006d575f80fd5b346100af57816003193601126100af57602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b82346100d1576100ce6100c5366105b5565b9291909161068d565b80f35b80fd5b91905034610338576101009136600319018381126103345760a01361033057815167ffffffffffffffff939060a081018581118282101761031d5784526101196105a2565b91828252602435918215158303610319576020810192835263ffffffff9360443585811681036102ce578783015260643585811681036102ce5760608301526084356001600160a01b03928382168203610315576080015260a4358881168091036102ce5760c435918983168093036102ce5760e435936fffffffffffffffffffffffffffffffff85168095036102ce577f000000000000000000000000000000000000000000000000000000000000000016330361031557866101dd9116610663565b9451151581156103055782156102f5578a6102e557885196602088019616865288870152606086015260808501525f60a0850152600260c085015260e084015260e08352820194828610908611176102d25761026891602491868652630100000160e01b6101208301526102598251809261012485019061079c565b81010383810186520184610641565b73333333333333333333333333333333333333333392833b156102ce576102a5935f928385518097819582946317938e1360e01b845283016107bd565b03925af19081156102c557506102b9575080f35b6102c391506105fd565b005b513d5f823e3d90fd5b5f80fd5b604183634e487b7160e01b5f525260245ffd5b88516376d4929560e11b81528890fd5b88516313c0a8df60e01b81528890fd5b885163017461b760e71b81528890fd5b8a80fd5b8780fd5b604184634e487b7160e01b5f525260245ffd5b8380fd5b8480fd5b8280fd5b919050346103385760603660031901126103385781359067ffffffffffffffff928383168093036102ce576024356001600160a01b03948582168092036102ce576044359081168091036102ce5786957f000000000000000000000000000000000000000000000000000000000000000016330361046557610259946103fc9260249286519260208401928352878401526060830152606082526103df82610625565b85519687926280000360e11b60208501525180928585019061079c565b73333333333333333333333333333333333333333392833b15610334576104399385928385518097819582946317938e1360e01b845283016107bd565b03925af190811561045c575061044c5750f35b610455906105fd565b6100d15780f35b513d84823e3d90fd5b8580fd5b83346100d1576100ce6100c5366105b5565b91905034610338573660031901126100af57356001600160a01b038082168092036102ce577f00000000000000000000000000000000000000000000000000000000000000001633036100af576100ce906024359033906107e9565b919050346102ce57806003193601126102ce576104f26105a2565b91602435926fffffffffffffffffffffffffffffffff84168094036102ce576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102ce5761054a90610663565b9282519063ffffffff602083019516855283820152828152606081019381851067ffffffffffffffff8611176102d25761026891602491868652630100000b60e01b608083015261025982518092608485019061079c565b6004359063ffffffff821682036102ce57565b60809060031901126102ce576004356001600160a01b03811681036102ce579060243567ffffffffffffffff811681036102ce579060443590606435805f0b81036102ce5790565b67ffffffffffffffff811161061157604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761061157604052565b90601f8019910116810190811067ffffffffffffffff82111761061157604052565b9061271063ffffffff8093160191821161067957565b634e487b7160e01b5f52601160045260245ffd5b9092916001600160a01b0392837f00000000000000000000000000000000000000000000000000000000000000001633036102ce57805f0b80155f1461071c575050915b826106dd575b50505050565b67ffffffffffffffff732000000000000000000000000000000000000000941684018094116106795780610713941691166107e9565b5f8080806106d7565b5f81131561075c57506107319060ff166108f1565b8015610748578106810390811161067957916106d1565b634e487b7160e01b5f52601260045260245ffd5b90505f9391930380805f0b03610679576107789060ff166108f1565b9280938181810204149015176106d157634e487b7160e01b5f52601160045260245ffd5b5f5b8381106107ad5750505f910152565b818101518382015260200161079e565b604091602082526107dd815180928160208601526020868601910161079c565b601f01601f1916010190565b9190604051906020938483019363a9059cbb60e01b85526001600160a01b03809316602485015260448401526044835261082283610625565b169160405190604082019067ffffffffffffffff9383831085841117610611577f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564875f9586956040528181520152519082865af13d156108e4573d918211610611576108ac926040519261089f86601f19601f8401160185610641565b83523d5f8685013e610902565b80519182159182156108c4575b50509050156102ce57565b8092508193810103126102ce57015180151581036102ce57805f806108b9565b6108ac9260609250610902565b60ff16604d811161067957600a0a90565b901561091c57815115610913575090565b3b156102ce5790565b5080519081156102ce57602001fdfea2646970667358221220c909e7dbe656d7c973709e34eb673837a89b1ae88012e9262063134290e6f21f64736f6c634300081800336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00a2646970667358221220d7009235facd2afacf7dbd784aa03c97dd6444be46f4a33aa486147317b81a7764736f6c6343000818003300000000000000000000000081d40f21f12a8f0e3252bccb954d722d4c464b640000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d000000000000000000000000002e76dc036a1eff1488ee5435ee66c6abf32674000000000000000000000000b88339cb7199b77e23db6e890353e22632ba630f0000000000000000000000005e7840e06faccb6d1c3b5f5e0d1d3d07f2829bba", + "nonce": "0x244", "chainId": "0x3e7" }, "additionalContracts": [ { "transactionType": "CREATE", "contractName": "HyperCoreFlowExecutor", - "address": "0x2483711d260d346d8bc1dd69b6b5465dd18e84ea", - "initCode": "0x60c0346100d157601f615ba038819003918201601f19168301916001600160401b038311848410176100d55780849260409485528339810103126100d157610052602061004b836100e9565b92016100e9565b6001600160a01b0390911660805260a052604051615aa290816100fe8239608051818181610842015281816109820152818161143a015281816116c7015281816127bb015281816145ca015281816146ec01528181614a5c0152614c00015260a051818181610610015281816107740152818161191101526136c80152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036100d15756fe60806040526004361015610011575f80fd5b5f5f3560e01c806246912e146123be57806301ffc9a714612368578063037a06a41461211c57806304c73f60146120fe578063057f0370146120445780631f74a0b514611fd257806321081d3c14611f97578063248a9ca314611f575780632e748b2114611dfb5780632f2ff15d14611db0578063319adf9f146119e857806336568abe146119a357806337710e201461194e57806339fff098146118b75780633b1c6a01146115bc5780633cf3a025146115875780634265fe861461153c578063490e662f146115015780634b3b029b146114c6578063502a82e214611409578063521c98ba14610cd857806369b97ac714610cba57806379c7b60b14610c6557806379c7f28914610b6657806390a0827b14610b2d57806391d1485414610ad657806396cc2cfb14610882578063a217fddf14610866578063a4b672b614610822578063af5de6f914610798578063c55dae6314610754578063ccbedaec146104e9578063d06e28ed146103fc578063d547741f146103a8578063e38b73a91461038c578063ea0aaf241461032e578063eb84e7f21461021a5763ff3eae00146101bb575f80fd5b346102175760c0366003190112610217576101d536612562565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1005415610208576102059061492d565b80f35b63cd6d8f7d60e01b8252600482fd5b80fd5b50346102175760203660031901126102175760408160c09260a0835161023f8161250a565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051906102928261250a565b6001600160a01b03815416918281526001600160401b0360018301549281600260208501926001600160a01b038716845282604087019760a01c1687520154946001600160a01b036060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b503461021757602036600319011261021757602061038361034d612421565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b5034610217578060031936011261021757602060405160068152f35b5034610217576040366003190112610217576103f86004356103c8612437565b906103f36103ee825f525f516020615a4d5f395f51905f52602052600160405f20015490565b612be7565b613628565b5080f35b503461021757604036600319011261021757610416612421565b602435906fffffffffffffffffffffffffffffffff82168092036104da5761043c612b78565b8261044682613027565b63ffffffff6001600160a01b03608083015116915116813b156104da57829160448392604051948593849263435354d360e01b845260048401528960248401525af180156104de576104c5575b50506001600160a01b03167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b816104cf91612541565b6104da57825f610493565b8280fd5b6040513d84823e3d90fd5b50346102175760603660031901126102175780610504612421565b61050c61244d565b610514612463565b9061051d612aba565b610557836001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156106b2574361059a846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0360016105df856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b015416926001600160401b0382166106b7575b50506001600160401b038116610606575050f35b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001683525f516020615a2d5f395f51905f526020526001600160401b0360046040852001541690823b156106b25760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156104de576106a15750f35b816106ab91612541565b6102175780f35b505050fd5b6001600160a01b031684525f516020615a2d5f395f51905f526020526001600160401b03600460408620015416833b156107505760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152838160648183875af1908115610745578491156105f2578161073691612541565b61074157825f6105f2565b5050fd5b6040513d86823e3d90fd5b8480fd5b503461021757806003193601126102175760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346102175760203660031901126102175760206001600160a01b036107c46107bf612421565b612f58565b610b046040516107d685830182612541565b81815284810191614f298339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152610816607582612541565b51902016604051908152f35b503461021757806003193601126102175760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5034610217578060031936011261021757602090604051908152f35b50346102175760403660031901126102175761089c612421565b906108a5612b78565b6108ae82612d18565b6108b783613027565b926108cb60e083510151840b6024356148ac565b9460208401906108f46001600160401b03835116886001600160401b0360a08901511691613553565b15610aad57859650610936846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b610941848254612623565b90556001600160a01b038416807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051878152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b15610aa95760405163f3fef3a360e01b81526001600160a01b0383166004820152602481018690529088908290604490829084905af18015610a9e5785918991610a81575b50506001600160401b0391610a116001600160a01b0392608060e0960192848451169061308b565b511692511694510151850b93813b15610a7d57604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156104de576106a15750f35b8580fd5b81925090610a8e91612541565b610a9a5783875f6109e9565b8680fd5b6040513d8a823e3d90fd5b8780fd5b6377e88bc960e11b86526001600160a01b0384166004526001600160401b038716602452604486fd5b5034610217576040366003190112610217576001600160a01b036040610afa612437565b9260043581525f516020615a4d5f395f51905f526020522091165f52602052602060ff60405f2054166040519015158152f35b503461021757604036600319011261021757610205610b4a612421565b610b52612aba565b602435906001600160a01b0333911661308b565b50346102175760203660031901126102175760a0610bc4610b85612421565b610b8d612a90565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b03600160405192610bdb846124ef565b63ffffffff8154818116865260ff8160201c1615156020870152818160281c16604087015260481c1660608501520154166080820152610c6360405180926001600160a01b036080809263ffffffff815116855260208101511515602086015263ffffffff604082015116604086015263ffffffff6060820151166060860152015116910152565bf35b5034610217576020366003190112610217576020610383610c84612421565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b50346102175780600319360112610217576020604051620f42408152f35b50346102175760a036600319011261021757610cf2612421565b610cfa612479565b90610d0361248c565b6064356001600160401b038116810361075057608435916001600160401b0383168303610a7d57610d32612b29565b610d3a612999565b508580604051602081019063ffffffff8916825260208152610d5d604082612541565b519061080c5afa610d6c612a61565b90156113fa578051810160208101916020818303126113d6576020810151906001600160401b0382116113f65701906101009082900312610aa95760405191610db483612525565b60208201516001600160401b0381116113f65760209083010181601f820112156113f65789815191610de583612a46565b92610df36040519485612541565b80845284602082840101116104da578060208093018386015e83010152835260408201516001600160401b0381116113f6576020908301019080601f830112156113f6578151916001600160401b0383116113c2578260051b9060405193610e5e6020840186612541565b84526020808501928201019283116113f257602001905b8282106113da57505050602083015261010090610e94606082016130cc565b6040840152610ea560808201614e68565b6060840152610eb660a08201614e68565b6080840152610ec760c08201614e7c565b60a0840152610ed860e08201614e7c565b60c0840152015180880b8082036113d657610ef89160e0840152846134b2565b5060405191610f068361250a565b825263ffffffff87166020830152821515604083015260608201526001600160401b03831660808201526001600160401b03841660a08201526001600160a01b03851687525f516020615a2d5f395f51905f5260205260408720815180518051906001600160401b0382116113c25781908b610f828654612a0e565b601f8111611387575b5050602090601f8311600114611325578c9261131a575b50508160011b915f199060031b1c19161782555b6001820160208201518051906001600160401b03821161130657680100000000000000008211611306576020908c84548486558085106112ba575b505001918b5260208b20908b5b8160021c811061127557506003198116810380611219575b505050506001600160a01b03947f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd8224435397946001600160401b039460a06006868896600260809d9901888060408401511616891982541617815560608201517fffffffff0000000000000000000000000000000000000000ffffffffffffffff7bffffffffffffffffffffffffffffffffffffffff000000000000000083549260401b16911617905560038201908c808f83015116166001600160a01b0319835416178255848101519082547fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff000000000000000000000000000000000000000000001694891b16911617171790556111a76004820188806020880151161689198254161781556040860151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b60608401516005820155019185808c8301511616861984541617835501516fffffffffffffffff000000000000000082549160401b16906fffffffffffffffff0000000000000000191617905563ffffffff6040519a168a52151560208a01521660408801521660608601521692a280f35b928c938d5b8181106112375750505060021c015584848460a0611016565b909194602061126b6001926001600160401b03895116908560031b6001600160401b03809160031b9316831b921b19161790565b960192910161121e565b8c8d5b6004811061128d575083820155600101610ffe565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501611278565b8382876112e6945220600380870160021c820192601888831b16806112ed575b500160021c0190614896565b8c5f610ff1565b5f198501908154905f19908a03851b1c1690555f6112da565b634e487b7160e01b8c52604160045260248cfd5b015190505f80610fa2565b858d52818d209250601f1984168d5b81811061136f5750908460019594939210611357575b505050811b018255610fb6565b01515f1960f88460031b161c191690555f808061134a565b92936020600181928786015181550195019301611334565b602082886113b1945220601f850160051c810191602086106113b8575b601f0160051c0190614896565b8b5f610f8b565b90915081906113a4565b634e487b7160e01b8b52604160045260248bfd5b8880fd5b602080916113e7846130cc565b815201910190610e75565b8b80fd5b8980fd5b639b0c335d60e01b8752600487fd5b503461021757604036600319011261021757611423612421565b60243561142e612aba565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156114c25760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156104de576114ad575b505061020591339061308b565b816114b791612541565b6104da57825f6114a0565b5080fd5b503461021757806003193601126102175760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b503461021757806003193601126102175760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b50346102175760c03660031901126102175761155736612562565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541561020857610205906142b4565b5034610217576020366003190112610217576115a1612aba565b80808080600435335af16115b3612a61565b50156102175780f35b5034610217576060366003190112610217576115d6612437565b604435906001600160a01b0382169182810361186e576115f4612b78565b6115fd81612d18565b61160683612fa0565b610750576040810151156107505760016001600160401b03608083015116016001600160401b0381116118a35790859161164760e083510151840b826134b2565b509361167160208401926001600160401b03845116906001600160401b0360a08701511691613553565b1561186e576116b0816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b6116bb868254612623565b90556001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803b156107505760405163f3fef3a360e01b81526001600160a01b0389166004820152602481018790529085908290604490829084905af1908115611898578591611883575b50506001600160401b039261174d60e08585511692510151860b876148ac565b509081611872575b5050505116926117bf60246040516001600160a01b036020820194169687855260408201526001606082015260608152611790608082612541565b6040519384916280000360e11b60208401525180918484015e810185838201520301601f198101835282612541565b7333333333333333333333333333333333333333333b156114c2578161180191604051809381926317938e1360e01b83526020600484015260248301906124cb565b0381837333333333333333333333333333333333333333335af180156104de57611859575b50506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b8161186391612541565b61186e57835f611826565b8380fd5b61187b92614cd4565b5f8080611755565b8161188d91612541565b61186e57835f61172d565b6040513d87823e3d90fd5b634e487b7160e01b86526011600452602486fd5b50346102175760e0366003190112610217576118d236612562565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1005415610208576001600160a01b036060820151166001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016145f1461194157610205906142b4565b6102059060c435906136c5565b503461021757602036600319011261021757602061038361196d612421565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5034610217576040366003190112610217576119bd612437565b336001600160a01b038216036119d9576103f890600435613628565b63334bd91960e11b8252600482fd5b5034610217576020366003190112610217576001600160a01b03611a0a612421565b611a126129d7565b501681525f516020615a2d5f395f51905f52602052604081209060405190611a398261250a565b604051611a4581612525565b60405184548184611a5583612a0e565b8083529260018116908115611d915750600114611d50575b611a7992500382612541565b815260018401604051808260208294549384815201908652602086209286905b806003830110611cfe57611acf945491818110611ce4575b818110611cc7575b818110611caa575b10611c9c575b500382612541565b60208201526001600160a01b0360028501546001600160401b038116604084015260401c16606082015260038401546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c820b60e0820152825260048301549260208301906001600160401b038516825260ff604085019560401c161515855260066005820154916060860192835201549460808501926001600160401b03871684526001600160401b0360a087019760401c1687526040519560208752519460c06020880152611bb6865161010060e08a01526101e08901906124cb565b60208088015189830360df19016101008b015280518084529282019892910190835b818110611c7d57505050926001600160401b03809693899a969360e0878c610120866040819c0151169101528c6101406001600160a01b036060840151169101528c6101606001600160a01b036080840151169101528c61018060ff60a0840151169101528c6101a060ff60c0840151169101520151900b6101c08b0152511660408901525115156060880152516080870152511660a0850152511660c08301520390f35b82516001600160401b03168a526020998a019990920191600101611bd8565b60c01c81526020015f611ac7565b9260206001916001600160401b038560801c168152019301611ac1565b9260206001916001600160401b038560401c168152019301611ab9565b9260206001916001600160401b0385168152019301611ab1565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c6060820152019401920184929391611a99565b5090868552602085209085915b818310611d75575050906020611a7992820101611a6d565b6020919350806001915483858801015201910190918392611d5d565b60209250611a7994915060ff191682840152151560051b820101611a6d565b5034610217576040366003190112610217576103f8600435611dd0612437565b90611df66103ee825f525f516020615a4d5f395f51905f52602052600160405f20015490565b613581565b503461021757608036600319011261021757611e15612421565b611e1d61244d565b611e25612463565b90606435926fffffffffffffffffffffffffffffffff841680940361075057611e4c612b78565b84611e5682613027565b926001600160a01b0360808501511693843b156104da57611ee06101048492836001600160401b03806040519788968795636f0d192560e11b875260048701906001600160a01b036080809263ffffffff815116855260208101511515602086015263ffffffff604082015116604086015263ffffffff6060820151166060860152015116910152565b169a8b60a485015216988960c48401528b60e48401525af180156104de57611f42575b50506001600160a01b036040917f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c93835195865260208601521692a380f35b81611f4c91612541565b61075057845f611f03565b5034610217576020366003190112610217576020611f8f6004355f525f516020615a4d5f395f51905f52602052600160405f20015490565b604051908152f35b503461021757806003193601126102175760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503461021757606036600319011261021757611fec612421565b6024356001600160401b0381116104da5761200b90369060040161249b565b9092604435906001600160401b038211610217576020611f8f868686612034366004890161249b565b93909261203f612b78565b612630565b50346120fa5760403660031901126120fa5761205e612421565b9060243561206a612aba565b6001600160a01b03608061207d85613027565b015116803b156120fa576040516390a0827b60e01b81526001600160a01b038516600482015260248101839052905f908290604490829084905af180156120ef576120d9575b5061020591926001600160a01b0333911661308b565b61020592505f6120e891612541565b5f916120c3565b6040513d5f823e3d90fd5b5f80fd5b346120fa575f3660031901126120fa5760206040516305f5e1008152f35b346120fa5760a03660031901126120fa57612135612421565b61213d612479565b61214561248c565b9160643563ffffffff81168091036120fa576084359163ffffffff83168093036120fa5761217281612d18565b5061217b612b29565b6001600160a01b0360016121bf836001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b015416908115612317575b63ffffffff604051956121dc876124ef565b16948581526001600160a01b036001816020840199151595868b52604085019a888c52606086018a8152836080880193169c8d845263ffffffff6122508a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b985116926cffffffff00000000000000000068ffffffff000000000064ff000000008b549351151560201b16935160281b16935160481b16936cffffffff000000000000000000199168ffffffffffffffffff1916171617171785555116920191166001600160a01b03198254161790556122ca86612fa0565b156120fa577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9936080936001600160a01b03936040519788526020880152604087015260608601521692a3005b905061232281612f58565b604051610b048082018281106001600160401b03821117612354578291614f29833903905ff580156120ef57906121ca565b634e487b7160e01b5f52604160045260245ffd5b346120fa5760203660031901126120fa5760043563ffffffff60e01b81168091036120fa57602090637965db0b60e01b81149081156123ad575b506040519015158152f35b6301ffc9a760e01b149050826123a2565b346120fa5760403660031901126120fa5761241f6123da612421565b6001600160a01b036123ea61244d565b916123f3612aba565b165f525f516020615a2d5f395f51905f526020526001600160401b03600460405f200154163390612c34565b005b600435906001600160a01b03821682036120fa57565b602435906001600160a01b03821682036120fa57565b602435906001600160401b03821682036120fa57565b604435906001600160401b03821682036120fa57565b6024359063ffffffff821682036120fa57565b6044359081151582036120fa57565b9181601f840112156120fa578235916001600160401b0383116120fa576020808501948460051b0101116120fa57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60a081019081106001600160401b0382111761235457604052565b60c081019081106001600160401b0382111761235457604052565b61010081019081106001600160401b0382111761235457604052565b90601f801991011681019081106001600160401b0382111761235457604052565b60c09060031901126120fa576040519061257b8261250a565b81600435815260243560208201526044356001600160a01b03811681036120fa5760408201526064356001600160a01b03811681036120fa576060820152608435608082015260a060a435910152565b91908110156125db5760051b0190565b634e487b7160e01b5f52603260045260245ffd5b906001600160401b03809116911601906001600160401b03821161260f57565b634e487b7160e01b5f52601160045260245ffd5b9190820180921161260f57565b9194935f935f968281036120fa57612678856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156120fa57929061268b85612d18565b91608061269787613027565b01916001600160a01b03835116946126be60208601966001600160401b03885116906130e0565b965f975b818d1061292e575b50508a1592506129239150505743612712866001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160401b0384168061272c575b50505050505050565b6127416127649560e0855101515f0b906134b2565b6001600160401b03869792975116906001600160401b0360a08701511691613553565b1561290457506127a4856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6127af858254612623565b90556001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166001600160a01b03861690803b156120fa5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101879052905f908290604490829084905af180156120ef576128d8575b506001600160a01b038261284c876001600160401b03948460e09751169061308b565b511693511691510151850b93823b15610a7d57604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156104de576128c3575b8080808080612723565b6128ce828092612541565b61021757806128b9565b6001600160401b0391975060e0926128f85f6001600160a01b0393612541565b5f989093509150612829565b6001600160a01b03866377e88bc960e11b5f521660045260245260445ffd5b505f96505050505050565b61293d8d83879f9b96976125cb565b359061294a8a85886125cb565b356001600160401b03811681036120fa5761297392898d926001600160a01b038b5116926131c2565b9490911561299157600191612987916125ef565b98019b93926126c2565b989c506126ca565b604051906129a682612525565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b604051906129e48261250a565b5f60a0836129f0612999565b81528260208201528260408201528260608201528260808201520152565b90600182811c92168015612a3c575b6020831014612a2857565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612a1d565b6001600160401b03811161235457601f01601f191660200190565b3d15612a8b573d90612a7282612a46565b91612a806040519384612541565b82523d5f602084013e565b606090565b60405190612a9d826124ef565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a602052604090205460ff1615612af257565b63e2517d3f60e01b5f52336004527f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f60245260445ffd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff1615612b6157565b63e2517d3f60e01b5f52336004525f60245260445ffd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd602052604090205460ff1615612bb057565b63e2517d3f60e01b5f52336004527f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef660245260445ffd5b805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0333165f5260205260ff60405f20541615612c1e5750565b63e2517d3f60e01b5f523360045260245260445ffd5b6024906001600160401b03612ca3939481604051936001600160a01b03602086019816885216604084015216606082015260608152612c74608082612541565b6040519384916280000360e11b60208401525180918484015e81015f838201520301601f198101835282612541565b7333333333333333333333333333333333333333333b156120fa575f612ce591604051809381926317938e1360e01b83526020600484015260248301906124cb565b0381837333333333333333333333333333333333333333335af180156120ef57612d0c5750565b5f612d1691612541565b565b6001600160a01b0390612d296129d7565b50165f525f516020615a2d5f395f51905f5260205260405f2060405191612d4f8361250a565b60405191612d5c83612525565b6040518154815f612d6c83612a0e565b8083529260018116908115612f395750600114612ef8575b612d9092500382612541565b8352600181019360405180602087549182815201965f5260205f20905f915b816003840110612eac5797612df09284926001600160a01b039798999a5491818110611ce457818110611cc757818110611caa5710611c9c57500382612541565b60208601528160028401546001600160401b038116604088015260401c1660608601526001600160401b0360066003850154946080880197858716895260ff8760a01c1660a082015260c081019660ff8160a81c16885260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015293511615159081612e9e575b50156120fa57565b60ff9150511615155f612e96565b97600160806004928b546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c6060820152019901920191612daf565b5090835f5260205f20905f915b818310612f1d575050906020612d9092820101612d84565b6020919350806001915483858801015201910190918392612f05565b60209250612d9094915060ff191682840152151560051b820101612d84565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152612f8d604882612541565b51902090565b519081151582036120fa57565b5f80916040516001600160a01b03602082019216825260208152612fc5604082612541565b51906108105afa612fd4612a61565b9015613018576020818051810103126120fa5760405190602082018281106001600160401b038211176123545760405261301090602001612f93565b809152151590565b6313dd7ccd60e31b5f5260045ffd5b61303390610b8d612a90565b906001600160a01b0360016040519361304b856124ef565b63ffffffff8154818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156120fa57565b60405163a9059cbb60e01b60208201526001600160a01b03929092166024830152604480830193909352918152612d16916130c7606483612541565b614b17565b51906001600160401b03821682036120fa57565b5f919082916001600160401b03604051916001600160a01b03602084019416845216604082015260408152613116606082612541565b51906108015afa613125612a61565b9015613193576060818051810103126120fa576040519060608201918083106001600160401b03841117612354576001600160401b0392604052604061318c6060613172602086016130cc565b948585526131818482016130cc565b6020860152016130cc565b9101521690565b639d2c8fcb60e01b5f5260045ffd5b906001600160401b03809116911603906001600160401b03821161260f57565b95929190939495805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20916001600160a01b0383541695861561343a576002840180549860ff8a60481c1661342b576001600160a01b03600187015491166001600160a01b03821603613418576001600160401b039060a01c166001600160401b038a1660ff8b60401c165f146133c1575080926001600160401b03811682115f146133b957613279916131a2565b985b6132858a8c6125ef565b9a6001600160401b038c166001600160401b038516116133a8575069010000000000000000009069ff00000000000000000019161790556132c88160019a6131a2565b966001600160401b0360206001600160a01b036132ec60e0875101515f0b8d6134b2565b50981694015116833b156120fa5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152915f908390606490829084905af19081156120ef577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4392604092613398575b506001600160a01b0360018187541696015416956001600160401b038351921682526020820152a4565b5f6133a291612541565b5f61336e565b5f9b508b9a50985050505050505050565b50505f613279565b9092809a93806001600160401b038316105f1461340d576133ec916133e5916131a2565b809b6125ef565b816001600160401b038216115f1461340657505b9161327b565b9050613400565b50506133ec5f6133e5565b8463358d72d160e01b5f5260045260245ffd5b63f7348a7960e01b5f5260045ffd5b631a40316d60e01b5f5260045ffd5b60ff16604d811161260f57600a0a90565b9190820391821161260f57565b8115613471570490565b634e487b7160e01b5f52601260045260245ffd5b90620f4240820291808304620f4240149015171561260f57565b8181029291811591840414171561260f57565b9190805f0b80155f146134cf5750506001600160401b0382169190565b5f8113156134fa57506134e76134f69160ff16613449565b6001600160401b03841661349f565b9190565b9050607f19811461260f576001600160401b039261351f613528925f0360ff16613449565b93849116612623565b5f19810190811161260f5761354f613548846001600160401b0393613467565b938461349f565b1690565b6135796001600160401b039293613573849361356e81614bb0565b6130e0565b946125ef565b169116101590565b805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260ff60405f205416155f1461362257805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260405f20600160ff198254161790556001600160a01b03339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260ff60405f2054165f1461362257805f525f516020615a4d5f395f51905f5260205260405f206001600160a01b0383165f5260205260405f2060ff1981541690556001600160a01b03339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b907f0000000000000000000000000000000000000000000000000000000000000000604083016136fe6001600160a01b03825116612fa0565b1561423f576001600160a01b038216805f525f516020615a2d5f395f51905f5260205260405f2090604051946137338661250a565b60405161373f81612525565b6040518454815f61374f83612a0e565b808352926001811690811561422057506001146141df575b61377392500382612541565b815260018401604051808260208294549384815201905f5260205f20925f905b80600383011061418d576137c5945491818110611ce457818110611cc757818110611caa5710611c9c57500382612541565b60208201526001600160a01b0360028501546001600160401b038116604084015260401c16606082015260038401546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528652600660048401549360ff60208901956001600160401b038116875260401c1615156040890152600581015460608901520154966001600160401b03881660808801526001600160401b0360a088019860401c168852606081016001600160a01b038151165f525f516020615a2d5f395f51905f5260205260405f2095604051966138b08861250a565b6040516138bc81612525565b6040518254815f6138cc83612a0e565b808352926001811690811561416e575060011461412d575b6138f092500382612541565b81526040516001830180548083525f9182526020808320849391840192905b8160038401106140db5754918181106140c1575b8181106140a4575b818110614087575b10614079575b50036139459082612541565b602082015260028201546001600160401b038116604083015260401c6001600160a01b0316606082015260038201546001600160a01b03811660808301528060a01c60ff1660a08301528060a81c60ff1660c083015260b01c5f0b60e0820152885260048101546001600160401b03811660208a015260401c60ff161515604089015260058101546060890152600601546001600160401b038116608089015260401c6001600160401b031660a088015281516001600160a01b0316613a0a90613027565b9383519060a08501918251996080870191825115159b613a2991612623565b8d5160e001515f0b613a3a916148ac565b90508d5160c0015160ff16825160c0015160ff1690613a5892614e9e565b9a1561404b578a925b5f8063ffffffff8b51166040516020810191825260208152613a84604082612541565b51906108085afa613a93612a61565b901561403c576020818051810103126120fa57898f9360ff60a0613ae19260206001600160401b03613ac982613ae799016130cc565b16950197885115155f14614031575051015116613449565b9061349f565b825115613fe75763ffffffff613b028160608d015116614bd6565b16620f4240019081620f42401161260f57613b38620f424091613ae16001600160401b039463ffffffff8f604001511690612623565b0416908351908115155f14613fdf5750915b5115613f9e576305f5e100811015613f5e57505f5b613b7f613b6c8651613485565b613b798a51885190612623565b90612623565b5f1981019190821161260f57613b79613ba592613b9f8b51895190612623565b90613467565b606480830283810482148415171561260f578211613ee45750505085518c5160e001515f0b613bd3916148ac565b9d8e8b9c929c516001600160401b031691516001600160401b031690613bf892613553565b15613e94579c8a9b9c9d85516001600160a01b03169387516001600160a01b0316935115159360405195613c2b8761250a565b86526020860190815260408601926001600160401b03169485845260608701926001600160401b0316968784526080810191825260a08101925f845260208d019586515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f2092516001600160a01b03166001600160a01b031683546001600160a01b0319161783556001830191516001600160a01b03166001600160a01b031682546001600160a01b0319161782555181549060a01b7bffffffffffffffff000000000000000000000000000000000000000016907fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1617905560020192516001600160401b03166001600160401b03166001600160401b0319845416178355511515613d8190839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151581549060481b69ff000000000000000000169069ff00000000000000000019161790555194516001600160a01b031695516001600160a01b03169651935160405194855260208501526001600160401b031660408401526060830152608082015260a07f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91891a4608001516001600160a01b03169283613e229261308b565b516001600160401b0316935160e001515f0b93813b156120fa57604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f92830b60648301529091908290608490829084905af180156120ef57612d0c5750565b5050505050945095505092505081612d16947f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c602080870151926001600160401b0360405191168152a35261492d565b999c5099509c50505050509450809650602091500151906001600160a01b038451169281810180911161260f575f19810190811161260f57811561347157612d16977fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e92604092049082519182526020820152a3526142b4565b6305f5e0ff19016001600160401b03811161260f57613f98613f8d6001600160401b036305f5e1009316613485565b6305f5e0ff90612623565b04613b5f565b6305f5e100811115613fb157505f613b5f565b6305f5e100036001600160401b03811161260f57613f98613f8d6001600160401b036305f5e1009316613485565b905091613b4a565b63ffffffff613ffb8160608d015116614bd6565b16620f42400390620f4240821161260f57613b38620f424091613ae16001600160401b039463ffffffff8f60400151169061345a565b905051015116613449565b635cffc5fb60e11b5f5260045ffd5b8261271003612710811161260f576127106140706001600160401b0392838f1661349f565b04169a92613a61565b60c01c81526020015f613939565b9260206001916001600160401b038560801c168152019301613933565b9260206001916001600160401b038560401c16815201930161392b565b9260206001916001600160401b0385168152019301613923565b935090916001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019084939261390f565b5090845f5260205f20905f915b8183106141525750509060206138f0928201016138e4565b602091935080600191548385880101520191019091839261413a565b602092506138f094915060ff191682840152151560051b8201016138e4565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c6060820152019401920184929391613793565b5090865f5260205f20905f915b81831061420457505090602061377392820101613767565b60209193508060019154838588010152019101909183926141ec565b6020925061377394915060ff191682840152151560051b820101613767565b6080840151909392501561426a576001600160a01b038351166320a2097d60e11b5f5260045260245ffd5b6001600160a01b0390612d16937f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60208481870151935116604051908152a216606082015261492d565b6060810151906001600160a01b035f921691825f525f516020615a2d5f395f51905f5260205260405f206040516142ea8161250a565b6040516142f681612525565b6040518354815f61430683612a0e565b80835292600181169081156148775750600114614836575b61432a92500382612541565b815260018301604051808260208294549384815201905f5260205f20925f905b8060038301106147e45761437c945491818110611ce457818110611cc757818110611caa5710611c9c57500382612541565b60208201526001600160a01b0360028401546001600160401b038116604084015260401c16606082015260038301546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528152600660048301549260ff60208401946001600160401b038116865260401c1615156040840152600581015460608401520154906001600160401b03821660808201526001600160401b0360a082019260401c168252604085019361444b6001600160a01b03865116612fa0565b156147745785519161271061447461446960a08a0195865190612623565b60808a01519061349f565b049383519480861161476c575b50846146ca575b6144c06144a7614499878b51612623565b60e0855101515f0b906148ac565b919092826001600160401b03808b511692511691613553565b1561467c5750846145c0575b61455d906001600160401b037fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c976145348c6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b61453f898254612623565b905551169360e06001600160a01b038a511694510151900b906148ac565b919092836145a4575b5050505061459f6001600160a01b0360208701519551169551915192604051938493846040919493926060820195825260208201520152565b0390a4565b6145b26145b794828c614cd4565b612c34565b5f808080614566565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001695863b156120fa5760405163f3fef3a360e01b81526001600160a01b038b16600482015260248101879052965f908890604490829084905af19687156120ef577fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c97614659575b5095506144cc565b61455d929194505f61466a91612541565b6001600160401b035f94919250614651565b9793505050507f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c9350602092508291506146b58161492d565b0151926001600160401b0360405191168152a3565b6146d9858a60208b0151614bf1565b614488576040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031660048201529094506020816024818c5afa9081156120ef575f9161473a575b5093614488565b90506020813d602011614764575b8161475560209383612541565b810103126120fa57515f614733565b3d9150614748565b94505f614481565b505050506080820151909250156147a2576001600160a01b038251166320a2097d60e11b5f5260045260245ffd5b612d16917f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b0381850151935116604051908152a261492d565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192018492939161434a565b5090855f5260205f20905f915b81831061485b57505090602061432a9282010161431e565b6020919350806001915483858801015201910190918392614843565b6020925061432a94915060ff191682840152151560051b82010161431e565b8181106148a1575050565b5f8155600101614896565b9190805f0b9081155f146148c95750506001600160401b03821690565b5f821315614904576148de915060ff16613449565b9182156134715761354f6148fd82856001600160401b0394069061345a565b9384613467565b505f0380805f0b0361260f5761354f6149276001600160401b039260ff16613449565b8461349f565b805161271061495061494560a0850193845190612623565b60808501519061349f565b0481518181115f14614b0f5750905b8190602084019161498283519460608701956001600160a01b0387511690614bf1565b15614b08575b80614a52575b7f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9161459f6001600160a01b03806149c7858a51612623565b966149df828a51169860408c0199848b51169061308b565b614a1c828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b614a27878254612623565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916001600160a01b0385511692803b156120fa5760405163f3fef3a360e01b81526001600160a01b03949094166004850152602484018390525f908490604490829084905af19283156120ef577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f93614af8575b50915061498e565b5f614b0291612541565b5f614af0565b505f614988565b90509061495f565b906001600160a01b03614b7892165f8060405193614b36604086612541565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602086015260208151910182855af1614b72612a61565b91614efd565b8051908115918215614b8e575b5050156120fa57565b81925090602091810103126120fa576020614ba99101612f93565b5f80614b85565b6001600160401b03166001609d1b01806001609d1b1161260f576001600160a01b031690565b63ffffffff60649116029063ffffffff821691820361260f57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa9182156120ef575f92614ca0575b5080821094851595614c61575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080614c5b565b9091506020813d602011614ccc575b81614cbc60209383612541565b810103126120fa5751905f614c4e565b3d9150614caf565b9091906001600160401b038316614e4d576001600160a01b03919250166040515f806020830163095ea7b360e01b8152736b9e773128f453f5c2c60935ee2de2cbc5390a24602485015285604485015260448452614d33606485612541565b83519082865af1614d42612a61565b81614e16575b5080614e0c575b15614db9575b5050736b9e773128f453f5c2c60935ee2de2cbc5390a243b156120fa5760405190630acb7f4b60e21b8252600482015263ffffffff60248201525f8160448183736b9e773128f453f5c2c60935ee2de2cbc5390a245af180156120ef57612d0c5750565b614e05916130c760405163095ea7b360e01b6020820152736b9e773128f453f5c2c60935ee2de2cbc5390a2460248201525f604482015260448152614dff606482612541565b82614b17565b5f80614d55565b50813b1515614d4f565b8051801592508215614e2b575b50505f614d48565b81925090602091810103126120fa576020614e469101612f93565b5f80614e23565b6001600160a01b03614e61612d1694614bb0565b911661308b565b51906001600160a01b03821682036120fa57565b519060ff821682036120fa57565b9060ff8091169116039060ff821161260f57565b9160ff811660ff83168181145f14614eb7575050505090565b6001600160401b039492911115614ee557614ede614ed961354f948693614e8a565b613449565b911661349f565b614ef6614ed9859261354f95614e8a565b9116613467565b91925015614f1957815115614f10575090565b3b156120fa5790565b5080519081156120fa57602001fdfe60a080604052346100455733608052610aba908161004a82396080518181816086015281816101b80152818161039a015281816104d50152818161054c01526106a40152f35b5f80fdfe60806040526004361015610011575f80fd5b5f5f3560e01c8063435354d31461050a57806390a0827b146104a55780639c45c34b146100ad578063a703334f1461033e578063de1a324a146100cb578063e94b77c1146100ad5763eba61c0e14610067575f80fd5b346100aa57806003193601126100aa5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b80fd5b50346100aa576100c86100bf366105ee565b92919091610699565b80f35b50346100aa57366003190161010081126103265760a0136100aa5760405160a0810181811067ffffffffffffffff82111761032a5760405261010b6105db565b91828252602435918215158303610326576020810192835260443563ffffffff8116810361032257604082015260643563ffffffff81168103610322576060820152608435906001600160a01b0382168203610322576080015260a43567ffffffffffffffff81168091036103265760c4359067ffffffffffffffff82168092036103225760e435946fffffffffffffffffffffffffffffffff861680960361031e576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361031e5763ffffffff6101ed911661066c565b9351151592811561030f57821561030057916102819391600493506040519263ffffffff60208501971687526040840152606083015260808201525f945f60a0830152600260c083015260e082015260e0815261024c61010082610636565b6020604051948592630100000160e01b83850152518091602485015e8201018281015f8152500301601f198101835282610636565b7333333333333333333333333333333333333333333b156102fc575f6102bb91604051809381926317938e1360e01b8352600483016108ff565b0381837333333333333333333333333333333333333333335af180156102f1576102e3575080f35b6102ef91505f90610636565b005b6040513d5f823e3d90fd5b5f80fd5b6313c0a8df60e01b8152600490fd5b63017461b760e71b8152600490fd5b8380fd5b8280fd5b5080fd5b634e487b7160e01b83526041600452602483fd5b50346100aa5760603660031901126100aa578060043567ffffffffffffffff811680910361049c57602435906001600160a01b0382168092036104a15760443567ffffffffffffffff811680910361049f576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361049f5761041f91600491604051916020830195865260408301526060820152606081526103eb608082610636565b60206040519485926280000360e11b83850152518091602485015e820101828101868152500301601f198101835282610636565b7333333333333333333333333333333333333333333b1561049c578161045991604051809381926317938e1360e01b8352600483016108ff565b0381837333333333333333333333333333333333333333335af18015610491576104805750f35b8161048a91610636565b6100aa5780f35b6040513d84823e3d90fd5b50fd5b505b5050fd5b50346100aa5760403660031901126100aa576004356001600160a01b038116809103610326576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163303610326576100c890602435903390610929565b50346102fc5760403660031901126102fc576105246105db565b602435906fffffffffffffffffffffffffffffffff82168092036102fc576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102fc5760046105806102819261066c565b926040519063ffffffff60208301951685526040820152604081526105a6606082610636565b6020604051948592630100000b60e01b83850152518091602485015e8201018281015f8152500301601f198101835282610636565b6004359063ffffffff821682036102fc57565b60809060031901126102fc576004356001600160a01b03811681036102fc579060243567ffffffffffffffff811681036102fc579060443590606435805f0b81036102fc5790565b90601f8019910116810190811067ffffffffffffffff82111761065857604052565b634e487b7160e01b5f52604160045260245ffd5b63ffffffff6127109116019063ffffffff821161068557565b634e487b7160e01b5f52601160045260245ffd5b926001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102fc57805f0b9081155f1461088f5750505b816106e357505050565b67ffffffffffffffff16806108685750906001600160a01b03166040515f806020830163095ea7b360e01b8152736b9e773128f453f5c2c60935ee2de2cbc5390a2460248501528560448501526044845261073f606485610636565b83519082865af161074e610a1a565b81610839575b508061082f575b156107d7575b5050736b9e773128f453f5c2c60935ee2de2cbc5390a243b156102fc5760405190630acb7f4b60e21b8252600482015263ffffffff60248201525f8160448183736b9e773128f453f5c2c60935ee2de2cbc5390a245af180156102f1576107c7575b505b565b5f6107d191610636565b5f6107c3565b6108289161082360405163095ea7b360e01b6020820152736b9e773128f453f5c2c60935ee2de2cbc5390a2460248201525f60448201526044815261081d606482610636565b82610978565b610978565b5f80610761565b50813b151561075b565b805180159250821561084e575b50505f610754565b6108619250602080918301019101610960565b5f80610846565b6001609d1b0191826001609d1b11610685576001600160a01b03806107c594169116610929565b5f8213156108d0576108a4915060ff16610a09565b80156108bc57808306830392831161068557506106d9565b634e487b7160e01b5f52601260045260245ffd5b505f0380805f0b03610685576108e89060ff16610a09565b8281810291818304149015171561068557506106d9565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b6107c5926001600160a01b036040519363a9059cbb60e01b6020860152166024840152604483015260448252610823606483610636565b908160209103126102fc575180151581036102fc5790565b906001600160a01b036109d992165f8060405193610997604086610636565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602086015260208151910182855af16109d3610a1a565b91610a59565b80519081159182156109ef575b5050156102fc57565b610a029250602080918301019101610960565b5f806109e6565b60ff16604d811161068557600a0a90565b3d15610a54573d9067ffffffffffffffff82116106585760405191610a49601f8201601f191660200184610636565b82523d5f602084013e565b606090565b91925015610a7557815115610a6c575090565b3b156102fc5790565b5080519081156102fc57602001fdfea26469706673582212208abf3b44431f4c61bdd7aa5253477149e50113939030a9816d0109335ec8e08364736f6c634300081e00336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0002dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800a26469706673582212203f720c6ea86c631282ab7132c9a7dc291402b5cc506847c33ef5f144f0f78ca764736f6c634300081e0033000000000000000000000000bc217096db9eb6d2782c1d9e725d462077a4d1f6000000000000000000000000b88339cb7199b77e23db6e890353e22632ba630f" + "address": "0xc5b7162e6a0fde52cc180dc8ab273feb8b32f57d", + "initCode": "0x60c0346200010457601f62005d3e38819003918201601f19168301916001600160401b038311848410176200010857808492604094855283398101031262000104576200005a602062000052836200011c565b92016200011c565b6001600160a01b0390911660805260a052604051615c0c90816200013282396080518181816108b7015281816109ff0152818161158b0152818161182f01528181612a1701528181614a0501528181614b7c01528181614f68015261504e015260a051818181610670015281816107de01528181611a7b01528181613a6e01528181614188015281816141e401528181614288015281816142b40152818161435301526146990152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001045756fe6080604052600436101562000012575f80fd5b5f803560e01c806246912e146200257057806301ffc9a71462002515578063037a06a414620022c957806304c73f6014620022a9578063057f037014620021f45780631f74a0b5146200217257806321081d3c1462002135578063248a9ca314620020e75780632e748b211462001f7f5780632f2ff15d1462001f1a578063319adf9f1462001b6657806336568abe1462001b1757806337710e201462001abd57806339fff0981462001a225780633b1c6a0114620017275780633cf3a02514620016eb5780634265fe861462001699578063490e662f146200165c5780634b3b029b146200161f578063502a82e21462001554578063521c98ba1462000d9157806369b97ac71462000d7157806379c7b60b1462000d1757806379c7f2891462000c1557806390a0827b1462000bd457806391d148541462000b6657806396cc2cfb14620008f9578063a217fddf14620008db578063a4b672b61462000895578063af5de6f91462000802578063c55dae6314620007bc578063ccbedaec1462000542578063d06e28ed1462000443578063d547741f14620003da578063e38b73a914620003bc578063ea0aaf241462000359578063eb84e7f2146200024b5763ff3eae0014620001e2575f80fd5b34620002485760c0366003190112620002485762000200366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d10054156200023657620002339062004e31565b80f35b60405163cd6d8f7d60e01b8152600490fd5b80fd5b503462000248576020366003190112620002485760408160c09260a0835162000274816200270b565b8281528260208201528285820152826060820152826080820152015260043581527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205220604051620002c8816200270b565b6001600160a01b039060028284541693848352600181015490602084019185811683526001600160401b0393849283604088019360a01c1683520154956060860193838816855260ff60a06080890198828b60401c1615158a52019860481c1615158852604051988952511660208801525116604086015251166060840152511515608083015251151560a0820152f35b50346200024857602036600319011262000248576020620003b36200037d620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b54604051908152f35b50346200024857806003193601126200024857602060405160068152f35b50346200024857604036600319011262000248576200043f600435620003ff620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262000439600160408620015462002e64565b620039b1565b5080f35b503462000248576040366003190112620002485762000461620025de565b602435906fffffffffffffffffffffffffffffffff82168092036200053e576200048a62002e08565b6200049581620032e8565b90836001600160a01b039263ffffffff84608083015116915116813b156200053a57829160448392604051948593849263435354d360e01b845260048401528a60248401525af180156200052f5762000513575b5050167f02366c0d102495be1ee805b749be7baebab4fc0710c6d3f38751f1a22bd711648380a380f35b6200051e90620026f7565b6200052b57835f620004e9565b8380fd5b6040513d84823e3d90fd5b8280fd5b5f80fd5b50346200024857606036600319011262000248578062000561620025de565b6200056b6200260c565b906200057662002623565b906200058162002d37565b620005bc816001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200071a574362000601826001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b556001600160a01b0380600162000648846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b01541693816001600160401b03938483166200071f575b5050508183166200066e578480f35b7f00000000000000000000000000000000000000000000000000000000000000001684525f8051602062005bb783398151915260205260046040852001541690823b156200071a5760405163a703334f60e01b81526001600160401b039283166004820152336024820152911660448201529082908290606490829084905af180156200052f5762000702575b8080808480f35b6200070d90620026f7565b6200024857805f620006fb565b505050fd5b1686525f8051602062005bb783398151915260205282600460408820015416853b15620007b85760405163a703334f60e01b81526001600160401b03918216600482015233602482015291166044820152858160648183895af1908115620007ad57869162000791575b82906200065f565b6200079c90620026f7565b620007a957845f62000789565b8480fd5b6040513d88823e3d90fd5b8680fd5b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503462000248576020366003190112620002485760206001600160a01b03620008346200082e620025de565b620031ff565b6109ab60405162000848858301826200277c565b818152848101916200520c8339519020604051908482019260ff60f81b84523060601b60218401526035830152605582015260558152620008898162002744565b51902016604051908152f35b5034620002485780600319360112620002485760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346200024857806003193601126200024857602090604051908152f35b503462000248576040366003190112620002485762000917620025de565b906200092262002e08565b6200092d8262002f58565b6200093883620032e8565b926200094e60e083510151840b60243562004da2565b9460208401906001600160401b036200097381845116898360a08a01511691620038d2565b1562000b3657869750620009b7856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b620009c48582546200287c565b90556001600160a01b03808616807f410b9a8c926b6c439cdceb39c0bb8f829838a25bc5a26af9d4c263d1313cc46b6020604051898152a2817f000000000000000000000000000000000000000000000000000000000000000016803b1562000b325760405163f3fef3a360e01b81526001600160a01b038316600482015260248101889052908a908290604490829084905af1801562000b275787918b9162000b08575b505062000a8290608060e0960192848451169062003353565b511692511694510151850b93813b1562000b0457604051639c45c34b60e01b81526001600160a01b039490941660048501526001600160401b0316602484015260448301919091525f9290920b60648201529082908290608490829084905af180156200052f5762000af15750f35b62000afc90620026f7565b620002485780f35b8580fd5b62000b15919250620026f7565b62000b235785895f62000a69565b8880fd5b6040513d8c823e3d90fd5b8980fd5b6040516377e88bc960e11b81526001600160a01b03861660048201526001600160401b0389166024820152604490fd5b50346200024857604036600319011262000248576001600160a01b03604062000b8e620025f5565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020522091165f52602052602060ff60405f2054166040519015158152f35b50346200024857604036600319011262000248576200023362000bf6620025de565b62000c0062002d37565b602435906001600160a01b0333911662003353565b503462000248576020366003190112620002485760a062000c7a62000c39620025de565b62000c4362002d0b565b506001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b6001600160a01b0360016040519262000c9384620026db565b805463ffffffff90818116865260ff8160201c1615156020870152818160281c16604087015260481c166060850152015416608082015262000d1560405180926001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565bf35b50346200024857602036600319011262000248576020620003b362000d3b620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b503462000248578060031936011262000248576020604051620f42408152f35b5034620002485760a0366003190112620002485762000daf620025de565b62000db96200263a565b9062000dc46200264e565b6001600160401b0360643516606435036200053e57608435906001600160401b03821682036200053e5762000df862002db1565b62000e0262002c05565b508480604051602081019063ffffffff881682526020815262000e258162002760565b519061080c5afa62000e3662002cd7565b901562001542578051810190602081830312620007b85760208101516001600160401b038111620015055761010081830184031262001505576040519262000e7e8462002727565b602082840101516001600160401b03811162000b325760208201603f828587010101121562000b32576020818486010101519062000ebc8262002cbb565b9162000ecc60405193846200277c565b8083526020840160408284888a01010101116200152a5762000efa9160406020850191878901010162002691565b8452604082840101516001600160401b03811162000b325760208201603f828587010101121562000b3257602081848601010151916001600160401b0383116200152e578260051b6040519362000f5560208301866200277c565b84526020808501920160408285888a01010101116200152a576040838688010101915b60408285888a01010101831062001509575050505060208401526101009062000fa683820160600162003428565b604085015262000fbb6080828501016200512c565b606085015262000fd060a0828501016200512c565b9283608086015262000fe760c08383010162005141565b60a086015262000ffc60e08383010162005141565b60c08601520101519081880b820362001505578160e08401526001600160a01b03808716911603620007b8576200103890870b60643562003829565b506040519162001048836200270b565b825263ffffffff86166020830152821515604083015260608201526001600160401b036064351660808201526001600160401b03831660a08201526001600160a01b03841686525f8051602062005bb783398151915260205260408620815180518051906001600160401b03821162001438578190620010c9855462002c80565b601f8111620014c5575b50602090601f831160011462001458578b926200144c575b50508160011b915f199060031b1c19161782555b60208101518051906001600160401b038211620014385768010000000000000000821162001438576020906001850154836001870155808410620013e5575b500190600184018a5260208a20908a5b8160021c81106200139d5750600319811680820362001334575b50505050936001600160a01b03937f12cf3d04179e82c834f3ee7169a5df80651aa65530127f9ddb04c8cd82244353969360068460809860026001600160401b039701876040830151168154907bffffffffffffffffffffffffffffffffffffffff0000000000000000606085015160401b169163ffffffff60e01b16171790556003820190898b820151169082549174ff000000000000000000000000000000000000000060a083015160a01b16907fffffffffffffffffff000000000000000000000000000000000000000000000060e075ff00000000000000000000000000000000000000000060c086015160a81b1694015160b01b76ff00000000000000000000000000000000000000000000169416171717179055620012c5600482018760208601511688198254161781556040850151151568ff0000000000000000825491151560401b169068ff00000000000000001916179055565b6060830151600582015501908488820151166fffffffffffffffff000000000000000060a0845493015160401b16916fffffffffffffffffffffffffffffffff19161717905563ffffffff6040519816885215156020880152816064351660408801521660608601521692a280f35b928b938c5b81840381106200135d5750505060021c0155838360066001600160401b0362001168565b9091946020620013926001926001600160401b03895116908560031b60031b916001600160401b03809116831b921b19161790565b960192910162001339565b8b8c5b60048110620013b75750838201556001016200114e565b85519095916001916020916001600160401b0360068a901b81811b199092169216901b1792019501620013a0565b6200141890600187018d52838d20600380870160021c820192601888831b16806200141f575b500160021c019062004d8a565b5f6200113e565b5f1990818601918254918a03851b1c1690555f6200140b565b634e487b7160e01b8a52604160045260248afd5b015190505f80620010eb565b9250848b5260208b20908b935b601f1984168510620014a9576001945083601f1981161062001490575b505050811b018255620010ff565b01515f1960f88460031b161c191690555f808062001482565b8181015183556020948501946001909301929091019062001465565b620014f390868d5260208d20601f850160051c81019160208610620014fa575b601f0160051c019062004d8a565b5f620010d3565b9091508190620014e5565b8780fd5b906020806040936200151b8662003428565b81520193019290915062000f78565b8b80fd5b634e487b7160e01b8b52604160045260248bfd5b604051639b0c335d60e01b8152600490fd5b503462000248576040366003190112620002485762001572620025de565b6024356200157f62002d37565b826001600160a01b03807f000000000000000000000000000000000000000000000000000000000000000016931692803b156200161b5760405163f3fef3a360e01b81526001600160a01b0385166004820152602481018490529082908290604490829084905af180156200052f5762001603575b50506200023391339062003353565b6200160e90620026f7565b6200053a57825f620015f4565b5080fd5b5034620002485780600319360112620002485760206040517f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef68152f35b5034620002485780600319360112620002485760206040517fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d1008152f35b5034620002485760c03660031901126200024857620016b8366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576200023390620046c4565b50346200024857602036600319011262000248576200170962002d37565b80808080600435335af16200171d62002cd7565b5015620002485780f35b503462000248576060366003190112620002485762001745620025f5565b6001600160a01b0360443581811692918382036200053e576200176762002e08565b620017728262002f58565b906200177e8462003257565b62000b045760408201511562000b04576001600160401b0391600183608083015116019083821162001a0e57620017bd60e082510151890b8362003829565b5094620017db602083019386855116908760a08601511691620038d2565b1562000b23576200181c906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0560205260405f2090565b620018298682546200287c565b905587837f000000000000000000000000000000000000000000000000000000000000000016803b156200161b5760405163f3fef3a360e01b81526001600160a01b038a166004820152602481018890529082908290604490829084905af180156200052f57620019f0575b50509183826200192494620018bc60e062001913986024975116925101518c0b8962004da2565b508481620019d0575b505050511695604051916020830191169687825260408301526001606083015260608252620018f48262002744565b6040519485926280000360e11b60208501525180928585019062002691565b81010360048101845201826200277c565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062001970906024830190620026b4565b03925af18015620019c557620019b3575b506040519081527f2b348084e891b20d449a69f90114c5ab7bf7c84d64c25445c8ab440d469a6b4d602060043592a480f35b620019be90620026f7565b5f62001981565b6040513d5f823e3d90fd5b620019df620019e79362002847565b168b62003353565b5f8084620018c5565b620019ff9094929394620026f7565b62001505579190875f62001895565b634e487b7160e01b5f52601160045260245ffd5b5034620002485760e0366003190112620002485762001a41366200279e565b7fc56a3250645180a53cd9e196b2ee0a634a4f54e2edf59ea457f2083917e4d100541562000236576001600160a01b0380606083015116907f000000000000000000000000000000000000000000000000000000000000000016145f1462001aae576200023390620046c4565b620002339060c4359062003a45565b50346200024857602036600319011262000248576020620003b362001ae1620025de565b6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b503462000248576040366003190112620002485762001b35620025f5565b336001600160a01b0382160362001b54576200043f90600435620039b1565b60405163334bd91960e11b8152600490fd5b50346200024857602036600319011262000248576001600160a01b0362001b8c620025de565b62001b9662002c45565b501681525f8051602062005bb783398151915260205260408120906040519162001bc0836200270b565b60405162001bce8162002727565b6040518254818562001be08362002c80565b808352926001811690811562001ef9575060011462001eb4575b62001c08925003826200277c565b815260405160018301805480835290855260208086209083019186915b81600384011062001e72579360069593819362001c73936001600160401b039997549181811062001e5e575b81811062001e47575b81811062001e30575b1062001e21575b5003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c850b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a0830152604051906020825282519060c0602084015262001d31825161010060e08601526101e0850190620026b4565b60208084015185830360df190161010087015280518084529282019492910190835b81811062001e015750505083946001600160401b039260e08385604060a0960151166101208901526001600160a01b036060820151166101408901526001600160a01b0360808201511661016089015260ff858201511661018089015260ff60c0820151166101a08901520151900b6101c086015282602082015116604086015260408101511515606086015260608101516080860152826080820151168286015201511660c08301520390f35b82516001600160401b031686526020958601959092019160010162001d53565b60c01c81526020015f62001c6a565b9260206001918c8560801c16815201930162001c63565b9260206001918c8560401c16815201930162001c5a565b9260206001918c8516815201930162001c51565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162001c25565b5090848652602086209086915b81831062001edc57505090602062001c089282010162001bfa565b602091935080600191548385880101520191019091839262001ec1565b6020925062001c0894915060ff191682840152151560051b82010162001bfa565b50346200024857604036600319011262000248576200043f60043562001f3f620025f5565b908084527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205262001f79600160408620015462002e64565b62003912565b503462000248576080366003190112620002485762001f9d620025de565b62001fa76200260c565b9062001fb262002623565b90606435926fffffffffffffffffffffffffffffffff84168094036200053e5762001fdc62002e08565b62001fe782620032e8565b856001600160a01b03928360808401511692833b156200053a57620020646101048492836040519586948593636f0d192560e11b855260048501906001600160a01b036080809263ffffffff8082511686526020820151151560208701528060408301511660408701526060820151166060860152015116910152565b6001600160401b038091169b8c60a485015216978860c48401528c60e48401525af180156200052f57620020cb575b5050907f500d805a349357fe5d4759fe052d79bd744b82c8452837f52a7456ec7d3d751c92604092835195865260208601521692a380f35b620020d990939293620026f7565b62000b045790855f62002093565b50346200024857602036600319011262000248576001604060209260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268008452200154604051908152f35b5034620002485780600319360112620002485760206040517f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f8152f35b503462000248576060366003190112620002485762002190620025de565b6001600160401b036024358181116200052b57620021b39036906004016200265e565b91909360443591821162000248576020620021ec868686620021d936600489016200265e565b939092620021e662002e08565b6200288a565b604051908152f35b50346200053e5760403660031901126200053e5762002212620025de565b906024356200222062002d37565b6001600160a01b03928360806200223783620032e8565b015116803b156200053e576040516390a0827b60e01b81526001600160a01b038316600482015260248101849052905f908290604490829084905af18015620019c55762002290575b5062000233929333911662003353565b620002339350620022a190620026f7565b5f9262002280565b346200053e575f3660031901126200053e5760206040516305f5e1008152f35b346200053e5760a03660031901126200053e57620022e6620025de565b620022f06200263a565b620022fa6200264e565b6064359063ffffffff938483168093036200053e57608435908582168092036200053e57620023298162002f58565b506200233462002db1565b6001600160a01b03918260016200237b846001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b0154168015620024bc575b8360018960405199620023998b620026db565b16988981526020810197151597888152604082018a81526060830190878252856080850197169d8e8852620023fe8a6001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0160205260405f2090565b9451166cffffffff00000000000000000068ffffffff000000000064ff0000000087549651151560201b16935160281b16935160481b16936cffffffffffffffffffffffffff19161717171781550191511673ffffffffffffffffffffffffffffffffffffffff19825416179055620024778762003257565b156200053e577ff0dcc8957a27613dd82c92382ad37254b9744169d0caa5f3873cfec7ba794eb9946080946040519788526020880152604087015260608601521692a3005b50620024c882620031ff565b6040516109ab8082018281106001600160401b03821117620025015782916200520c833903905ff58062002386576040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b346200053e5760203660031901126200053e5760043563ffffffff60e01b81168091036200053e57602090637965db0b60e01b81149081156200255e575b506040519015158152f35b6301ffc9a760e01b1490508262002553565b346200053e5760403660031901126200053e57620025dc62002591620025de565b6001600160a01b03620025a36200260c565b91620025ae62002d37565b165f525f8051602062005bb78339815191526020526001600160401b03600460405f20015416339062002ea6565b005b600435906001600160a01b03821682036200053e57565b602435906001600160a01b03821682036200053e57565b602435906001600160401b03821682036200053e57565b604435906001600160401b03821682036200053e57565b6024359063ffffffff821682036200053e57565b6044359081151582036200053e57565b9181601f840112156200053e578235916001600160401b0383116200053e576020808501948460051b0101116200053e57565b5f5b838110620026a35750505f910152565b818101518382015260200162002693565b90602091620026cf8151809281855285808601910162002691565b601f01601f1916010190565b60a081019081106001600160401b038211176200250157604052565b6001600160401b0381116200250157604052565b60c081019081106001600160401b038211176200250157604052565b61010081019081106001600160401b038211176200250157604052565b608081019081106001600160401b038211176200250157604052565b604081019081106001600160401b038211176200250157604052565b90601f801991011681019081106001600160401b038211176200250157604052565b60c09060031901126200053e5760405190620027ba826200270b565b60043582526024356020830152816001600160a01b0360443581811681036200053e57604083015260643590811681036200053e576060820152608435608082015260a060a435910152565b9190811015620028175760051b0190565b634e487b7160e01b5f52603260045260245ffd5b9190916001600160401b038080941691160191821162001a0e57565b9073200000000000000000000000000000000000000091820180921162001a0e57565b906305f5e0ff820180921162001a0e57565b9190820180921162001a0e57565b919392935f945f948083036200053e57620028d5856001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b544311156200053e57869796939291620028ef8662002f58565b926080620028fd88620032e8565b01906001600160a01b0392838351169460208701976200292a6001600160401b0397888b5116906200343d565b845f9b5b1062002b94575b50508c15925062002b8791505057436200297f886001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0260205260405f2090565b5582861662002993575b5050505050505050565b620029c195620029ab60e0865101515f0b8262003829565b85889992995116908660a08901511691620038d2565b1562002b4d575062002a03876001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62002a108782546200287c565b90558682167f00000000000000000000000000000000000000000000000000000000000000008316803b156200053e5760405163f3fef3a360e01b81526001600160a01b038316600482015260248101899052905f908290604490829084905af18015620019c55762002b29575b509062002a9587849360e096958451169062003353565b511693511691510151850b93823b1562000b0457604051639c45c34b60e01b81526001600160a01b0390911660048201526001600160401b03909116602482015260448101929092525f9290920b606482015290829082908183816084810103925af180156200052f5762002b11575b80808080808062002989565b62002b1d8291620026f7565b62000248578062002b05565b60e094939291995062002b3c90620026f7565b5f98929350909162002a9562002a7e565b8451608001516040516377e88bc960e11b81529084166001600160a01b031660048201526001600160401b03919091166024820152604490fd5b505f985050505050505050565b62002ba6859e9f809c95968462002806565b359062002bb58c858862002806565b359189831683036200053e578a62002bd2938a8a51169262003520565b9490911562002bfb5762002beb8f93926001926200282b565b9b019e9d8f95949192956200292e565b9a9e9d5062002935565b6040519062002c148262002727565b5f60e08360608152606060208201528260408201528260608201528260808201528260a08201528260c08201520152565b6040519062002c54826200270b565b5f60a08362002c6262002c05565b81528260208201528260408201528260608201528260808201520152565b90600182811c9216801562002cb0575b602083101462002c9c57565b634e487b7160e01b5f52602260045260245ffd5b91607f169162002c90565b6001600160401b0381116200250157601f01601f191660200190565b3d1562002d06573d9062002ceb8262002cbb565b9162002cfb60405193846200277c565b82523d5f602084013e565b606090565b6040519062002d1a82620026db565b5f6080838281528260208201528260408201528260608201520152565b335f9081527f176c3a4e1b4d1d61213187c0dde7f83e2e2c933a339cb5403b4090bc2f0aa49a60205260409020547f880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f9060ff161562002d935750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b335f9081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff161562002dea57565b60405163e2517d3f60e01b81523360048201525f6024820152604490fd5b335f9081527fcf7720576cdb70176b3017d893bcb89ba0f36ed0261d4350fd0180e938b3bacd60205260409020547f5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef69060ff161562002d935750565b805f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260405f20335f5260205260ff60405f2054161562002d935750565b620019139262002eec92602492604051926001600160a01b0360208501931683526001600160401b03809216604085015216606083015260608252620018f48262002744565b733333333333333333333333333333333333333333803b156200053e576040516317938e1360e01b815260206004820152915f91839182908490829062002f38906024830190620026b4565b03925af18015620019c55762002f4b5750565b62002f5690620026f7565b565b9062002f6362002c45565b506001600160a01b038092165f5260205f8051602062005bb78339815191528152604092835f2084519062002f98826200270b565b85519562002fa68762002727565b80518254905f8162002fb88462002c80565b918282526001948a86821691825f14620031e05750506001146200319f575b62002fe5925003826200277c565b88528083019082519087829384928282549586815201915f52825f20945f5b8160038201106200315a57620030469654928583831062003140575b83831062003124575b83831062003107575b505010620030f8575b50905003826200277c565b858801526006600283015492856001600160401b0394858116858c0152841c1660608a015260ff60038201549760808b019a888a168c52828a60a01c1660a082015260c0810199838160a81c168b5260b01c5f0b60e0820152875260048301549086821690880152841c161515838601526005810154606086015201549082821660808501521c1660a082015293511615159081620030e9575b50156200053e57565b60ff9150511615155f620030e0565b60c01c81520188905f6200303b565b9091946001600160401b038560801c1681520193015f8562003032565b8192956001600160401b03868d1c168152019401908562003029565b8192956001600160401b0386168152019401908562003020565b86546001600160401b038082168652818b1c811696860196909652608081811c9096168a86015260c01c6060850152958201958795508c949093019260040162003004565b5050845f528188805f20855f915b858310620031c657505062002fe5935082010162002fd7565b8091929450548385880101520191018990858593620031ad565b60ff19168482015262002fe594151560051b840101915062002fd79050565b60405160208101913060601b83526bffffffffffffffffffffffff199060601b16603482015260288152606081018181106001600160401b03821117620025015760405251902090565b519081151582036200053e57565b5f80916040516001600160a01b036020820192168252602081526200327c8162002760565b51906108105afa6200328d62002cd7565b9015620032d6576020818051810103126200053e5760405190602082018281106001600160401b038211176200250157604052620032ce9060200162003249565b809152151590565b6040516313dd7ccd60e31b8152600490fd5b620032f79062000c4362002d0b565b906001600160a01b036001604051936200331185620026db565b805463ffffffff90818116875260ff8160201c1615156020880152818160281c16604088015260481c1660608601520154169182608082015291156200053e57565b60405163a9059cbb60e01b602082019081526001600160a01b039384166024830152604480830195909552938152620033ea9390925f92839291906200339b6064876200277c565b16937f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020604051620033ce8162002760565b8181520152519082855af1620033e362002cd7565b91620051de565b805190811591821562003402575b5050156200053e57565b81925090602091810103126200053e57602062003420910162003249565b5f80620033f8565b51906001600160401b03821682036200053e57565b604080516001600160a01b0392909216602083019081526001600160401b039390931682820152808252915f918291906200347a6060826200277c565b51906108015afa6200348b62002cd7565b9015620034f5576060818051810103126200053e5781516001600160401b0392606082018481118382101762002501578152620034ee6060620034d16020860162003428565b94858552620034e284820162003428565b60208601520162003428565b9101521690565b8151639d2c8fcb60e01b8152600490fd5b6001600160401b03918216908216039190821162001a0e57565b949192909394805f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0360205260405f20906001600160a01b039485835416958615620037ab57600284019788549860ff8a60481c166200379957600186015483608086510151168482160362003780576001600160401b039060a01c166001600160401b038b1660ff8c60401c165f146200371f575080926001600160401b03811682115f146200371657620035d79162003506565b995b620035e58b8d6200282b565b9b6001600160401b038d166001600160401b0385161162003704575069010000000000000000009069ff00000000000000000019161790556200362b8160019b62003506565b976001600160401b036020846200364b8d60e0895101515f0b9062003829565b50991695015116843b156200053e5760405163a703334f60e01b81526001600160401b0391821660048201526001600160a01b0392909216602483015282166044820152925f908490606490829084905af1918215620019c5577f53b9d5645f8b7ccd861ebd6036860fd21716451d1f238cb3720f12f3c49b0c4393604093620036f2575b5060018187541696015416956001600160401b038351921682526020820152a4565b620036fd90620026f7565b5f620036d0565b5f9c508c9b5099505050505050505050565b50505f620035d7565b9092809b93806001600160401b038316105f1462003773576200374f91620037479162003506565b809c6200282b565b816001600160401b038216115f146200376b57505b91620035d9565b905062003764565b50506200374f5f62003747565b60405163358d72d160e01b815260048101879052602490fd5b60405163f7348a7960e01b8152600490fd5b604051631a40316d60e01b8152600490fd5b60ff16604d811162001a0e57600a0a90565b9190820391821162001a0e57565b8115620037e8570490565b634e487b7160e01b5f52601260045260245ffd5b90620f42409182810292818404149015171562001a0e57565b8181029291811591840414171562001a0e57565b9190805f0b80155f14620038475750506001600160401b0382169190565b5f81131562003877575062003863620038739160ff16620037bd565b6001600160401b03841662003815565b9190565b9050607f19811462001a0e5762003893905f0360ff16620037bd565b91620038aa836001600160401b038093166200287c565b5f19810190811162001a0e57620038c684620038ce92620037dd565b938462003815565b1690565b6200390a6200390392936001600160401b0393836001600160a01b03620038fc8780971662002847565b166200343d565b946200282b565b169116101590565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f205416155f14620039aa57825f5260205260405f20815f5260205260405f20600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b5050505f90565b90815f527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800806020526001600160a01b0360405f20921691825f5260205260ff60405f2054165f14620039aa57825f5260205260405f20815f5260205260405f2060ff19815416905533917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b9062003a5e6001600160a01b0360408401511662003257565b1562004618576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165f525f8051602062005bb783398151915260205260405f20906040519162003ab6836200270b565b60405162003ac48162002727565b6040518254815f62003ad68362002c80565b8083529260018116908115620045f75750600114620045b2575b62003afe925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b81600384011062004570579360069593819362003b67936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152855260ff6004820154848116602088015260401c1615156040860152600581015460608601520154818116608085015260401c1660a08301526001600160a01b036060840151165f525f8051602062005bb783398151915260205260405f206040519062003c2e826200270b565b60405162003c3c8162002727565b6040518254815f62003c4e8362002c80565b80835292600181169081156200454f57506001146200450a575b62003c76925003826200277c565b8152600182016040519081602082549182815201915f5260205f20905f915b816003840110620044bd579360069593819362003cdf936001600160401b039997549181811062001e5e5781811062001e475781811062001e30571062001e21575003826200277c565b60208201526001600160a01b036002830154858116604084015260401c16606082015260038201546001600160a01b038116608083015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e0820152845260ff6004820154848116602087015260401c1615156040850152600581015460608501520154818116608084015260401c1660a082015262003d896001600160a01b03606086015116620032e8565b9184519062003dd662003dbb62003dac60a089015160808a01511515956200287c565b60e0885101515f0b9062004da2565b905060ff60c0885101511660ff60c087510151169162005165565b91156200448c5781925b5f8063ffffffff875116604051602081019182526020815262003e038162002760565b51906108085afa9062003e1562002cd7565b91156200447a576020828051810103126200053e576001600160401b0362003e44602062003e6d940162003428565b60208801519116919015620044665760ff60a062003e669251015116620037bd565b9062003815565b602085015115620044175763ffffffff62003e8e8160608801511662005026565b16620f4240818101811162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c01511691016200287c565b0416906080870151908115155f146200440e5750905b602085015115620043c5576305f5e1008110156200437e57505f5b62003f2462003f0b60a0890151620037fc565b62003f1d895160a08b0151906200287c565b906200287c565b5f1981019190821162001a0e5762003f1d62003f539262003f4c8a5160a08c0151906200287c565b90620037dd565b606482028281046064148315171562001a0e578111620042df57505062003f84855160e0865101515f0b9062004da2565b92909562003faf6001600160401b03602088015116856001600160401b0360a08a01511691620038d2565b156200423b5791608093917f550576b2f9e0ac12dfd5dd2d5743b5b7f11f34302b5f6bec6ad60db81bd6a91860a0856001600160401b036001600160a01b036040819b990151168a606084015116908a8401511515916040519162004014836200270b565b82526020820152828816604082015282871660608201528a81019182528481015f815262004118602086019384515f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b03602052856060600260405f206001600160a01b0388511673ffffffffffffffffffffffffffffffffffffffff19825416178155600181016001600160a01b0360208a0151168154907bffffffffffffffff000000000000000000000000000000000000000060408c015160a01b169163ffffffff60e01b161717905501950151168619855416178455511515839068ff0000000000000000825491151560401b169068ff00000000000000001916179055565b51151569ff00000000000000000082549160481b169069ff00000000000000000019161790555194818b60408501511697818d6060870151169a87875197015160405197885260208801521660408601521660608401521688820152a4015116620041ae83826001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001662003353565b60e06001600160401b03602084015116925101515f0b90803b156200053e57604051639c45c34b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526001600160401b03909316602484015260448301939093525f90810b6064830152909182908183816084810162002f38565b94505050905062002f56925060208201516001600160401b03604051921682527f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c60206001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693a36001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b9350949350505060208201516001600160a01b0360608401511691606481019081811162001a0e5760630190811162001a0e5760407fc8f90125c6a36c77a571201afc10310420481ab4895fadabb596d0ba71c22e3e9162002f569660648351920482526020820152a36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166060820152620046c4565b6001600160401b036305f5e0ff1982011162001a0e57620043be620043b86001600160401b036305f5e100936305f5e0ff190116620037fc565b6200286a565b0462003ef8565b6305f5e100811115620043da57505f62003ef8565b6001600160401b03816305f5e100031162001a0e57620043be620043b86001600160401b036305f5e10093840316620037fc565b90509062003edd565b63ffffffff6200442d8160608801511662005026565b16620f4240808281031162001a0e576001600160401b039262003e6662003ec792620f42409463ffffffff60408c0151169103620037cf565b5062003e6660ff60a08951015116620037bd565b604051635cffc5fb60e11b8152600490fd5b6127108181031162001a0e576001600160401b03612710620044b383820383861662003815565b0416919262003de0565b926001608060049286546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192019162003c95565b5090845f5260205f20905f915b8183106200453257505090602062003c769282010162003c68565b602091935080600191548385880101520191019091839262004517565b6020925062003c7694915060ff191682840152151560051b82010162003c68565b9260016004918554906001600160401b03918281168252828160401c16602083015260809281841c16604083015260c01c606082015201940192019162003b1d565b5090845f5260205f20905f915b818310620045da57505090602062003afe9282010162003af0565b6020919350806001915483858801015201910190918392620045bf565b6020925062003afe94915060ff191682840152151560051b82010162003af0565b506080810151909190156200464c5760246001600160a01b03604084015116604051906320a2097d60e11b82526004820152fd5b9080602062002f569201517f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d60206001600160a01b03604085015116604051908152a26001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016606082015262004e31565b6060906060810151905f6001600160a01b0380931692835f526020945f8051602062005bb78339815191528652604091825f2096835162004705816200270b565b845193620047138562002727565b85518a5491905f81620047268562002c80565b918282526001958887821691825f1462004d6b57505060011462004d2a575b62004756925094939403826200277c565b8652808b019184885193898586948484549283815201935f52845f20965f915b83600384011062004cde575085620047b998549484841062004cc4575b84841062004ca7575b5083831062004c8a575b50501062004c7b5750905003826200277c565b82850152600289015495836001600160401b039788811688880152871c16606086015260038a0154946080958581168783015260ff8160a01c1660a083015260ff8160a81c1660c083015260b01c5f0b60e08201528252600660048b01549a60ff8585019c8d8b82169052891c1615158885015260058101546060850152015490878216868401528760a0840192881c168252868901976200485e868a511662003257565b1562004c0d57899a9b5f9a98999a508c612710620048958b519b8b6200488b60a083019e8f51906200287c565b9101519062003815565b04988a5199808b1162004c04575b508d8a8062004b2e575b620048c2620048d191620048e493516200287c565b60e08a5101515f0b9062004da2565b91909782878088511692511691620038d2565b1562004ae65750908c9695949392918a620049f9575b509260e0896200498f94620049c59b9997947fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b99976200496e906001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b6200497b8d82546200287c565b9055511696511694510151900b9062004da2565b9190928d84620049ca575b5050505050880151965116965192519351938493846040919493926060820195825260208201520152565b0390a4565b620049ee94620049e89188620049e08562002847565b169062003353565b62002ea6565b5f8080808d6200499a565b919293949596505087807f00000000000000000000000000000000000000000000000000000000000000001691875101511690803b156200053e578b5163f3fef3a360e01b81526001600160a01b03929092166004830152602482018a90525f908290604490829084905af1801562004adc578c948f91998098956200498f957fb021c853215aadb12b6fa8afa7b3158201517d9abf7f756cdbb67bd66abc5a1c9f9e9d9b9995620049c59d62004ac0575b509497995094509950939597999a9b620048fa565b60e0945062004acf90620026f7565b6200496e5f945062004aab565b8b513d5f823e3d90fd5b9a50505050955050505093957f8d61555dcf69ba745e97fdea78e2652d9cdd464ca521faad482fe796f6d5841c955084915062004b238162004e31565b0151945191168152a3565b62004b45908a8301518c858c51015116906200503f565b1562004b53575b8a620048ad565b509192939495968881999a50602492508389510151168c51928380926370a0823160e01b82528d7f00000000000000000000000000000000000000000000000000000000000000001660048301525afa90811562004adc57918e918e9a999897969594935f9162004bc7575b509962004b4c565b92505098508681813d831162004bfc575b62004be481836200277c565b810103126200053e57518c988e91620048d162004bbf565b503d62004bd8565b99505f620048a3565b505050509296509450839293015115155f1462004c405750505191516320a2097d60e11b81529116600482015260249150fd5b62002f56948294827f31604f5790fbed7a37471cea010a8f08483441a6e1235ac48bb59337439a630d9401519451169051908152a262004e31565b60c01c81520185905f6200303b565b9091946001600160401b038560801c1681520193015f85620047a6565b956001600160401b03868495981c16815201940190858e6200479c565b6001600160401b0386168752958101959282019262004793565b95509360049296509284919388546001600160401b039283821683528d8483831c169084015260809382851c169083015260c01c8782015201960191019386948c9296938a9562004776565b50508c5f528186805f20865f915b85831062004d5157505062004756935082010162004745565b809192945054838588010152019101879086859362004d38565b60ff1916848201526200475694151560051b8401019150620047459050565b81811062004d96575050565b5f815560010162004d8a565b9190805f0b9081155f1462004dc05750506001600160401b03821690565b5f82131562004e035762004dd8915060ff16620037bd565b918215620037e857620038ce62004dfb82856001600160401b03940690620037cf565b9384620037dd565b505f0380805f0b0362001a0e57620038ce62004e2a6001600160401b039260ff16620037bd565b8462003815565b805161271062004e5862004e4c60a08501938451906200287c565b60808501519062003815565b0481518181115f146200501d5750905b8190602084019182519162004e8f60608701956001600160a01b039485885116906200503f565b1562005014575b8162004f61575b620049c5837f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f9462004ed1858a516200287c565b9662004eeb828a51169860408c0199848b51169062003353565b62004f29828a51166001600160a01b03165f527f6c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b0460205260405f2090565b62004f368782546200287c565b9055519651169651169651915192604051938493846040919493926060820195825260208201520152565b84518316927f00000000000000000000000000000000000000000000000000000000000000008116803b156200053e5760405163f3fef3a360e01b81526001600160a01b03959095166004860152602485018490525f908590604490829084905af1908115620019c5577f4755f239bb1b047245415cb917deced72a3ca8baebcef109c396ff332ea6f50f94620049c59262005002575b5093505062004e9d565b6200500d90620026f7565b5f62004ff8565b5f915062004e96565b90509062004e68565b90606463ffffffff8093160291821691820362001a0e57565b6040516370a0823160e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811660048301529092169392909190602082602481885afa918215620019c5575f92620050f2575b5080821094851595620050b2575b50505050565b7f5180f0ad9e9bd2296de2ee38c85d11c56613fa73f8ee66792f26ac318f1274749260609260405192835260208301526040820152a25f808080620050ac565b9091506020813d60201162005123575b8162005111602093836200277c565b810103126200053e5751905f6200509e565b3d915062005102565b51906001600160a01b03821682036200053e57565b519060ff821682036200053e57565b9060ff8091169116039060ff821162001a0e57565b919060ff821660ff82168181145f1462005180575050505090565b929391921015620051b857620038ce9192620051b0620051aa6001600160401b0395869362005150565b620037bd565b911662003815565b620038ce91620051d6620051aa6001600160401b0395869362005150565b9116620037dd565b9015620051fb57815115620051f1575090565b3b156200053e5790565b5080519081156200053e57602001fdfe60a080604052346100455733608052610961908161004a8239608051818181608b015281816101ac0152818161038e015281816104a20152818161051b015261069c0152f35b5f80fdfe60406080815260049081361015610014575f80fd5b5f915f3560e01c908163435354d3146104d757816390a0827b1461047b5781639c45c34b14610469578163a703334f1461033c578163de1a324a146100d457508063e94b77c1146100b35763eba61c0e1461006d575f80fd5b346100af57816003193601126100af57602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b82346100d1576100ce6100c5366105b5565b9291909161068d565b80f35b80fd5b91905034610338576101009136600319018381126103345760a01361033057815167ffffffffffffffff939060a081018581118282101761031d5784526101196105a2565b91828252602435918215158303610319576020810192835263ffffffff9360443585811681036102ce578783015260643585811681036102ce5760608301526084356001600160a01b03928382168203610315576080015260a4358881168091036102ce5760c435918983168093036102ce5760e435936fffffffffffffffffffffffffffffffff85168095036102ce577f000000000000000000000000000000000000000000000000000000000000000016330361031557866101dd9116610663565b9451151581156103055782156102f5578a6102e557885196602088019616865288870152606086015260808501525f60a0850152600260c085015260e084015260e08352820194828610908611176102d25761026891602491868652630100000160e01b6101208301526102598251809261012485019061079c565b81010383810186520184610641565b73333333333333333333333333333333333333333392833b156102ce576102a5935f928385518097819582946317938e1360e01b845283016107bd565b03925af19081156102c557506102b9575080f35b6102c391506105fd565b005b513d5f823e3d90fd5b5f80fd5b604183634e487b7160e01b5f525260245ffd5b88516376d4929560e11b81528890fd5b88516313c0a8df60e01b81528890fd5b885163017461b760e71b81528890fd5b8a80fd5b8780fd5b604184634e487b7160e01b5f525260245ffd5b8380fd5b8480fd5b8280fd5b919050346103385760603660031901126103385781359067ffffffffffffffff928383168093036102ce576024356001600160a01b03948582168092036102ce576044359081168091036102ce5786957f000000000000000000000000000000000000000000000000000000000000000016330361046557610259946103fc9260249286519260208401928352878401526060830152606082526103df82610625565b85519687926280000360e11b60208501525180928585019061079c565b73333333333333333333333333333333333333333392833b15610334576104399385928385518097819582946317938e1360e01b845283016107bd565b03925af190811561045c575061044c5750f35b610455906105fd565b6100d15780f35b513d84823e3d90fd5b8580fd5b83346100d1576100ce6100c5366105b5565b91905034610338573660031901126100af57356001600160a01b038082168092036102ce577f00000000000000000000000000000000000000000000000000000000000000001633036100af576100ce906024359033906107e9565b919050346102ce57806003193601126102ce576104f26105a2565b91602435926fffffffffffffffffffffffffffffffff84168094036102ce576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633036102ce5761054a90610663565b9282519063ffffffff602083019516855283820152828152606081019381851067ffffffffffffffff8611176102d25761026891602491868652630100000b60e01b608083015261025982518092608485019061079c565b6004359063ffffffff821682036102ce57565b60809060031901126102ce576004356001600160a01b03811681036102ce579060243567ffffffffffffffff811681036102ce579060443590606435805f0b81036102ce5790565b67ffffffffffffffff811161061157604052565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761061157604052565b90601f8019910116810190811067ffffffffffffffff82111761061157604052565b9061271063ffffffff8093160191821161067957565b634e487b7160e01b5f52601160045260245ffd5b9092916001600160a01b0392837f00000000000000000000000000000000000000000000000000000000000000001633036102ce57805f0b80155f1461071c575050915b826106dd575b50505050565b67ffffffffffffffff732000000000000000000000000000000000000000941684018094116106795780610713941691166107e9565b5f8080806106d7565b5f81131561075c57506107319060ff166108f1565b8015610748578106810390811161067957916106d1565b634e487b7160e01b5f52601260045260245ffd5b90505f9391930380805f0b03610679576107789060ff166108f1565b9280938181810204149015176106d157634e487b7160e01b5f52601160045260245ffd5b5f5b8381106107ad5750505f910152565b818101518382015260200161079e565b604091602082526107dd815180928160208601526020868601910161079c565b601f01601f1916010190565b9190604051906020938483019363a9059cbb60e01b85526001600160a01b03809316602485015260448401526044835261082283610625565b169160405190604082019067ffffffffffffffff9383831085841117610611577f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564875f9586956040528181520152519082865af13d156108e4573d918211610611576108ac926040519261089f86601f19601f8401160185610641565b83523d5f8685013e610902565b80519182159182156108c4575b50509050156102ce57565b8092508193810103126102ce57015180151581036102ce57805f806108b9565b6108ac9260609250610902565b60ff16604d811161067957600a0a90565b901561091c57815115610913575090565b3b156102ce5790565b5080519081156102ce57602001fdfea2646970667358221220c909e7dbe656d7c973709e34eb673837a89b1ae88012e9262063134290e6f21f64736f6c634300081800336c70e510d36398bee89cc6e19ea6807a9915863d7d724712e0b3c15b01368b00a2646970667358221220d7009235facd2afacf7dbd784aa03c97dd6444be46f4a33aa486147317b81a7764736f6c63430008180033000000000000000000000000002e76dc036a1eff1488ee5435ee66c6abf32674000000000000000000000000b88339cb7199b77e23db6e890353e22632ba630f" } ], "isFixedGasLimit": false }, { - "hash": "0x160e0989eab0766946934f34fa6a417808e6fb5065c5099ff4b6d71f88b46537", + "hash": "0x2d31d35b8f8ed4e1f7f84b15c97c9c0e95798883948b4bcf349229e1ab62d82d", "transactionType": "CALL", "contractName": "DonationBox", - "contractAddress": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6", + "contractAddress": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", "function": "transferOwnership(address)", - "arguments": ["0xb63c02e60C05F05975653edC83F876C334E07C6d"], + "arguments": ["0x83e245941BefbDe29682dF068Bcda006A804eb0C"], "transaction": { "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", - "to": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6", - "gas": "0x9925", + "to": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", + "gas": "0x9922", "value": "0x0", - "input": "0xf2fde38b000000000000000000000000b63c02e60c05f05975653edc83f876c334e07c6d", - "nonce": "0x29e", + "input": "0xf2fde38b00000000000000000000000083e245941befbde29682df068bcda006a804eb0c", + "nonce": "0x245", "chainId": "0x3e7" }, "additionalContracts": [], @@ -72,43 +72,43 @@ "receipts": [ { "status": "0x1", - "cumulativeGasUsed": "0x2b13f3", + "cumulativeGasUsed": "0xc11a2", "logs": [ { - "address": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6", + "address": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", "topics": [ "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" ], "data": "0x", - "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", - "blockNumber": "0x1453728", - "blockTimestamp": "0x693714f0", - "transactionHash": "0xf8b97d99b6c68adf40a93b675eee70389c3c3d621d018dceb796c2cf58949678", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d", "transactionIndex": "0x3", - "logIndex": "0x65", + "logIndex": "0x22", "removed": false } ], - "logsBloom": "0x00000000000000000000000000000800000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001000000000000000000000000000000000000020000000000000000000802000000000000000000000000040000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000000000000000000000040000400000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000880000000000000000000000000000000000000000008020000001000000000000000000000000000000000000000000000000000000000000", "type": "0x2", - "transactionHash": "0xf8b97d99b6c68adf40a93b675eee70389c3c3d621d018dceb796c2cf58949678", + "transactionHash": "0x16b942e66df79bdf146ba03b8232604d966ae571282c4229eb3d0e9f8fcebf8d", "transactionIndex": "0x3", - "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", - "blockNumber": "0x1453728", - "gasUsed": "0x3d4eb", - "effectiveGasPrice": "0x16a5a32d", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "gasUsed": "0x3f377", + "effectiveGasPrice": "0x6052340", "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", "to": null, - "contractAddress": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6" + "contractAddress": "0x002e76dc036a1eff1488ee5435ee66c6abf32674" }, { "status": "0x1", - "cumulativeGasUsed": "0x9a83e7", + "cumulativeGasUsed": "0x79e7fa", "logs": [ { - "address": "0xb63c02e60c05f05975653edc83f876c334e07c6d", + "address": "0x83e245941befbde29682df068bcda006a804eb0c", "topics": [ "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", "0x5300fde95a5e446527bf6aa7c91bd6661bef5398afc77061d9bc87efb80b7ef6", @@ -116,16 +116,16 @@ "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x", - "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", - "blockNumber": "0x1453728", - "blockTimestamp": "0x693714f0", - "transactionHash": "0x7ba663e21e7399474f6ce30190460110c285ff703b1bc1f661936550fd4997a5", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", "transactionIndex": "0x4", - "logIndex": "0x66", + "logIndex": "0x23", "removed": false }, { - "address": "0xb63c02e60c05f05975653edc83f876c334e07c6d", + "address": "0x83e245941befbde29682df068bcda006a804eb0c", "topics": [ "0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff", "0x880a9ba888678c7fe4e8c4f028c224f26ce12a3bed6e96025c61ef8a5db6312f", @@ -133,16 +133,16 @@ "0x0000000000000000000000000000000000000000000000000000000000000000" ], "data": "0x", - "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", - "blockNumber": "0x1453728", - "blockTimestamp": "0x693714f0", - "transactionHash": "0x7ba663e21e7399474f6ce30190460110c285ff703b1bc1f661936550fd4997a5", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", "transactionIndex": "0x4", - "logIndex": "0x67", + "logIndex": "0x24", "removed": false }, { - "address": "0xb63c02e60c05f05975653edc83f876c334e07c6d", + "address": "0x83e245941befbde29682df068bcda006a804eb0c", "topics": [ "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -150,65 +150,65 @@ "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d" ], "data": "0x", - "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", - "blockNumber": "0x1453728", - "blockTimestamp": "0x693714f0", - "transactionHash": "0x7ba663e21e7399474f6ce30190460110c285ff703b1bc1f661936550fd4997a5", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", "transactionIndex": "0x4", - "logIndex": "0x68", + "logIndex": "0x25", "removed": false } ], - "logsBloom": "0x00000004000000000800000000000000080002000000000000000080000000000100000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000020000400000000000000800000000000000000000000000040000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000008000000000000000000000001000000000000000000000000000800000000000000000000000000000000100000000000020100001000000000000000000000000000100000000000000000000000000000000", + "logsBloom": "0x00000004000000000800000000000000080000000000000000000080000000000100000000000000000000000000000000000000001000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000020000400000000000000800000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000800000000000000000000000000000000100000000000020100001000000000000000000000000000100000004000000000000000001000000", "type": "0x2", - "transactionHash": "0x7ba663e21e7399474f6ce30190460110c285ff703b1bc1f661936550fd4997a5", + "transactionHash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802", "transactionIndex": "0x4", - "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", - "blockNumber": "0x1453728", - "gasUsed": "0x6f6ff4", - "effectiveGasPrice": "0x16a5a32d", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "gasUsed": "0x6dd658", + "effectiveGasPrice": "0x6052340", "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", "to": null, - "contractAddress": "0xb63c02e60c05f05975653edc83f876c334e07c6d" + "contractAddress": "0x83e245941befbde29682df068bcda006a804eb0c" }, { "status": "0x1", - "cumulativeGasUsed": "0x9af2c7", + "cumulativeGasUsed": "0x7a56d9", "logs": [ { - "address": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6", + "address": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", "topics": [ "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", "0x0000000000000000000000009a8f92a830a5cb89a3816e3d267cb7791c16b04d", - "0x000000000000000000000000b63c02e60c05f05975653edc83f876c334e07c6d" + "0x00000000000000000000000083e245941befbde29682df068bcda006a804eb0c" ], "data": "0x", - "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", - "blockNumber": "0x1453728", - "blockTimestamp": "0x693714f0", - "transactionHash": "0x160e0989eab0766946934f34fa6a417808e6fb5065c5099ff4b6d71f88b46537", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "blockTimestamp": "0x692f466c", + "transactionHash": "0x2d31d35b8f8ed4e1f7f84b15c97c9c0e95798883948b4bcf349229e1ab62d82d", "transactionIndex": "0x5", - "logIndex": "0x69", + "logIndex": "0x26", "removed": false } ], - "logsBloom": "0x00000000000000000000000000000800000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001000000000000000000000000000000000000000000000020000000000002000000000000000000000000040000410040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000001000000000000100000000000000000000000000000000000000000000000000000000000000000000000040000400000000000000000000000200000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000880000000000000000000000000000000000000000008000000001000000000000000000000000000000000000000000000000000000000000", "type": "0x2", - "transactionHash": "0x160e0989eab0766946934f34fa6a417808e6fb5065c5099ff4b6d71f88b46537", + "transactionHash": "0x2d31d35b8f8ed4e1f7f84b15c97c9c0e95798883948b4bcf349229e1ab62d82d", "transactionIndex": "0x5", - "blockHash": "0x4763697f5441a6ed9aa4f7eace879c55b4720ee2406b5298db03fb4349318806", - "blockNumber": "0x1453728", - "gasUsed": "0x6ee0", - "effectiveGasPrice": "0x16a5a32d", + "blockHash": "0xb057afed72386c4900b363bee6966fe39ca786f0b43f85943d107bc040a365d7", + "blockNumber": "0x13d47a9", + "gasUsed": "0x6edf", + "effectiveGasPrice": "0x6052340", "from": "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", - "to": "0xbc217096db9eb6d2782c1d9e725d462077a4d1f6", + "to": "0x002e76dc036a1eff1488ee5435ee66c6abf32674", "contractAddress": null } ], "libraries": [], "pending": [], "returns": {}, - "timestamp": 1765217588351, + "timestamp": 1764705991008, "chain": 999, - "commit": "5a4c408" + "commit": "4b103fe" } diff --git a/broadcast/deployed-addresses.json b/broadcast/deployed-addresses.json index 10e764c3d..4d72aa918 100644 --- a/broadcast/deployed-addresses.json +++ b/broadcast/deployed-addresses.json @@ -484,9 +484,9 @@ "transaction_hash": "0xf72c3e798991af0e7123197bfd7da40585f936b4353b91159b749008ceac541a" }, "SponsoredCCTPDstPeriphery": { - "address": "0xb63c02e60c05f05975653edc83f876c334e07c6d", - "block_number": 21313320, - "transaction_hash": "0x7ba663e21e7399474f6ce30190460110c285ff703b1bc1f661936550fd4997a5" + "address": "0x83e245941befbde29682df068bcda006a804eb0c", + "block_number": 20793257, + "transaction_hash": "0xb08e0d23618a1447781b9071792c660bfd76db006b105822ea41d65e0ddc2802" }, "PermissionedMulticallHandler": { "address": "0x0980d0f6799ca06c71ffafdc0e423cf2b0f20502", diff --git a/broadcast/deployed-addresses.md b/broadcast/deployed-addresses.md index e17821e75..5f7a39ee1 100644 --- a/broadcast/deployed-addresses.md +++ b/broadcast/deployed-addresses.md @@ -160,7 +160,7 @@ This file contains the latest deployed smart contract addresses from the broadca | Contract Name | Address | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| DonationBox | [0xbC217096db9EB6d2782c1d9E725D462077a4d1f6](https://hyperevmscan.io//address/0xbC217096db9EB6d2782c1d9E725D462077a4d1f6) | +| DonationBox | [0x002E76DC036A1efF1488ee5435eE66C6aBF32674](https://hyperevmscan.io//address/0x002E76DC036A1efF1488ee5435eE66C6aBF32674) | | DonationBox | [0x90E2487764E5316a2e4109c2Ed40A3B3ad423659](https://hyperevmscan.io//address/0x90E2487764E5316a2e4109c2Ed40A3B3ad423659) | | DstOFTHandler | [0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461](https://hyperevmscan.io//address/0x40153DdFAd90C49dbE3F5c9F96f2a5B25ec67461) | | Helios | [0xc19B7EF43a6eBd393446F401d1eCFac01B181ac0](https://hyperevmscan.io//address/0xc19B7EF43a6eBd393446F401d1eCFac01B181ac0) | @@ -170,7 +170,7 @@ This file contains the latest deployed smart contract addresses from the broadca | SpokePool | [0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04](https://hyperevmscan.io//address/0x35E63eA3eb0fb7A3bc543C71FB66412e1F6B0E04) | | SpokePoolPeriphery | [0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb](https://hyperevmscan.io//address/0xF1BF00D947267Da5cC63f8c8A60568c59FA31bCb) | | SpokePoolVerifier | [0x3Fb9cED51E968594C87963a371Ed90c39519f65A](https://hyperevmscan.io//address/0x3Fb9cED51E968594C87963a371Ed90c39519f65A) | -| SponsoredCCTPDstPeriphery | [0xb63c02e60C05F05975653edC83F876C334E07C6d](https://hyperevmscan.io//address/0xb63c02e60C05F05975653edC83F876C334E07C6d) | +| SponsoredCCTPDstPeriphery | [0x83e245941BefbDe29682dF068Bcda006A804eb0C](https://hyperevmscan.io//address/0x83e245941BefbDe29682dF068Bcda006A804eb0C) | ## Lisk (1135) From 760dc35a2f411be301d007741ba51e881f865628 Mon Sep 17 00:00:00 2001 From: Ihor Farion Date: Mon, 8 Dec 2025 17:32:12 -0800 Subject: [PATCH 47/47] fix finalizeSwapFlows funding ordering issue introduced by USDC linking method Signed-off-by: Ihor Farion --- .../mintburn/HyperCoreFlowExecutor.sol | 85 +++++++++++-------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol index 1b6457836..3e71df8ab 100644 --- a/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol +++ b/contracts/periphery/mintburn/HyperCoreFlowExecutor.sol @@ -741,29 +741,34 @@ contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow address(finalTokenInfo.swapHandler), finalCoreTokenInfo.coreIndex ); + + uint64[] memory totalToSendAmounts = new uint64[](quoteNonces.length); + uint64[] memory additionalToSendAmounts = new uint64[](quoteNonces.length); uint64 totalAdditionalToSend = 0; + for (; finalized < quoteNonces.length; ++finalized) { - bool success; - uint64 additionalToSend; - (success, additionalToSend, availableBalance) = _finalizeSingleSwap( - quoteNonces[finalized], - limitOrderOuts[finalized], - finalCoreTokenInfo, - finalTokenInfo.swapHandler, - finalToken, - availableBalance - ); - if (!success) { - break; - } + ( + bool success, + uint64 totalToSend, + uint64 additionalToSend, + uint64 remainingBalance + ) = _prepareSwapFinalization( + quoteNonces[finalized], + limitOrderOuts[finalized], + finalToken, + availableBalance + ); + if (!success) break; + + totalToSendAmounts[finalized] = totalToSend; + additionalToSendAmounts[finalized] = additionalToSend; totalAdditionalToSend += additionalToSend; + availableBalance = remainingBalance; } - if (finalized > 0) { - $.lastPullFundsBlock[finalToken] = block.number; - } else { - return 0; - } + if (finalized == 0) return 0; + + $.lastPullFundsBlock[finalToken] = block.number; if (totalAdditionalToSend > 0) { (uint256 totalAdditionalToSendEVM, uint64 totalAdditionalReceivedCore) = HyperCoreLib @@ -797,23 +802,41 @@ contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals ); } + + for (uint256 i = 0; i < finalized; ++i) { + SwapFlowState storage swap = $.swaps[quoteNonces[i]]; + (uint256 additionalToSendEVM, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( + additionalToSendAmounts[i], + finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals + ); + + finalTokenInfo.swapHandler.transferFundsToUserOnCore( + finalCoreTokenInfo.coreIndex, + swap.finalRecipient, + totalToSendAmounts[i] + ); + emit SwapFlowFinalized( + quoteNonces[i], + swap.finalRecipient, + finalToken, + totalToSendAmounts[i], + additionalToSendEVM + ); + } } - /// @notice Finalizes a single swap flow, sending the tokens to user on core. Relies on caller to send the `additionalToSend` - function _finalizeSingleSwap( + /// @notice Finalizes a single swap flow state, calculating amounts but not sending tokens yet + function _prepareSwapFinalization( bytes32 quoteNonce, uint64 limitOrderOut, - CoreTokenInfo memory finalCoreTokenInfo, - SwapHandler swapHandler, - address finalToken, + address expectedToken, uint64 availableBalance - ) internal returns (bool success, uint64 additionalToSend, uint64 balanceRemaining) { + ) internal returns (bool success, uint64 totalToSend, uint64 additionalToSend, uint64 balanceRemaining) { SwapFlowState storage swap = _getMainStorage().swaps[quoteNonce]; if (swap.finalRecipient == address(0)) revert SwapDoesNotExist(); if (swap.finalized) revert SwapAlreadyFinalized(); - if (swap.finalToken != finalToken) revert WrongSwapFinalizationToken(quoteNonce); + if (swap.finalToken != expectedToken) revert WrongSwapFinalizationToken(quoteNonce); - uint64 totalToSend; (totalToSend, additionalToSend) = _calcSwapFlowSendAmounts( limitOrderOut, swap.minAmountToSend, @@ -824,20 +847,12 @@ contract HyperCoreFlowExecutor is AccessControlUpgradeable, AuthorizedFundedFlow // `additionalToSend` will land on HCore before this core > core send will need to be executed balanceRemaining = availableBalance + additionalToSend; if (totalToSend > balanceRemaining) { - return (false, 0, availableBalance); + return (false, 0, 0, availableBalance); } swap.finalized = true; success = true; balanceRemaining -= totalToSend; - - (uint256 additionalToSendEVM, ) = HyperCoreLib.minimumCoreReceiveAmountToAmounts( - additionalToSend, - finalCoreTokenInfo.tokenInfo.evmExtraWeiDecimals - ); - - swapHandler.transferFundsToUserOnCore(finalCoreTokenInfo.coreIndex, swap.finalRecipient, totalToSend); - emit SwapFlowFinalized(quoteNonce, swap.finalRecipient, swap.finalToken, totalToSend, additionalToSendEVM); } /// @notice Forwards `amount` plus potential sponsorship funds (for bridging fee) to user on HyperEVM