ETH Price: $2,878.76 (-2.08%)

Contract

0x2AC500c5f53D0219B1691B785aa857A0fbb2035D
 

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Underwrite And C...135634052025-01-03 20:37:05387 days ago1735936625IN
0x2AC500c5...0fbb2035D
0 ETH0.000006580.00118076
Underwrite And C...135592252025-01-03 18:17:45387 days ago1735928265IN
0x2AC500c5...0fbb2035D
0 ETH0.000002180.00146289
Underwrite And C...135584902025-01-03 17:53:15387 days ago1735926795IN
0x2AC500c5...0fbb2035D
0 ETH0.00000110.00130554
Underwrite And C...135464792025-01-03 11:12:53387 days ago1735902773IN
0x2AC500c5...0fbb2035D
0 ETH0.000000470.00145326
Underwrite And C...134688272025-01-01 16:04:29389 days ago1735747469IN
0x2AC500c5...0fbb2035D
0 ETH0.000002070.00597639
Underwrite And C...133298732024-12-29 10:52:41392 days ago1735469561IN
0x2AC500c5...0fbb2035D
0 ETH0.000001640.00401748
Underwrite And C...132408692024-12-27 9:25:53394 days ago1735291553IN
0x2AC500c5...0fbb2035D
0 ETH0.000006290.00121722
Underwrite And C...132405252024-12-27 9:14:25394 days ago1735290865IN
0x2AC500c5...0fbb2035D
0 ETH0.000008830.00126998
Underwrite And C...132084292024-12-26 15:24:33395 days ago1735226673IN
0x2AC500c5...0fbb2035D
0 ETH0.000025440.00118171
Underwrite And C...132084292024-12-26 15:24:33395 days ago1735226673IN
0x2AC500c5...0fbb2035D
0 ETH0.000025440.00118171
Underwrite And C...132084282024-12-26 15:24:31395 days ago1735226671IN
0x2AC500c5...0fbb2035D
0 ETH0.000025670.0011813
Underwrite And C...131739482024-12-25 20:15:11396 days ago1735157711IN
0x2AC500c5...0fbb2035D
0 ETH0.000001790.00138328
Underwrite And C...131295422024-12-24 19:34:59397 days ago1735068899IN
0x2AC500c5...0fbb2035D
0 ETH0.000010150.00128544
Underwrite And C...131179182024-12-24 13:07:31397 days ago1735045651IN
0x2AC500c5...0fbb2035D
0 ETH0.000023710.00121415
Underwrite And C...131174412024-12-24 12:51:37397 days ago1735044697IN
0x2AC500c5...0fbb2035D
0 ETH0.000008180.00123598
Underwrite And C...131164562024-12-24 12:18:47397 days ago1735042727IN
0x2AC500c5...0fbb2035D
0 ETH0.000001280.00137455
Underwrite And C...131162762024-12-24 12:12:47397 days ago1735042367IN
0x2AC500c5...0fbb2035D
0 ETH0.000001010.00135729
Underwrite And C...131158452024-12-24 11:58:25397 days ago1735041505IN
0x2AC500c5...0fbb2035D
0 ETH0.000001180.00138776
Underwrite And C...127836462024-12-16 19:25:07405 days ago1734377107IN
0x2AC500c5...0fbb2035D
0 ETH0.00000450.00202999
Underwrite And C...127799312024-12-16 17:21:17405 days ago1734369677IN
0x2AC500c5...0fbb2035D
0 ETH0.000006320.00145619
Underwrite And C...127660182024-12-16 9:37:31405 days ago1734341851IN
0x2AC500c5...0fbb2035D
0 ETH0.000006190.00155407
Underwrite And C...127654192024-12-16 9:17:33405 days ago1734340653IN
0x2AC500c5...0fbb2035D
0 ETH0.000005270.00186152
Underwrite And C...127625602024-12-16 7:42:15405 days ago1734334935IN
0x2AC500c5...0fbb2035D
0 ETH0.000003090.00153444
Underwrite And C...127432142024-12-15 20:57:23406 days ago1734296243IN
0x2AC500c5...0fbb2035D
0 ETH0.000001210.0016641
Underwrite And C...125977772024-12-12 12:09:29409 days ago1734005369IN
0x2AC500c5...0fbb2035D
0 ETH0.00000320.00599759
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
162648802025-03-07 9:26:15324 days ago1741339575
0x2AC500c5...0fbb2035D
0 ETH
162648802025-03-07 9:26:15324 days ago1741339575
0x2AC500c5...0fbb2035D
0 ETH
162648802025-03-07 9:26:15324 days ago1741339575
0x2AC500c5...0fbb2035D
0 ETH
162374182025-03-06 18:10:51325 days ago1741284651
0x2AC500c5...0fbb2035D
0 ETH
162374182025-03-06 18:10:51325 days ago1741284651
0x2AC500c5...0fbb2035D
0 ETH
162374182025-03-06 18:10:51325 days ago1741284651
0x2AC500c5...0fbb2035D
0 ETH
158386372025-02-25 12:38:09334 days ago1740487089
0x2AC500c5...0fbb2035D
0 ETH
158386372025-02-25 12:38:09334 days ago1740487089
0x2AC500c5...0fbb2035D
0 ETH
155366372025-02-18 12:51:29341 days ago1739883089
0x2AC500c5...0fbb2035D
0 ETH
155366372025-02-18 12:51:29341 days ago1739883089
0x2AC500c5...0fbb2035D
0 ETH
155349022025-02-18 11:53:39341 days ago1739879619
0x2AC500c5...0fbb2035D
0.00000258 ETH
155349022025-02-18 11:53:39341 days ago1739879619
0x2AC500c5...0fbb2035D
0.00000258 ETH
154999492025-02-17 16:28:33342 days ago1739809713
0x2AC500c5...0fbb2035D
0 ETH
154999492025-02-17 16:28:33342 days ago1739809713
0x2AC500c5...0fbb2035D
0 ETH
154975632025-02-17 15:09:01342 days ago1739804941
0x2AC500c5...0fbb2035D
0.0000024 ETH
154975632025-02-17 15:09:01342 days ago1739804941
0x2AC500c5...0fbb2035D
0.0000024 ETH
154615492025-02-16 19:08:33343 days ago1739732913
0x2AC500c5...0fbb2035D
0 ETH
154615492025-02-16 19:08:33343 days ago1739732913
0x2AC500c5...0fbb2035D
0 ETH
154615492025-02-16 19:08:33343 days ago1739732913
0x2AC500c5...0fbb2035D
0 ETH
154613532025-02-16 19:02:01343 days ago1739732521
0x2AC500c5...0fbb2035D
0 ETH
154613532025-02-16 19:02:01343 days ago1739732521
0x2AC500c5...0fbb2035D
0 ETH
154613532025-02-16 19:02:01343 days ago1739732521
0x2AC500c5...0fbb2035D
0 ETH
154603982025-02-16 18:30:11343 days ago1739730611
0x2AC500c5...0fbb2035D
0 ETH
154603982025-02-16 18:30:11343 days ago1739730611
0x2AC500c5...0fbb2035D
0 ETH
154583122025-02-16 17:20:39343 days ago1739726439
0x2AC500c5...0fbb2035D
0.00000893 ETH
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CatalystChainInterface

Compiler Version
v0.8.22+commit.4fc1097e

Optimization Enabled:
Yes with 10000 runs

Other Settings:
paris EvmVersion, None license
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;

import {ERC20} from 'solady/tokens/ERC20.sol';
import {SafeTransferLib} from 'solady/utils/SafeTransferLib.sol';
import { Ownable} from "solady/auth/Ownable.sol";

import { IMessageEscrowStructs } from "GeneralisedIncentives/src/interfaces/IMessageEscrowStructs.sol";
import { IIncentivizedMessageEscrow } from "GeneralisedIncentives/src/interfaces/IIncentivizedMessageEscrow.sol";
import { Bytes65 } from "GeneralisedIncentives/src/utils/Bytes65.sol";

import { ICatalystChainInterface } from "./interfaces/ICatalystChainInterface.sol";
import { ICatalystReceiver } from "./interfaces/IOnCatalyst.sol";
import "./interfaces/ICatalystV1VaultState.sol"; // structs
import { ICatalystV1Vault } from "./ICatalystV1Vault.sol";
import "./ICatalystV1Vault.sol";
import "./CatalystPayload.sol";


/**
 * @title Catalyst Cross-chain Interface for Generalised Incentives.
 * @author Cata Labs Inc.
 * @notice Cross-chain interface built using Generalised Incentives.
 * Catalyst vaults depends on a translator contract to execute cross-chain swaps to convert
 * the swap details into a bytearray that can be sent to the destination chain.
 *
 * The cross-chain interface is also in charge of extending messaging protocol with additional features
 * like underwriting. 
 * 
 * Underwriting is facilitated by allowing external actors to front the output of a swap. The cross-chain interfaces creates 
 * an appropriate escrow on the vault and then pays the user. Once the incoming swap arrives, it is intercepted to release the
 * escrow to then pay the underwriter.
 *
 * @dev To adopt the cross-chain interface for another messaging protocol, you need to
 * - Change the message submission flow: (GARP.submitMessage(...))
 * - Change the package callbakcs (receiveMessage, receiveAck)
 *
 * However, it may be required to also modify the vaults as the incentive structure is currently fixed.
 */
contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 {
    
    //--- ERRORS ---//
    /** @dev Only the message router should be able to deliver messages. */
    error InvalidCaller(); // 48f5c3ed 

    error InvalidContext(bytes1 context); // 9f769791
    error InvalidAddress(); // e6c4247b
    error InvalidSourceApplication(); // 003923e0
    error NotEnoughIncentives(uint256 expected, uint256 actual); // 6de78246
    error ChainAlreadySetup(); // b8e35614
    
    //-- Underwriting Errors --//
    error SwapAlreadyUnderwritten(); // d0c27c9f
    error UnderwriteDoesNotExist(bytes32 identifier); // ae029d69
    error UnderwriteNotExpired(uint256 blocksUnitilExpiry); // 62141db5
    error MaxUnderwriteDurationTooLong(); // 3f6368aa
    error MaxUnderwriteDurationTooShort(); // 6229dcd0
    error NoVaultConnection(); // ea66ca6d
    error SwapRecentlyUnderwritten(); // 695b3a94

    //--- Events ---//

    event SwapFailed(bytes1 error);
    event RemoteImplementationSet(bytes32 chainIdentifier, bytes remoteCCI, bytes remoteGARP);
    event MaxUnderwriteDuration(uint256 newMaxUnderwriteDuration);
    
    event MinGasFor(
        bytes32 identifier,
        uint48 minGas
    );

    //-- Underwriting Events --//

    event SwapUnderwritten(
        bytes32 indexed identifier,
        address indexed underwriter,
        uint96 expiry,
        address targetVault,
        address toAsset,
        uint256 U,
        address toAccount,
        uint256 outAmount
    );

    event FulfillUnderwrite(
        bytes32 indexed identifier
    );

    event ExpireUnderwrite(
        bytes32 indexed identifier,
        address expirer,
        uint256 reward
    );

    //--- Structs ---//

    struct UnderwritingStorage {
        uint256 tokens;     // 1 slot
        address refundTo;   // 2 slot: 20/32
        uint96 expiry;      // 2 slot: 32/32
    }

    //--- Underwrite Duration Frame Parameters ---//
    // Underwriters should take on as little risk as possible. One risk associated with underwriting is
    // chain haulting or timestamp manipulation / diviation. To combat this risk, time is measured in blocks.
    // This implies that there needs to be some configuration if the block time changes over time or to fit it
    // to various blockchains.
    // The reason behind is if the underwrite duration was timestamp based, say 8 hours. If the chain haulted for
    // 10 hours, then the underwriter would risk being expired and lose everything. If a chain doesn't produce any
    // blocks during a hault, then it wouldn't be a risk to an underwriter.
    // Generally, it is more common for unpredictable block slowdowns rather than unpredictable block speedups.

    /// @notice The initial underwrite duration (in blocks).
    /// @dev Is 8 hours if the block time is 2 seconds, 48 hours if the block time is 12 seconds. Not a great initial value but better than nothing.
    uint256 constant INITIAL_MAX_UNDERWRITE_BLOCK_DURATION = 24 hours / 6 seconds; 

    /// @notice The maximum configurable underwrite duration (in blocks).
    /// @dev Is 9.3 days if the block time is 2 seconds, 56 days if the block time is 12 seconds.
    uint256 constant MAX_UNDERWRITE_BLOCK_DURATION = 14 days / 3 seconds;  

    /// @notice The minimum configurable underwrite duration (in blocks).
    /// @dev Is 10 minutes if the block time is 2 seconds, 1 hour if the block time is 12 seconds.
    uint256 constant MIN_UNDERWRITE_BLOCK_DURATION = 1 hours / 12 seconds;  

    //--- Config ---//

    /** @notice The generalised incentives endpoint */
    IIncentivizedMessageEscrow public immutable GARP; // Set on deployment

    //-- Underwriting Config--//

    /** 
     * @notice How many blocks should there be between when an identifier can be underwritten.
     * @dev The purpose of these buffer blocks is to ensure that underwriters don't mistakenly underwrite swaps right after they are filled.
     */
    uint24 constant BUFFER_BLOCKS = 4;

    /** @notice Set underwriter collateral. Is of UNDERWRITING_COLLATERAL_DENOMINATOR */
    uint256 constant public UNDERWRITING_COLLATERAL = 35;  // 3.5% extra as collateral.
    uint256 constant public UNDERWRITING_COLLATERAL_DENOMINATOR = 1000;

    /** @notice How much of the collateral is given to the exipirer the rest goes to the vault. Is of EXPIRE_CALLER_REWARD_DENOMINATOR. */
    uint256 constant public EXPIRE_CALLER_REWARD = 350;  // 35% of the 3.5% = 1.225%. Of $1000 = $12.25
    uint256 constant public EXPIRE_CALLER_REWARD_DENOMINATOR = 1000;

    //--- Storage ---//

    /** @notice The destination address on the chain by chain identifier. */
    mapping(bytes32 => bytes) public chainIdentifierToDestinationAddress;

    /** @notice The minimum amount of gas for a specific chain. bytes32(0) indicates ack. */
    mapping(bytes32 => uint48) public minGasFor;

    //-- Underwriting Storage --//

    /** 
     * @notice Sets the maximum duration for underwriting.
     * @dev Should be set long enough for all swaps to be able to confirm + a small buffer
     * Should also be set short enough to not take up an excess amount of escrow usage.
     */
    uint256 public maxUnderwritingDuration = INITIAL_MAX_UNDERWRITE_BLOCK_DURATION;

    /** 
     * @notice Maps underwriting identifiers to underwriting state.
     * refundTo can be checked to see if the ID has been underwritten.
     */
     mapping(bytes32 => UnderwritingStorage) public underwritingStorage;

    constructor(address GARP_, address defaultOwner) payable {
        require(address(GARP_) != address(0));  // dev: GARP_ cannot be zero address
        GARP = IIncentivizedMessageEscrow(GARP_);
        _initializeOwner(defaultOwner);

        emit MaxUnderwriteDuration(INITIAL_MAX_UNDERWRITE_BLOCK_DURATION);
    }

    //-- Admin--//

    /** 
     * @notice Allow updating of the minimum gas limit.
     * @dev Set chainIdentifier to 0 for gas for ack. 
     */
    function setMinGasFor(bytes32 chainIdentifier, uint48 minGas) override external onlyOwner {
        minGasFor[chainIdentifier] = minGas;

        emit MinGasFor(chainIdentifier, minGas);
    }

    /**
     * @notice Sets the new max underwrite duration: That is the period of time before an underwrite can be expired.
     * When an underwrite is expired, the underwriter loses all capital provided.
     * @dev This function can be exploited by the owner. By setting newMaxUnderwriteDuration to (almost) 0 right before 
     * someone calls underwrite and then expiring them before the actual swap arrives. The min protection here is not
     * sufficient since it needs to be well into when a message can be validated. As a result, the owner of this contract
     * should be a timelock which underwriters monitor or trusted by underwriters.
     */
    function setMaxUnderwritingDuration(uint256 newMaxUnderwriteDuration) onlyOwner override external {
        if (newMaxUnderwriteDuration <= MIN_UNDERWRITE_BLOCK_DURATION) revert MaxUnderwriteDurationTooShort();
        // If the underwriting duration is too long, users can freeze up a lot of value for not a lot of cost.
        if (newMaxUnderwriteDuration > MAX_UNDERWRITE_BLOCK_DURATION) revert MaxUnderwriteDurationTooLong();

        maxUnderwritingDuration = newMaxUnderwriteDuration;
        
        emit MaxUnderwriteDuration(newMaxUnderwriteDuration);
    }

    /**
     * @notice Check that the incentives are set. This is important for the system work
     * without external monitoring. Especially the ack gas limit is important.
     * @dev Is it enforced that the gas price of ack has to be 10% higher than the gas price spent on the submitted transaction.
     */
    modifier checkRouteDescription(ICatalystV1Vault.RouteDescription calldata routeDescription) {
        // -- Check incentives -- //
        ICatalystV1Vault.IncentiveDescription calldata incentive = routeDescription.incentive;
        // 1. Gas limits
        uint48 minGasChainIdentifier = minGasFor[routeDescription.chainIdentifier];
        if (incentive.maxGasDelivery < minGasChainIdentifier) revert NotEnoughIncentives(minGasChainIdentifier, incentive.maxGasDelivery);
        uint48 minGasDefault = minGasFor[bytes32(0)];
        if (incentive.maxGasAck < minGasDefault) revert NotEnoughIncentives(minGasDefault, incentive.maxGasAck);

        // 2. Gas prices
        // The gas price of ack has to be 10% higher than the gas price spent on this transaction.
        if (incentive.priceOfAckGas < tx.gasprice * 11 / 10) revert NotEnoughIncentives(tx.gasprice * 11 / 10, incentive.priceOfAckGas);

        // -- Check Address Lengths -- //
        // toAccount
        if (!_checkBytes65(routeDescription.toAccount)) revert InvalidBytes65Address();

        // toVault
        if (!_checkBytes65(routeDescription.toVault)) revert InvalidBytes65Address();

        _;
    }

    modifier onlyGARP() {
        if (msg.sender != address(GARP)) revert InvalidCaller();
        _;
    }

    modifier verifySourceChainAddress(bytes32 sourceChainIdentifier, bytes calldata fromApplication) {
        if (keccak256(fromApplication) != keccak256(chainIdentifierToDestinationAddress[sourceChainIdentifier])) revert InvalidSourceApplication();
        _;
    }

    //-- Transparent viewer --//

    /**
     * @notice Estimate the addition verification cost beyond the cost paid to the relayer.
     * @dev This is implement as a simple lookup on GARP.
     */
    function estimateAdditionalCost() override external view returns(address asset, uint256 amount) {
        (asset, amount) = GARP.estimateAdditionalCost();
    }

    //-- Functions --//

    /**
     * @notice matches the hash of error calldata to common revert functions
     * and then reverts a relevant ack which can be exposed on the origin to provide information
     * about why the transaction didn't execute as expected.
     * @param err The error in bytes
     */
    function _handleError(bytes memory err) pure internal returns (bytes1) {
        // To only get the error identifier, only use the first 8 bytes. This lets us add additional error
        // data for easier debugger on trace.
        bytes8 errorIdentifier = bytes8(err);
        // We can use memory slices to get better insight into exactly the error which occured.
        // This would also allow us to reuse events.
        // However, it looks like it will significantly increase gas costs so this works for now.
        // It looks like Solidity will improve their error catch implementation which can replace this.
        if (bytes8(abi.encodeWithSelector(ExceedsSecurityLimit.selector)) == errorIdentifier) return 0x11;
        if (bytes8(abi.encodeWithSelector(ReturnInsufficient.selector)) == errorIdentifier) return 0x12;
        if (bytes8(abi.encodeWithSelector(VaultNotConnected.selector)) == errorIdentifier) return 0x13;
        return 0x10; // unknown error.
    }

    /**
     * @notice Connects this CCI with another contract on another chain.
     * @dev Each chain can only be setup once. This reduces governance risks.
     * @param remoteCCI The bytes65 encoded address on the destination chain.
     * @param remoteGARP The messaging router encoded address on the destination chain.
     */
    function connectNewChain(bytes32 chainIdentifier, bytes calldata remoteCCI, bytes calldata remoteGARP) onlyOwner checkBytes65Address(remoteCCI) override external {
        // Check if the chain has already been set.
        // If it has, we don't allow setting it as another. This would impact existing pools.
        if (chainIdentifierToDestinationAddress[chainIdentifier].length != 0) revert ChainAlreadySetup();

        // Set the remote CCI.
        chainIdentifierToDestinationAddress[chainIdentifier] = remoteCCI;

        emit RemoteImplementationSet(chainIdentifier, remoteCCI, remoteGARP);

        // Set the remote messaging router escrow.
        GARP.setRemoteImplementation(chainIdentifier, remoteGARP);
    }

    /**
     * @notice Packs cross-chain swap information into a bytearray to send to the destination cross-chain interface.
     * @dev Callable by anyone but this cannot be abused since the connection management ensures no wrong messages enter a healthy vault.
     * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
     * @param toAssetIndex The index of the asset the user wants to buy in the target vault.
     * @param U The calculated liquidity reference. (Units)
     * @param minOut The minimum number output of tokens on the target chain.
     * @param fromAmount Escrow related value. The amount returned if the swap fails.
     * @param fromAsset Escrow related value. The asset that was sold.
     * @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16).max)
     * @param calldata_ Data field if a call should be made on the target chain.
     * Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(<address>)), <data>).
     */
    function sendCrossChainAsset(
        ICatalystV1Vault.RouteDescription calldata routeDescription,
        uint8 toAssetIndex,
        uint256 U,
        uint256 minOut,
        uint256 fromAmount,
        address fromAsset,
        uint16 underwriteIncentiveX16,
        bytes calldata calldata_
    ) checkRouteDescription(routeDescription) override external payable {
        // We need to ensure that all information is in the correct places. This ensures that calls to this contract
        // will always be decoded semi-correctly even if the inputs are incorrect. This also checks that the user inputs
        // into the swap contracts are correct while making the cross-chain interface flexible for future implementations.
        // These checks are done by the modifier.

        // Anyone can call this function, but unless someone can also manage to pass the security check on onRecvPacket
        // they cannot drain any value. As such, the very worst they can do is waste gas.

        // Encode payload. See CatalystPayload.sol for the payload definition
        bytes memory data = bytes.concat(
            CTX0_ASSET_SWAP,
            // bytes.concat(
                bytes1(uint8(20)),      // EVM addresses are 20 bytes.
                bytes32(0),     // EVM only uses 20 bytes. abi.encode packs the 20 bytes into 32 then we need to add 32 more
                bytes32(uint256(uint160(msg.sender))),  // Use abi.encode to encode address into 32 bytes
            // ),
            bytes.concat(
                routeDescription.toVault,    // Length is expected to be pre-encoded.
                routeDescription.toAccount,  // Length is expected to be pre-encoded.
                bytes32(U),
                bytes1(toAssetIndex),
                bytes32(minOut),
                bytes32(fromAmount)
            ),
            // bytes.concat(
                bytes1(uint8(20)),      // EVM addresses are 20 bytes.
                bytes32(0),     // EVM only uses 20 bytes. abi.encode packs the 20 bytes into 32 then we need to add 32 more
                bytes32(uint256(uint160(fromAsset))),  // Use abi.encode to encode address into 32 bytes
            // ),
            bytes4(uint32(block.number)),   // This is the same as block.number mod 2**32-1
            bytes2(uint16(underwriteIncentiveX16)),
            bytes2(uint16(calldata_.length)),   // max length of calldata is 2**16-1 = 65535 bytes which should be more than plenty.
            calldata_
        );

        GARP.submitMessage{value: msg.value}(
            routeDescription.chainIdentifier,
            chainIdentifierToDestinationAddress[routeDescription.chainIdentifier],
            data,
            routeDescription.incentive,
            routeDescription.deadline
        );
    }

    /**
     * @notice Packs cross-chain swap information into a bytearray and sends it to the destination cross-chain interface.
     * @dev Callable by anyone but this cannot be abused since the connection management ensures no wrong messages enter a healthy vault.
     * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
     * @param U The calculated liquidity reference. (Units)
     * @param minOut An array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets]
     * @param fromAmount Escrow related value. The amount returned if the swap fails.
     * @param calldata_ Data field if a call should be made on the target chain.
     * Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(<address>)), <data>).
     */
    function sendCrossChainLiquidity(
        ICatalystV1Vault.RouteDescription calldata routeDescription,
        uint256 U,
        uint256[2] calldata minOut,
        uint256 fromAmount,
        bytes calldata calldata_
    ) checkRouteDescription(routeDescription) override external payable {
        // We need to ensure that all information is in the correct places. This ensures that calls to this contract
        // will always be decoded semi-correctly even if the input is incorrect. This also checks that the user inputs
        // into the swap contracts are correct while making the cross-chain interface flexible for future implementations.
        // These checks are done by the modifier.

        // Anyone can call this function, but unless someone can also manage to pass the security check on onRecvPacket
        // they cannot drain any value. As such, the very worst they can do is waste gas.

        // Encode payload. See CatalystPayload.sol for the payload definition
        bytes memory data = bytes.concat(
            CTX1_LIQUIDITY_SWAP,
            // bytes.concat(
                bytes1(uint8(20)),  // EVM addresses are 20 bytes.
                bytes32(0),  // EVM only uses 20 bytes. abi.encode packs the 20 bytes into 32 then we need to add 32 more
                bytes32(uint256(uint160(msg.sender))),  // Use abi.encode to encode address into 32 bytes
            // )
            routeDescription.toVault,  // Length is expected to be pre-encoded.
            routeDescription.toAccount,  // Length is expected to be pre-encoded.
            bytes32(U),
            bytes32(minOut[0]),
            bytes32(minOut[1]),
            bytes32(fromAmount),
            bytes4(uint32(block.number)),
            bytes2(uint16(calldata_.length)),
            calldata_
        );

        GARP.submitMessage{value: msg.value}(
            routeDescription.chainIdentifier,
            chainIdentifierToDestinationAddress[routeDescription.chainIdentifier],
            data,
            routeDescription.incentive,
            routeDescription.deadline
        );
    }

    /**
     * @notice Cross-chain message success handler
     * @dev Should never revert. (on valid messages)
     */
    function _onPacketSuccess(bytes32 destinationIdentifier, bytes calldata data) internal {
        bytes1 context = data[CONTEXT_POS];
        // Since this is a callback, fromVault must be an EVM address.
        address fromVault = address(bytes20(data[ FROM_VAULT_START_EVM : FROM_VAULT_END ]));

        if (context == CTX0_ASSET_SWAP) {
            ICatalystV1Vault(fromVault).onSendAssetSuccess(
                destinationIdentifier,                                                      // connectionId
                data[ TO_ACCOUNT_LENGTH_POS : TO_ACCOUNT_END ],                             // toAccount
                uint256(bytes32(data[ UNITS_START : UNITS_END ])),                          // units
                uint256(bytes32(data[ CTX0_FROM_AMOUNT_START : CTX0_FROM_AMOUNT_END ])),    // fromAmount
                address(bytes20(data[ CTX0_FROM_ASSET_START_EVM : CTX0_FROM_ASSET_END ])),  // fromAsset
                uint32(bytes4(data[ CTX0_BLOCK_NUMBER_START : CTX0_BLOCK_NUMBER_END ]))     // block number
            );
            return;
        }
        if (context == CTX1_LIQUIDITY_SWAP) {
            ICatalystV1Vault(fromVault).onSendLiquiditySuccess(
                destinationIdentifier,                                                      // connectionId
                data[ TO_ACCOUNT_LENGTH_POS : TO_ACCOUNT_END ],                             // toAccount
                uint256(bytes32(data[ UNITS_START : UNITS_END ])),                          // units
                uint256(bytes32(data[ CTX1_FROM_AMOUNT_START : CTX1_FROM_AMOUNT_END ])),    // fromAmount
                uint32(bytes4(data[ CTX1_BLOCK_NUMBER_START : CTX1_BLOCK_NUMBER_END ]))     // block number
            );
            return;
        }
        // A proper message should never get here. If the message got here, we are never going to be able to properly process it.
        revert InvalidContext(context);
    }

    /**
     * @notice Cross-chain message failure handler
     * @dev Should never revert. (on valid messages)
     */
    function _onPacketFailure(bytes32 destinationIdentifier, bytes calldata data) internal {
        bytes1 context = data[CONTEXT_POS];
        // Since this is a callback, fromVault must be an EVM address.
        address fromVault = address(bytes20(data[ FROM_VAULT_START_EVM : FROM_VAULT_END ]));

        if (context == CTX0_ASSET_SWAP) {
            return ICatalystV1Vault(fromVault).onSendAssetFailure(
                destinationIdentifier,                                                      // connectionId
                data[ TO_ACCOUNT_LENGTH_POS : TO_ACCOUNT_END ],                             // toAccount
                uint256(bytes32(data[ UNITS_START : UNITS_END ])),                          // units
                uint256(bytes32(data[ CTX0_FROM_AMOUNT_START : CTX0_FROM_AMOUNT_END ])),    // fromAmount
                address(bytes20(data[ CTX0_FROM_ASSET_START_EVM : CTX0_FROM_ASSET_END ])),  // fromAsset
                uint32(bytes4(data[ CTX0_BLOCK_NUMBER_START : CTX0_BLOCK_NUMBER_END ]))     // block number
            );
        }
        if (context == CTX1_LIQUIDITY_SWAP) {
            return ICatalystV1Vault(fromVault).onSendLiquidityFailure(
                destinationIdentifier,                                                     // connectionId
                data[ TO_ACCOUNT_LENGTH_POS : TO_ACCOUNT_END ],                             // toAccount
                uint256(bytes32(data[ UNITS_START : UNITS_END ])),                          // units
                uint256(bytes32(data[ CTX1_FROM_AMOUNT_START : CTX1_FROM_AMOUNT_END ])),    // fromAmount
                uint32(bytes4(data[ CTX1_BLOCK_NUMBER_START : CTX1_BLOCK_NUMBER_END ]))     // block number
            );
        }
        // A proper message should never get here. If the message got here, we are never going to be able to properly process it.
        revert InvalidContext(context);
    }

    /**
     * @notice The Acknowledgement package handler
     * @dev Should never revert. (on valid messages)
     * @param destinationIdentifier Identifier for the destination chain
     * @param acknowledgement The acknowledgement bytes for the cross-chain swap.
     */
    function receiveAck(bytes32 destinationIdentifier, bytes32 /* messageIdentifier */, bytes calldata acknowledgement) onlyGARP override external {
        // If the transaction executed but some logic failed, an ack is sent back with an error acknowledgement.
        // We refer to this as "fail on ack". The package should be failed.
        // The acknowledgement is prepended the message, so we need to fetch it.
        // Then, we need to ignore it when passing the data to the handlers.
        bytes1 swapStatus = acknowledgement[0];
        if (swapStatus != 0x00) {
            emit SwapFailed(swapStatus);  // The acknowledgement can be mapped to get some information about what happened.
            return _onPacketFailure(destinationIdentifier, acknowledgement[1:]);
        }
        // Otherwise, swapStatus == 0x00 which implies success.
        _onPacketSuccess(destinationIdentifier, acknowledgement[1:]);
    }

    /**
     * @notice The receive packet handler
     * @param sourceIdentifier Source chain identifier.
     * @param fromApplication The bytes65 encoded fromApplication.
     * @param message The message sent by the source chain.
     * @return acknowledgement The acknowledgement status of the transaction after execution.
     */
    function receiveMessage(bytes32 sourceIdentifier, bytes32 /* messageIdentifier */, bytes calldata fromApplication, bytes calldata message) onlyGARP verifySourceChainAddress(sourceIdentifier, fromApplication) override external returns (bytes memory acknowledgement) {
        bytes1 swapStatus = _receiveMessage(sourceIdentifier, message);

        // We will send the original message back. While this is not ideal from a size perspective, it makes it very easy to manage.
        return acknowledgement = bytes.concat(
            swapStatus,
            message
        );
    }

    /**
     * @notice Message handler
     * @param data The data packet we originally sent.
     * @return acknowledgement The status of the transaction after execution
     */
    function _receiveMessage(bytes32 sourceIdentifier, bytes calldata data) internal virtual returns (bytes1 acknowledgement) {
        bytes1 context = data[CONTEXT_POS];

        // Check that toAccount is the correct length and only contains 0 bytes beyond the address.
        if (uint8(data[TO_ACCOUNT_LENGTH_POS]) != 20) revert InvalidAddress();  // Check correct length
        if (uint256(bytes32(data[TO_ACCOUNT_START:TO_ACCOUNT_START+32])) != 0) revert InvalidAddress();  // Check first 32 bytes are 0.
        if (uint96(bytes12(data[TO_ACCOUNT_START+32:TO_ACCOUNT_START_EVM])) != 0) revert InvalidAddress();  // Check the next 32-20=12 bytes are 0.
        // To vault will not be checked. If it is assumed that any error is random, then an incorrect toVault will result in the call failling.
        // The reason toAccount is checked is that any to account will be treated as valid. So any random error will result
        // in lost funds.

        if (context == CTX0_ASSET_SWAP) {
            return acknowledgement = _handleReceiveAsset(sourceIdentifier, data);
        }
        if (context == CTX1_LIQUIDITY_SWAP) {
            return acknowledgement = _handleReceiveLiquidity(sourceIdentifier, data);
        }
        /* revert InvalidContext(context); */
        // No revert here. Instead, another implementation can override this implementation. It should just keep adding ifs with returns inside:
        // acknowledgement = super._receiveMessage(...)
        // if (acknowledgement == 0x01) { if (context == CTXX) ...}
        return acknowledgement = 0x01;
    }

    function _handleReceiveAssetFallback(bytes32 sourceIdentifier, bytes calldata data) internal returns (bytes1 status) {
        // We don't know how from_vault is encoded. So we load it as bytes. Including the length.
        bytes calldata fromVault = data[ FROM_VAULT_LENGTH_POS : FROM_VAULT_END ];
        // We know that toVault is an EVM address
        address toVault = address(bytes20(data[ TO_VAULT_START_EVM : TO_VAULT_END ]));

        try ICatalystV1Vault(toVault).receiveAsset(
            sourceIdentifier,                                                           // connectionId
            fromVault,                                                                  // fromVault
            uint8(data[CTX0_TO_ASSET_INDEX_POS]),                                       // toAssetIndex
            address(bytes20(data[ TO_ACCOUNT_START_EVM : TO_ACCOUNT_END ])),            // toAccount
            uint256(bytes32(data[ UNITS_START : UNITS_END ])),                          // units
            uint256(bytes32(data[ CTX0_MIN_OUT_START : CTX0_MIN_OUT_END ])),            // minOut
            uint256(bytes32(data[ CTX0_FROM_AMOUNT_START : CTX0_FROM_AMOUNT_END ])),    // fromAmount
            bytes(data[ CTX0_FROM_ASSET_LENGTH_POS : CTX0_FROM_ASSET_END ]),            // fromAsset
            uint32(bytes4(data[ CTX0_BLOCK_NUMBER_START : CTX0_BLOCK_NUMBER_END ]))     // blocknumber
        ) returns(uint256 purchasedTokens) {
            uint16 dataLength = uint16(bytes2(data[CTX0_DATA_LENGTH_START : CTX0_DATA_LENGTH_END]));
            if (dataLength != 0) {
                address dataTarget = address(bytes20(data[ CTX0_DATA_START : CTX0_DATA_START+20 ]));
                bytes calldata dataArguments = data[ CTX0_DATA_START+20 : CTX0_DATA_START+dataLength ];
                
                // Let users define custom logic which should be executed after the swap.
                // The logic is not contained within a try - except so if the logic reverts
                // the transaction will timeout and the user gets the input tokens on the sending chain.
                // If this is not desired, wrap further logic in a try - except at dataTarget.
                ICatalystReceiver(dataTarget).onCatalystCall(purchasedTokens, dataArguments, false);
                // If dataTarget doesn't implement onCatalystCall BUT implements a fallback function, the call will still succeed.
            }
            return 0x00;
        } catch (bytes memory err) {
            return _handleError(err);
        }
    }

    function _handleReceiveLiquidity(bytes32 sourceIdentifier, bytes calldata data) internal returns (bytes1 status) {
        // We don't know how from_vault is encoded. So we load it as bytes. Including the length.
        bytes calldata fromVault = data[ FROM_VAULT_LENGTH_POS : FROM_VAULT_END ];
        // We know that toVault is an EVM address
        address toVault = address(bytes20(data[ TO_VAULT_START_EVM : TO_VAULT_END ]));

        try ICatalystV1Vault(toVault).receiveLiquidity(
            sourceIdentifier,                                                           // connectionId
            fromVault,                                                                  // fromVault
            address(bytes20(data[ TO_ACCOUNT_START_EVM : TO_ACCOUNT_END ])),            // toAccount
            uint256(bytes32(data[ UNITS_START : UNITS_END ])),                          // units
            uint256(bytes32(data[ CTX1_MIN_VAULT_TOKEN_START : CTX1_MIN_VAULT_TOKEN_END ])), // minOut
            uint256(bytes32(data[ CTX1_MIN_REFERENCE_START : CTX1_MIN_REFERENCE_END ])),// minOut
            uint256(bytes32(data[ CTX1_FROM_AMOUNT_START : CTX1_FROM_AMOUNT_END ])),    // fromAmount
            uint32(bytes4(data[ CTX1_BLOCK_NUMBER_START : CTX1_BLOCK_NUMBER_END ]))     // blocknumber
        ) returns (uint256 purchasedVaultTokens) {
            uint16 dataLength = uint16(bytes2(data[CTX1_DATA_LENGTH_START : CTX1_DATA_LENGTH_END]));
            if (dataLength != 0) {
                address dataTarget = address(bytes20(data[ CTX1_DATA_START : CTX1_DATA_START+20 ]));
                bytes calldata dataArguments = data[ CTX1_DATA_START+20 : CTX1_DATA_START+dataLength ];
                
                // Let users define custom logic which should be executed after the swap.
                // The logic is not contained within a try - except so if the logic reverts
                // the transaction will timeout and the user gets the input tokens on the sending chain.
                // If this is not desired, wrap further logic in a try - except at dataTarget.
                ICatalystReceiver(dataTarget).onCatalystCall(purchasedVaultTokens, dataArguments, false);
                // If dataTarget doesn't implement onCatalystCall BUT implements a fallback function, the call will still succeed.
            }
            return 0x00;
        } catch (bytes memory err) {
            return _handleError(err);
        }
    }

    //--- Underwriting ---//
    // The following section contains the underwriting module of Catalyst.
    // It serves to speedup swap execution by letting a thirdparty take on the confirmation / delivery risk
    // by pre-executing the latter part and then reserving the swap result in the escrow.

    /**
     * @notice Computes the underwriting identifier for a Catalyst swap.
     */
    function _getUnderwriteIdentifier(
        address targetVault,
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes calldata cdata
    ) internal pure returns (bytes32 identifier) {
        return identifier = keccak256(
            bytes.concat(
                bytes20(targetVault),
                bytes20(toAsset),
                bytes32(U),
                bytes32(minOut),
                bytes20(toAccount),
                bytes2(underwriteIncentiveX16),
                cdata
            )
        );
    }

    /**
     * @notice Computes the underwriting identifier for a Catalyst swap.
     */
    function getUnderwriteIdentifier(
        address targetVault,
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes calldata cdata
    ) override external pure returns (bytes32 identifier) {
        return identifier = _getUnderwriteIdentifier(
            targetVault,
            toAsset,
            U,
            minOut,
            toAccount,
            underwriteIncentiveX16,
            cdata
        );
    }

    /**
     * @notice Underwrites a swap and check if there is a connection. 
     * @dev sourceIdentifier and fromVault are not verified that they belong to the message.
     * As a result, care should be placed on correctly reading these.
     */
    function underwriteAndCheckConnection(
        bytes32 sourceIdentifier,
        bytes calldata fromVault, // -- Conection Check
        address targetVault,  // -- Swap information
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes calldata cdata
    ) override external {
        if (!ICatalystV1Vault(targetVault)._vaultConnection(sourceIdentifier, fromVault)) revert NoVaultConnection();

        underwrite(
            targetVault,
            toAsset,
            U,
            minOut,
            toAccount,
            underwriteIncentiveX16,
            cdata
        );
    }

    /**
     * @notice Underwrite a swap.
     * There are a few important properties that underwriters should be aware of:
     * 1. There is nothing that checks if the connection is valid on this call. This check should be done seperately.
     *      Even if a swap is executed where it appears that it is possible to execute the swap,
     *      these values should be read and a connection should be checked. (via this function).
     *      As you can underwrite the swap (where there is no connection) but when the swap arrives it
     *      does not fill the underwrite but instead fails on ack and releases the input on the source chain.
     *      You can use the similar function underwriteAndCheckConnection to also check the connection.
     *
     * 2. You are underwriting the specific instance of the transaction NOT the inclusion of the transaction.
     *      What this means for you, is that if the block of the transaction is lost/abandon/re-entered, then
     *      the underwriting will not be noted unless the transaction is re-executed EXACTLY as it was before.
     *      The most important parameter is U which is volatile and any change to the vault balances on the source chain
     *      will cause U to be different.
     *      In other words, if that transaction is re-executed before or after another swap and it wasn't the case before,
     *      then it won't fill the underwrite anymore and either be exeucted as an ordinary swap (to the user) or fail
     *      on ack and release the original funds back to the user.
     *
     * 3. The execution of the underwrite is dependent on the correct execution of both
     *      the minout but also the additional logic. If either fails, then the swap is not
     *      underwritable. To avoid wasting gas, it is important that the underwrite is simulated before executed.
     */
    function underwrite(
        address targetVault,  // -- Swap information
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes calldata cdata
    ) public returns(bytes32 identifier) {

        // Get the swap identifier.
        // For any incoming swap, the swap will have a semi-unique identifier, which can be derived solely based on the
        // swap parameters. Assuming that the swap has already been executed, these arguments are constant and known.
        // As a result, once the swap arrives (and the underwriter is competent), the same identifier will be computed and matched.
        // For other implementations: The arguments the identifier is based on should be the same but the hashing
        // algorithm doesn't matter. It is only used and computed on the destination chain.
        identifier = _getUnderwriteIdentifier(
            targetVault,
            toAsset,
            U,
            minOut,
            toAccount,
            underwriteIncentiveX16,
            cdata
        );
        // Observation: A swapper can execute multiple swaps which return the same U. 
        // In these cases, it would not be possible to underwrite the second swap (or further swaps) 
        // until after the first swap has arrived. This can be counteracted by either:
        // 1. Changing U. The tail of U is **volatile**. As a result, to get overlapping identifiers,
        // it would have to be deliberatly.
        // 2. Add random noise to minOut or underwriteIncentiveX16. For tokens with 18 decimals, this noise can
        // be as small as 1e-12 then the chance that 2 swaps collide would be 1 in a million. (literally 1/(1e(18-12)) = 1/1e6)
        // 3. Add either a counter or noise to cdata.

        // For most implementations, the observation can be ignored because of the strength of point 1.

        // Check if the associated underwrite just arrived and has already been matched.
        // This is an issue when the swap was JUST underwriten, JUST arrived (and matched), AND someone else JUST underwrote the swap.
        // To give underwriters a bit more protection, we add a buffer of size `BUFFER_BLOCKS`.
        // Error SwapAlreadyUnderwritten vs Error SwapRecentlyUnderwritten: It is very likely that this block is triggered not because a swap was
        // fulfilled but because it has already been underwritten. 
        // That is because (lastTouchBlock + BUFFER_BLOCKS >= uint96(block.number)) WILL ALWAYS be true when a swap has been underwritten, thus  
        // SwapRecentlyUnderwritten will be the error. You might have expected the error "SwapAlreadyUnderwritten". However, we never get there so
        // it cannot emit. We also cannot move that check up here, since then an external call would be made between a state check and a
        // state modification (reentry). As a result, SwapRecentlyUnderwritten will be emitted when a swap has already been underwritten EXCEPT
        // when underwriting a swap through reentry. Then the reentry will underwrite the swap and the main call will fail with SwapAlreadyUnderwritten.
        UnderwritingStorage storage underwriteState = underwritingStorage[identifier];
        unchecked {
            // Get the last touch block. For most underwrites it is going to be 0.
            uint96 lastTouchBlock = underwriteState.expiry;
            if (lastTouchBlock != 0) { // implies that the swap has never been underwritten.
                // if lastTouchBlock > type(uint96).max + BUFFER_BLOCKS then lastTouchBlock + BUFFER_BLOCKS overflows.
                // if lastTouchBlock < BUFFER_BLOCKS then lastTouchBlock - BUFFER_BLOCKS underflows.
                if ((lastTouchBlock <  type(uint96).max - BUFFER_BLOCKS) && (lastTouchBlock > BUFFER_BLOCKS)) {
                    // Add a reasonable buffer so if the transaction got into the memory pool and is delayed into the next blocks
                    // it doesn't underwrite a non-existing swap.
                    if (lastTouchBlock + BUFFER_BLOCKS >= uint96(block.number)) {
                        // Check that uint96(block.number) hasn't overflowed and this is an old reference. We don't care about the underflow
                        // as that will always return false.
                        if (lastTouchBlock - BUFFER_BLOCKS <= uint96(block.number)) revert SwapRecentlyUnderwritten();
                    }
                } else {
                    if (lastTouchBlock == uint96(block.number)) revert SwapRecentlyUnderwritten();
                }
            }
        }

        // Get the number of purchased units from the vault. 
        // This uses a custom call that doesn't return any assets. The cal escrows purchasedTokens on the vault.
        // Importantly! The connection is not checked here. Instead it is checked when the
        // message arrives. As a result, the underwriter should verify that a message is good.
        uint256 purchasedTokens = ICatalystV1Vault(targetVault).underwriteAsset(
            identifier,
            toAsset,
            U,
            minOut * (1 << 16) / ((1 << 16) - uint256(underwriteIncentiveX16))  // minout is checked after underwrite fee.
        );

        // The following number of lines act as re-entry protection. Do not add any external call inbetween these lines.

        // Ensure the swap hasn't already been underwritten by checking if refundTo is set. 
        // This line is very unlikely to ever get emitted. Instead, read the comment about SwapRecentlyUnderwritten.
        if (underwriteState.refundTo != address(0)) revert SwapAlreadyUnderwritten();

        uint96 underwriteExpiry = uint96(uint256(block.number) + uint256(maxUnderwritingDuration)); // Should never overflow.

        // Save the underwriting state.
        underwriteState.tokens = purchasedTokens;
        underwriteState.refundTo = msg.sender;
        underwriteState.expiry = underwriteExpiry;

        // The above combination of lines act as local re-entry protection. Do not add any external call inbetween these lines.

        // Collect tokens and collateral from underwriter.
        // We still collect the tokens used to incentivise the underwriter as otherwise they could freely reserve liquidity
        // in the vaults. Vaults would essentially be a free source of short term options which isn't wanted.
        SafeTransferLib.safeTransferFrom(
            toAsset,
            msg.sender, 
            address(this),
            purchasedTokens * (
                UNDERWRITING_COLLATERAL_DENOMINATOR+UNDERWRITING_COLLATERAL
            )/UNDERWRITING_COLLATERAL_DENOMINATOR
        );

        uint256 underwritingIncentive = (purchasedTokens * uint256(underwriteIncentiveX16)) >> 16;
        // Subtract the underwrite incentive from the funds sent to the user.
        unchecked {
            // underwritingIncentive <= purchasedTokens.
            purchasedTokens -= underwritingIncentive;
        }

        // Send the assets to the user.
        SafeTransferLib.safeTransfer(toAsset,toAccount, purchasedTokens);

        // Figure out if the user wants to execute additional logic.
        // Note that this logic is not contained within a try catch. It could fail.
        // An underwrite should simulate the tx execution before submitting the transaction as otherwise
        // they could be out the associated gas.
        uint16 calldataLength = uint16(bytes2(cdata[0:2]));
        if (calldataLength != 0) {
            address dataTarget = address(bytes20(cdata[2:2+20]));
            bytes calldata customCalldata = cdata[2+20:2+calldataLength];
            ICatalystReceiver(dataTarget).onCatalystCall(purchasedTokens, customCalldata, true);
        }
        
        emit SwapUnderwritten(
            identifier,
            msg.sender,
            underwriteExpiry,
            targetVault,
            toAsset,
            U,
            toAccount,
            purchasedTokens
        );
    }

    /**
     * @notice Resolves unexpired underwrites so that the escrowed value can be freed.
     * @dev The underwrite owner can expire it at any time. Other callers needs to wait until after expiry. 
     */
    function expireUnderwrite(
        address targetVault,
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes calldata cdata
    ) override external {
        bytes32 identifier = _getUnderwriteIdentifier(
            targetVault,
            toAsset,
            U,
            minOut,
            toAccount,
            underwriteIncentiveX16,
            cdata
        );

        UnderwritingStorage storage underwriteState = underwritingStorage[identifier];
        // Check that the refundTo address is set. (Indicates that the underwrite exists.)
        address refundAddress = underwriteState.refundTo;
        if (refundAddress == address(0)) revert UnderwriteDoesNotExist(identifier);
        
        // Check that the underwriting can be expired. If the msg.sender is the refundTo address, then it can be expired at any time.
        // This lets the underwriter reclaim _some_ of the collateral they provided if they change their mind or observed an issue.
        // Load the associated storage slot.
        uint256 underWrittenTokens = underwriteState.tokens;
        uint256 expiryTime = uint256(underwriteState.expiry);
        if (msg.sender != refundAddress) {
            // Otherwise, the expiry time must have been passed.
            if (underwriteState.expiry > block.number) revert UnderwriteNotExpired(expiryTime - block.number);
        }
        // The next line acts as reentry protection. When the storage is deleted refundAddress == address(0) will be true.
        delete underwritingStorage[identifier];

        // Delete the escrow
        ICatalystV1Vault(targetVault).deleteUnderwriteAsset(identifier, U, underWrittenTokens, toAsset);

        unchecked {
            // Compute the underwriting incentive. 
            // Notice the parts that we only have: incentive + collateral to work with
            // The incentive was never sent to the user, neither was the underwriting incentive.
            uint256 underwritingIncentive = (underWrittenTokens * uint256(underwriteIncentiveX16)) >> 16; 
            // This computation has been done before.

            // Get the collateral.
            // A larger computation has already been done when the swap was initially underwritten.
            uint256 refundAmount = underWrittenTokens * (
                UNDERWRITING_COLLATERAL
            )/UNDERWRITING_COLLATERAL_DENOMINATOR + underwritingIncentive;
            // collateral + underwritingIncentive must be less than the full amount.

            // Send the coded shares of the collateral to the expirer the rest to the vault.
        
            // This following logic might overflow but we would rather have it overflow (which reduces expireShare)
            // than to never be able to expire an underwrite.
            uint256 expireShare = refundAmount * EXPIRE_CALLER_REWARD / EXPIRE_CALLER_REWARD_DENOMINATOR;
            SafeTransferLib.safeTransfer(toAsset, msg.sender, expireShare);
            // refundAmount > expireShare, and specially when expireShare overflows.
            uint256 vaultShare = refundAmount - expireShare;
            SafeTransferLib.safeTransfer(toAsset, targetVault, vaultShare);

            emit ExpireUnderwrite(
                identifier,
                msg.sender,
                expireShare
            );
        }
        // The underwriting storage has already been deleted.
    }

    /** @dev It is important that any call to this functions has pre-checked the vault connection. */
    function _matchUnderwrite(
        bytes32 identifier,
        address toAsset,
        address vault,
        bytes32 sourceIdentifier,
        bytes calldata fromVault,
        uint16 underwriteIncentiveX16
    ) internal returns (bool swapUnderwritten) {
        UnderwritingStorage storage underwriteState = underwritingStorage[identifier];
        // Load number of tokens from storage.
        uint256 underwrittenTokenAmount = underwriteState.tokens;
        // Check if the swap was underwritten => refundTo != address(0)
        address refundTo = underwriteState.refundTo;
        // if refundTo == address(0) then the swap hasn't been underwritten.
        if (refundTo == address(0)) return swapUnderwritten = false;

        // Reentry protection. No external calls are allowed before this line. The line 'if (refundTo == address(0)) ...' will always be true.
        delete underwritingStorage[identifier];
        // Set the last touch block so someone doesn't underwrite this swap again.
        underwritingStorage[identifier].expiry = uint96(block.number);

        // Delete escrow information and send swap tokens directly to the underwriter.
        ICatalystV1Vault(vault).releaseUnderwriteAsset(refundTo, identifier, underwrittenTokenAmount, toAsset, sourceIdentifier, fromVault);

        // We now only need to handle the collateral and underwriting incentive.
        // We don't have to check that the vault didn't lie to us about underwriting.

        // Also refund the collateral.
        uint256 refundAmount = underwrittenTokenAmount * (
            UNDERWRITING_COLLATERAL
        )/UNDERWRITING_COLLATERAL_DENOMINATOR;

        // add the underwriting incentive as well. Notice that 2x refundAmount are in play.
        //   1. The first part comes from the underwriter + collateral.
        // + 1. The second part comes from the vault after the matching message arrives.
        // = 2 parts
        // 1 - incentive has been sent to the user.
        // That leaves us with part (1 - (1 - incentive)) + (1) = incentive + 1
        uint256 underwritingIncentive = (underwrittenTokenAmount * uint256(underwriteIncentiveX16)) >> 16;
        refundAmount += underwritingIncentive;

        SafeTransferLib.safeTransfer(toAsset, refundTo, refundAmount);

        emit FulfillUnderwrite(
            identifier
        );

        return swapUnderwritten = true;
    }

    function _handleReceiveAsset(bytes32 sourceIdentifier, bytes calldata data) internal returns (bytes1 acknowledgement) {
        // We don't know how from_vault is encoded. So we load it as bytes. Including the length.
        bytes calldata fromVault = data[ FROM_VAULT_LENGTH_POS : FROM_VAULT_END ];
        // We know that toVault is an EVM address
        address toVault = address(bytes20(data[ TO_VAULT_START_EVM : TO_VAULT_END ]));

        // Select excess calldata. Excess calldata is not decoded.
        bytes calldata cdata = data[CTX0_DATA_LENGTH_START:];

        // Get the toAsset
        uint8 toAssetIndex = uint8(data[CTX0_TO_ASSET_INDEX_POS]);
        address toAsset = ICatalystV1Vault(toVault)._tokenIndexing(toAssetIndex);

        // Get the rest of the swap parameters.
        address toAccount = address(bytes20(data[ TO_ACCOUNT_START_EVM : TO_ACCOUNT_END ]));
        uint256 U = uint256(bytes32(data[ UNITS_START : UNITS_END ]));
        uint256 minOut = uint256(bytes32(data[ CTX0_MIN_OUT_START : CTX0_MIN_OUT_END ]));
        uint16 underwriteIncentiveX16 = uint16(bytes2(data[CTX0_UW_INCENTIVE_START:CTX0_UW_INCENTIVE_END]));

        // Get the underwriting identifier.
        bytes32 identifier = _getUnderwriteIdentifier(
            toVault,
            toAsset,
            U,
            minOut,
            toAccount,
            underwriteIncentiveX16,
            cdata
        );

        // Check if the swap has been underwritten. If it has, return funds to underwriter.
        bool swapUnderwritten = _matchUnderwrite(
            identifier,
            toAsset,
            toVault,
            sourceIdentifier,
            fromVault,
            underwriteIncentiveX16
        );

        if (!swapUnderwritten) {
            // The swap hasn't been underwritten lets execute the swap properly.
            return acknowledgement = _handleReceiveAssetFallback(sourceIdentifier, data);
        }
        // There is no case where only a subset of the units are filled. As either the complete swap (through the swap identifier)
        // is underwritten or it wasn't underwritten.

        return acknowledgement = 0x00;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// - The ERC20 standard allows minting and transferring to and from the zero address,
///   minting and transferring zero tokens, as well as self-approvals.
///   For performance, this implementation WILL NOT revert for such actions.
///   Please add any checks with overrides if desired.
/// - The `permit` function uses the ecrecover precompile (0x1).
///
/// If you are overriding:
/// - NEVER violate the ERC20 invariant:
///   the total sum of all balances must be equal to `totalSupply()`.
/// - Check that the overridden function is actually used in the function you want to
///   change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC20 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The total supply has overflowed.
    error TotalSupplyOverflow();

    /// @dev The allowance has overflowed.
    error AllowanceOverflow();

    /// @dev The allowance has underflowed.
    error AllowanceUnderflow();

    /// @dev Insufficient balance.
    error InsufficientBalance();

    /// @dev Insufficient allowance.
    error InsufficientAllowance();

    /// @dev The permit is invalid.
    error InvalidPermit();

    /// @dev The permit has expired.
    error PermitExpired();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
    event Transfer(address indexed from, address indexed to, uint256 amount);

    /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
    uint256 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
    uint256 private constant _APPROVAL_EVENT_SIGNATURE =
        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The storage slot for the total supply.
    uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;

    /// @dev The balance slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _BALANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let balanceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;

    /// @dev The allowance slot of (`owner`, `spender`) is given by:
    /// ```
    ///     mstore(0x20, spender)
    ///     mstore(0x0c, _ALLOWANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let allowanceSlot := keccak256(0x0c, 0x34)
    /// ```
    uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;

    /// @dev The nonce slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _NONCES_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let nonceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _NONCES_SLOT_SEED = 0x38377508;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
    uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
    bytes32 private constant _DOMAIN_TYPEHASH =
        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    /// @dev `keccak256("1")`.
    bytes32 private constant _VERSION_HASH =
        0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;

    /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
    bytes32 private constant _PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ERC20 METADATA                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the name of the token.
    function name() public view virtual returns (string memory);

    /// @dev Returns the symbol of the token.
    function symbol() public view virtual returns (string memory);

    /// @dev Returns the decimals places of the token.
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           ERC20                            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the amount of tokens in existence.
    function totalSupply() public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_TOTAL_SUPPLY_SLOT)
        }
    }

    /// @dev Returns the amount of tokens owned by `owner`.
    function balanceOf(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
    function allowance(address owner, address spender)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x34))
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
    ///
    /// Emits a {Approval} event.
    function approve(address spender, uint256 amount) public virtual returns (bool) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
        }
        return true;
    }

    /// @dev Transfer `amount` tokens from the caller to `to`.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    ///
    /// Emits a {Transfer} event.
    function transfer(address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(msg.sender, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, caller())
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(msg.sender, to, amount);
        return true;
    }

    /// @dev Transfers `amount` tokens from `from` to `to`.
    ///
    /// Note: Does not update the allowance if it is the maximum uint256 value.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(from, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            // Compute the allowance slot and load its value.
            mstore(0x20, caller())
            mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
            let allowanceSlot := keccak256(0x0c, 0x34)
            let allowance_ := sload(allowanceSlot)
            // If the allowance is not the maximum uint256 value.
            if add(allowance_, 1) {
                // Revert if the amount to be transferred exceeds the allowance.
                if gt(amount, allowance_) {
                    mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated allowance.
                sstore(allowanceSlot, sub(allowance_, amount))
            }
            // Compute the balance slot and load its value.
            mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(from, to, amount);
        return true;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          EIP-2612                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev For more performance, override to return the constant value
    /// of `keccak256(bytes(name()))` if `name()` will never change.
    function _constantNameHash() internal view virtual returns (bytes32 result) {}

    /// @dev Returns the current nonce for `owner`.
    /// This value is used to compute the signature for EIP-2612 permit.
    function nonces(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the nonce slot and load its value.
            mstore(0x0c, _NONCES_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
    /// authorized by a signed approval by `owner`.
    ///
    /// Emits a {Approval} event.
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        /// @solidity memory-safe-assembly
        assembly {
            // Revert if the block timestamp is greater than `deadline`.
            if gt(timestamp(), deadline) {
                mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
                revert(0x1c, 0x04)
            }
            let m := mload(0x40) // Grab the free memory pointer.
            // Clean the upper 96 bits.
            owner := shr(96, shl(96, owner))
            spender := shr(96, shl(96, spender))
            // Compute the nonce slot and load its value.
            mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
            mstore(0x00, owner)
            let nonceSlot := keccak256(0x0c, 0x20)
            let nonceValue := sload(nonceSlot)
            // Prepare the domain separator.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), _VERSION_HASH)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            mstore(0x2e, keccak256(m, 0xa0))
            // Prepare the struct hash.
            mstore(m, _PERMIT_TYPEHASH)
            mstore(add(m, 0x20), owner)
            mstore(add(m, 0x40), spender)
            mstore(add(m, 0x60), value)
            mstore(add(m, 0x80), nonceValue)
            mstore(add(m, 0xa0), deadline)
            mstore(0x4e, keccak256(m, 0xc0))
            // Prepare the ecrecover calldata.
            mstore(0x00, keccak256(0x2c, 0x42))
            mstore(0x20, and(0xff, v))
            mstore(0x40, r)
            mstore(0x60, s)
            let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20)
            // If the ecrecover fails, the returndatasize will be 0x00,
            // `owner` will be checked if it equals the hash at 0x00,
            // which evaluates to false (i.e. 0), and we will revert.
            // If the ecrecover succeeds, the returndatasize will be 0x20,
            // `owner` will be compared against the returned address at 0x20.
            if iszero(eq(mload(returndatasize()), owner)) {
                mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
                revert(0x1c, 0x04)
            }
            // Increment and store the updated nonce.
            sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
            // Compute the allowance slot and store the value.
            // The `owner` is already at slot 0x20.
            mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
            sstore(keccak256(0x2c, 0x34), value)
            // Emit the {Approval} event.
            log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
    function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Grab the free memory pointer.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), _VERSION_HASH)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            result := keccak256(m, 0xa0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL MINT FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Mints `amount` tokens to `to`, increasing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _mint(address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(address(0), to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
            let totalSupplyAfter := add(totalSupplyBefore, amount)
            // Revert if the total supply overflows.
            if lt(totalSupplyAfter, totalSupplyBefore) {
                mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
                revert(0x1c, 0x04)
            }
            // Store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(address(0), to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL BURN FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Burns `amount` tokens from `from`, reducing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _burn(address from, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, address(0), amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, from)
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Subtract and store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
            // Emit the {Transfer} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
        }
        _afterTokenTransfer(from, address(0), amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Moves `amount` of tokens from `from` to `to`.
    function _transfer(address from, address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            // Compute the balance slot and load its value.
            mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(from, to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL ALLOWANCE FUNCTIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and load its value.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            let allowanceSlot := keccak256(0x0c, 0x34)
            let allowance_ := sload(allowanceSlot)
            // If the allowance is not the maximum uint256 value.
            if add(allowance_, 1) {
                // Revert if the amount to be transferred exceeds the allowance.
                if gt(amount, allowance_) {
                    mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated allowance.
                sstore(allowanceSlot, sub(allowance_, amount))
            }
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
    ///
    /// Emits a {Approval} event.
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let owner_ := shl(96, owner)
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HOOKS TO OVERRIDE                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Hook that is called before any transfer of tokens.
    /// This includes minting and burning.
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /// @dev Hook that is called after any transfer of tokens.
    /// This includes minting and burning.
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
///   responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for
    /// the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                mstore(0x34, amount) // Store back the original `amount`.
                // Retry the approval, reverting upon failure.
                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul(
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

File 5 of 23 : IMessageEscrowStructs.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

interface IMessageEscrowStructs {
    struct IncentiveDescription {
        uint48 maxGasDelivery;      // 0: 6/32 bytes
        uint48 maxGasAck;           // 0: 12/32 bytes
        address refundGasTo;        // 0: 32/32 bytes
        uint96 priceOfDeliveryGas;  // 1: 12/32 bytes
        uint96 priceOfAckGas;       // 1: 24/32 bytes
        uint64 targetDelta;         // 1: 32/32 bytes
    }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import { IMessageEscrowStructs } from "./IMessageEscrowStructs.sol";
import { IMessageEscrowErrors } from "./IMessageEscrowErrors.sol";
import { IMessageEscrowEvents } from "./IMessageEscrowEvents.sol";

interface IIncentivizedMessageEscrow is IMessageEscrowStructs, IMessageEscrowErrors, IMessageEscrowEvents {
   function bounty(address fromApplication, bytes32 destinationIdentifier, bytes32 messageIdentifier) external view returns(IncentiveDescription memory incentive);

   function messageDelivered(bytes32 sourceIdentifier, bytes calldata sourceImplementationIdentifier, bytes32 messageIdentifier) external view returns(bytes32 hasMessageBeenExecuted);

    function increaseBounty(
        address fromApplication,
        bytes32 destinationIdentifier,
        bytes32 messageIdentifier,
        uint96 priceOfDeliveryGas,
        uint96 priceOfAckGas
    ) external payable;

    function submitMessage(
        bytes32 destinationIdentifier,
        bytes calldata destinationAddress,
        bytes calldata message,
        IncentiveDescription calldata incentive,
        uint64 deadline
    ) external payable returns(uint256 gasRefund, bytes32 messageIdentifier);

    function processPacket(bytes calldata messagingProtocolContext, bytes calldata message, bytes32 feeRecipient) payable external;

    function setRemoteImplementation(bytes32 chainIdentifier, bytes calldata implementation) external;

    /**
     * @notice Estimates the additional cost to the messaging router to validate the message
     * @return asset The asset the token is in. If native token, returns address(0);
     * @return amount The number of assets to pay.
     */
    function estimateAdditionalCost() external view returns(address asset, uint256 amount);

    function timeoutMessage(
        bytes32 sourceIdentifier,
        bytes calldata implementationIdentifier,
        uint256 originBlockNumber,
        bytes calldata message
    ) external payable;

    function reemitAckMessage(
        bytes32 sourceIdentifier,
        bytes calldata implementationIdentifier,
        bytes calldata receiveAckWithContext
    ) external payable;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

contract Bytes65 {
    error InvalidBytes65Address();

    function _checkBytes65(bytes calldata supposedlyBytes65) internal pure returns(bool) {
        return supposedlyBytes65.length == 65;
    }

    modifier checkBytes65Address(bytes calldata supposedlyBytes65) {
        if (!_checkBytes65(supposedlyBytes65)) revert InvalidBytes65Address();
        _;
    }

    function convertEVMTo65(address evmAddress) public pure returns(bytes memory) {
        return bytes.concat(
            bytes1(uint8(20)),                      // Size of address. Is always 20 for EVM
            bytes32(0),                             // First 32 bytes on EVM are 0
            bytes32(uint256(uint160((evmAddress)))) // Encode the address in bytes32.
        );
    }

    function thisBytes65() public view returns(bytes memory) {
        return convertEVMTo65(address(this));
    }
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { ICatalystV1Structs } from "./ICatalystV1VaultState.sol";
import { ICrossChainReceiver } from "GeneralisedIncentives/src/interfaces/ICrossChainReceiver.sol";

// Autogenerated
interface ICatalystChainInterface is ICatalystV1Structs, ICrossChainReceiver {

    function EXPIRE_CALLER_REWARD() external view returns (uint256);
    function EXPIRE_CALLER_REWARD_DENOMINATOR() external view returns (uint256);
    function UNDERWRITING_COLLATERAL() external view returns (uint256);
    function UNDERWRITING_COLLATERAL_DENOMINATOR() external view returns (uint256);
    function chainIdentifierToDestinationAddress(bytes32) external view returns (bytes memory);
    function connectNewChain(bytes32 chainIdentifier, bytes memory remoteCCI, bytes memory remoteGARP) external;
    function estimateAdditionalCost() external view returns (address asset, uint256 amount);
    function expireUnderwrite(
        address targetVault,
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes memory cdata
    ) external;
    function getUnderwriteIdentifier(
        address targetVault,
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes memory cdata
    ) external pure returns (bytes32 identifier);
    function maxUnderwritingDuration() external view returns (uint256);
    function minGasFor(bytes32) external view returns (uint48);
    function sendCrossChainAsset(
        RouteDescription memory routeDescription,
        uint8 toAssetIndex,
        uint256 U,
        uint256 minOut,
        uint256 fromAmount,
        address fromAsset,
        uint16 underwriteIncentiveX16,
        bytes memory calldata_
    ) external payable;
    function sendCrossChainLiquidity(
        RouteDescription memory routeDescription,
        uint256 U,
        uint256[2] memory minOut,
        uint256 fromAmount,
        bytes memory calldata_
    ) external payable;
    function setMaxUnderwritingDuration(uint256 newMaxUnderwriteDuration) external;
    function setMinGasFor(bytes32 chainIdentifier, uint48 minGas) external;
    function underwrite(
        address targetVault,
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes memory cdata
    ) external returns (bytes32 identifier);
    function underwriteAndCheckConnection(
        bytes32 sourceIdentifier,
        bytes memory fromVault,
        address targetVault,
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes memory cdata
    ) external;
    function underwritingStorage(bytes32) external view returns (uint256 tokens, address refundTo, uint96 expiry);
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

interface ICatalystReceiver {
    /** 
     * @notice The callback from a catalyst call. To determine if the swap was an asset or liquidity swap, either the current balance should be checked or it should be encoded into data.
     * @dev If you want full finality (not just economical finality)
     * revert on underwritten == true.
     */
    function onCatalystCall(uint256 purchasedTokens, bytes calldata data, bool underwritten) external;
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IMessageEscrowStructs } from "GeneralisedIncentives/src/interfaces/IMessageEscrowStructs.sol";

interface ICatalystV1Structs is IMessageEscrowStructs {
    /**
     * @param chainIdentifier target chain identifier.
     * @param toVault Target vault on the target chain. Encoded in 64 + 1 bytes.
     * @param toAccount Recipient of the transaction on the target chain. Encoded in 64 + 1 bytes.
     * @param incentive Cross-chain relaying incentive description.
     */
    struct RouteDescription {
        bytes32 chainIdentifier;
        bytes toVault;
        bytes toAccount;
        IncentiveDescription incentive;
        uint64 deadline;
    }
}

/**
 * @title Vault state
 * @notice Contains all vault storage which depends on the vault state.
 */
interface ICatalystV1VaultState {
    /** @notice Token weights. Used for maintaining a non symmetric vault asset balance. */
    function _weight(address token) external view returns (uint256);

    function _adjustmentTarget() external view returns (uint48);

    function _lastModificationTime() external view returns (uint48);

    /** @notice The vault fee in WAD. Implementation of fee: mulWadDown(_amount, _vaultFee) */
    function _vaultFee() external view returns (uint64);

    function _governanceFeeShare() external view returns (uint64);

    /** @notice The address of the responsible for adjusting the fees. */
    function _feeAdministrator() external view returns (address);

    /** @notice The setupMaster is the short-term owner of the vault. They can connect the vault to vaults on other chains. */
    function _setupMaster() external view returns (address);

    // Security limit
    /** @notice The max incoming liquidity flow from the router. */
    function _maxUnitCapacity() external view returns (uint256);

    // Escrow reference
    /** @notice Total current escrowed tokens */
    function _escrowedTokens(address token) external view returns (uint256);

    /** @notice Find escrow information. Used for both normal swaps and liquidity swaps. */
    function _escrowLookup(bytes32 sendAssetHash) external view returns (address);

    /** @notice Total current escrowed vault tokens */
    function _escrowedVaultTokens() external view returns (uint256);

    /** @notice Checks if there is a connection to the described vault */
    function _vaultConnection(bytes32 sourceIdentifier, bytes calldata fromVault) external view returns (bool);

    function factoryOwner() external view returns (address);

    function governanceFeeDestination() external view returns (address);

    /**
     * @notice External view function purely used to signal if a vault is safe to use.
     * @dev Just checks if the setup master has been set to ZERO_ADDRESS. In other words, has finishSetup been called?
     */
    function ready() external view returns (bool);
}

File 11 of 23 : ICatalystV1Vault.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;

import "./interfaces/ICatalystV1VaultSuccessFailure.sol";
import "./interfaces/ICatalystV1VaultAdministration.sol";
import "./interfaces/ICatalystV1VaultDerived.sol";
import "./interfaces/ICatalystV1VaultErrors.sol";
import "./interfaces/ICatalystV1VaultEvents.sol";
import "./interfaces/ICatalystV1VaultImmutables.sol";
import "./interfaces/ICatalystV1VaultPermissionless.sol";
import "./interfaces/ICatalystV1VaultState.sol";
import "./interfaces/ICatalystV1Underwriting.sol";

interface ICatalystV1Vault is
    ICatalystV1VaultSuccessFailure,
    ICatalystV1VaultAdministration,
    ICatalystV1VaultDerived,
    ICatalystV1VaultEvents,
    ICatalystV1VaultImmutables,
    ICatalystV1VaultPermissionless,
    ICatalystV1VaultState,
    ICatalystV1Structs,
    ICatalystV1Underwriting
{}

File 12 of 23 : CatalystPayload.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// Catalyst Messaging payload structure ***********************************************************************************************
// Note: Addresses have 65 bytes reserved, however, the first byte should only be used for the address size.
//
// Common Payload (beginning)
//    CONTEXT               0   (1 byte)
//    + FROM_VAULT_LENGTH   1   (1 byte)
//    + FROM_VAULT          2   (64 bytes)
//    + TO_VAULT_LENGTH     66  (1 byte)
//    + TO_VAULT            67  (64 bytes)
//    + TO_ACCOUNT_LENGTH   131 (1 byte)
//    + TO_ACCOUNT          132 (64 bytes)
//    + UNITS               196 (32 bytes)
// 
// Context-depending Payload
//    CTX0 - 0x00 - Asset Swap Payload
//       + TO_ASSET_INDEX   228 (1 byte)
//       + MIN_OUT          229 (32 bytes)
//       + FROM_AMOUNT      261 (32 bytes)
//       + FROM_ASSET_LEN   293 (1 byte)
//       + FROM_ASSET       294 (64 bytes)
//       + BLOCK_NUMBER     358 (4 bytes)
//      (Underwrite Logic)
//       + UW_INCENTIVE     362 (2 bytes)
//
//    CTX1 - 0x01 - Liquidity Swap Payload
//       + MIN_OUT          228 (32 bytes)
//       + MIN_REFERENCE    260 (32 bytes)
//       + FROM_AMOUNT      292 (32 bytes)
//       + BLOCK_NUMBER     324 (4 bytes)
// 
// Common Payload (end)
//    + DATA_LENGTH         LENGTH-N-2 (2 bytes)
//    + DATA                LENGTH-N   (N bytes)



// Contexts *********************************************************************************************************************

bytes1 constant CTX0_ASSET_SWAP     = 0x00;
bytes1 constant CTX1_LIQUIDITY_SWAP = 0x01;


// Common Payload ***************************************************************************************************************

uint constant CONTEXT_POS           = 0;

uint constant FROM_VAULT_LENGTH_POS = 1;
uint constant FROM_VAULT_START      = 2;
uint constant FROM_VAULT_START_EVM  = 46;  // If the address is an EVM address, this is the start
uint constant FROM_VAULT_END        = 66;

uint constant TO_VAULT_LENGTH_POS   = 66;
uint constant TO_VAULT_START        = 67;
uint constant TO_VAULT_START_EVM    = 111;  // If the address is an EVM address, this is the start
uint constant TO_VAULT_END          = 131;

uint constant TO_ACCOUNT_LENGTH_POS = 131;
uint constant TO_ACCOUNT_START      = 132;
uint constant TO_ACCOUNT_START_EVM  = 176;  // If the address is an EVM address, this is the start
uint constant TO_ACCOUNT_END        = 196;

uint constant UNITS_START           = 196;
uint constant UNITS_END             = 228;



// CTX0 Asset Swap Payload ******************************************************************************************************

uint constant CTX0_TO_ASSET_INDEX_POS    = 228;

uint constant CTX0_MIN_OUT_START         = 229;
uint constant CTX0_MIN_OUT_END           = 261;

uint constant CTX0_FROM_AMOUNT_START     = 261;
uint constant CTX0_FROM_AMOUNT_END       = 293;

uint constant CTX0_FROM_ASSET_LENGTH_POS = 293; 
uint constant CTX0_FROM_ASSET_START      = 294; 
uint constant CTX0_FROM_ASSET_START_EVM  = 338;  // If the address is an EVM address, this is the start
uint constant CTX0_FROM_ASSET_END        = 358;

uint constant CTX0_BLOCK_NUMBER_START    = 358;
uint constant CTX0_BLOCK_NUMBER_END      = 362;

uint constant CTX0_UW_INCENTIVE_START    = 362;
uint constant CTX0_UW_INCENTIVE_END      = 364;

uint constant CTX0_DATA_LENGTH_START     = 364;
uint constant CTX0_DATA_LENGTH_END       = 366;

uint constant CTX0_DATA_START            = 366;



// CTX1 Liquidity Swap Payload **************************************************************************************************

uint constant CTX1_MIN_VAULT_TOKEN_START = 228;
uint constant CTX1_MIN_VAULT_TOKEN_END   = 260;

uint constant CTX1_MIN_REFERENCE_START   = 260;
uint constant CTX1_MIN_REFERENCE_END     = 292;

uint constant CTX1_FROM_AMOUNT_START     = 292;
uint constant CTX1_FROM_AMOUNT_END       = 324;

uint constant CTX1_BLOCK_NUMBER_START    = 324;
uint constant CTX1_BLOCK_NUMBER_END      = 328;

uint constant CTX1_DATA_LENGTH_START     = 328;
uint constant CTX1_DATA_LENGTH_END       = 330;

uint constant CTX1_DATA_START            = 330;

File 13 of 23 : IMessageEscrowErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

interface IMessageEscrowErrors {
    error AckHasNotBeenExecuted(); // 0x3d1553f8
    error CannotRetryWrongMessage(bytes32,bytes32); // 0x48ce7fac
    error DeadlineInPast(uint64 blocktimestamp, uint64 actual); // 0x2d098d59
    error DeadlineNotPassed(uint64 expected, uint64 actual); // 0x862c57f4
    error DeadlineTooLong(uint64 maxAllowed, uint64 actual); // 0x3c06f369
    error FeeRecipientIsZero(); // 0xfc53a835
    error ImplementationAddressAlreadySet(bytes currentImplementation); // 0xdba47850
    error IncorrectValueProvided(uint128 expected, uint128 actual); // 0x0b52a60b
    error InvalidImplementationAddress(); // 0xc970156c
    error InvalidTimeoutPackage(bytes32 expected, bytes32 actual); // 0xe020885d
    error MessageAlreadyAcked(); // 0x8af35858
    error MessageAlreadyBountied(); // 0x068a62ee
    error MessageAlreadyProcessed(); // 0x7b042609
    error MessageAlreadySpent(); // 0xe954aba2
    error MessageDoesNotExist(); // 0x970e41ec
    error MessageHasInvalidContext(); // 0x3fcdbaba
    error NoImplementationAddressSet(); // 0x9f994b4b
    error NotEnoughGasExecution(); // 0x6bc33587
    error NotEnoughGasProvided(uint128 expected, uint128 actual); // 0x030748b5
    error NotImplementedError(); // 0xd41c17e7
    error RefundGasToIsZero(); // 0x6a1a6afe
    error RouteDisabled(); // 0x17d0b6db
    error SendLostGasToIsZero(); // 0x9c76a9df
}

File 14 of 23 : IMessageEscrowEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import { IMessageEscrowStructs } from "./IMessageEscrowStructs.sol";

interface IMessageEscrowEvents {
    // Important notice for relayers. The implementations (sourceImplementation and destinationImplementation),
    // when indexed in events, the hash is in the topic not the actual implementation.
    event BountyPlaced(
        bytes indexed destinationImplementation, 
        bytes32 chainIdentifier,
        bytes32 indexed messageIdentifier,
        IMessageEscrowStructs.IncentiveDescription incentive
    );
    event MessageDelivered(bytes indexed sourceImplementation, bytes32 chainIdentifier, bytes32 indexed messageIdentifier);
    event MessageAcked(bytes destinationImplementation, bytes32 chainIdentifier, bytes32 messageIdentifier); // Not indexed since relayers can sort by BountyClaimed.
    event TimeoutInitiated(bytes sourceImplementation, bytes32 chainIdentifier, bytes32 messageIdentifier);
    event MessageTimedOut(bytes destinationImplementation, bytes32 chainIdentifier, bytes32 messageIdentifier); // Not indexed since relayers can sort by BountyClaimed.
    event BountyClaimed(
        bytes indexed destinationImplementation,
        bytes32 chainIdentifier,
        bytes32 indexed messageIdentifier,
        uint64 gasSpentOnDestination,
        uint64 gasSpentOnSource,
        uint128 destinationRelayerReward,
        uint128 sourceRelayerReward
    );

    // To save gas, this event does not emit the full incentive scheme.
    // Instead, the new gas prices are emitted. As a result, the relayer can collect all bountyIncreased
    // and then use the maximum. (since the  maxmimum is enforced in the smart contract)
    event BountyIncreased(
        bytes32 indexed messageIdentifier,
        uint96 newDeliveryGasPrice,
        uint96 newAckGasPrice 
    );


    event RemoteImplementationSet(address application, bytes32 chainIdentifier, bytes32 implementationAddressHash, bytes implementationAddress);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

interface ICrossChainReceiver {
    /**
     * @notice Handles the acknowledgement from the destination
     * @dev acknowledgement is exactly the output of receiveMessage except if receiveMessage failed, then it is error code (0xff or 0xfe) + original message.
     * If an acknowledgement isn't needed, this can be implemented as {}.
     * - This function can be called by someone else again! Ensure that if this endpoint is called twice with the same message nothing bad happens.
     * - If the application expects that the maxGasAck will be provided, then it should check that it got enough and revert if it didn't.
     * Otherwise, it is assumed that you didn't need the extra gas.
     * @param destinationIdentifier An identifier for the destination chain.
     * @param messageIdentifier A unique identifier for the message. The identifier matches the identifier returned when escrowed the message.
     * This identifier can be mismanaged by the messaging protocol.
     * @param acknowledgement The acknowledgement sent back by receiveMessage. Is 0xff if receiveMessage reverted.
     */
    function receiveAck(bytes32 destinationIdentifier, bytes32 messageIdentifier, bytes calldata acknowledgement) external;

    /**
     * @notice receiveMessage from a cross-chain call.
     * @dev The application needs to check the fromApplication combined with sourceIdentifierbytes to figure out if the call is authenticated.
     * - If the application expects that the maxGasDelivery will be provided, then it should check that it got enough and revert if it didn't.
     * Otherwise, it is assumed that you didn't need the extra gas.
     * @return acknowledgement Information which is passed to receiveAck. 
     *  If you return 0xff, you cannot know the difference between Executed but "failed" and outright failed.
     */
    function receiveMessage(bytes32 sourceIdentifierbytes, bytes32 messageIdentifier, bytes calldata fromApplication, bytes calldata message) external returns(bytes memory acknowledgement);
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/**
 * @title Escrow related functions defined by Catalyst v1 Vaults
 * @notice Contains the functions used to manage escrows by the cross-chain interface.
 */
interface ICatalystV1VaultSuccessFailure {
    /** @notice Release the escrowed tokens into the vault.  */
    function onSendAssetSuccess(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        address escrowToken,
        uint32 blockNumberMod
    ) external;

    /** @notice Returned the escrowed tokens to the user */
    function onSendAssetFailure(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        address escrowToken,
        uint32 blockNumberMod
    ) external;

    /** @notice Release the escrowed tokens into the vault.  */
    function onSendLiquiditySuccess(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        uint32 blockNumberMod
    ) external;

    /** @notice Returned the escrowed tokens to the user */
    function onSendLiquidityFailure(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        uint32 blockNumberMod
    ) external;
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
 * @title Administrative actions defined by Catalyst v1 Vaults
 * @notice Contains all functions which can only be called by privileged users.
 */
interface ICatalystV1VaultAdministration {
    function setFeeAdministrator(address administrator) external;

    function setVaultFee(uint64 fee) external;

    function setGovernanceFee(uint64 fee) external;

    /**
     * @notice Initializes the vault pricing parameters.
     * @param assets The list of assets the vault will support.
     * @param weights The weights of the tokens.
     * @param amp Vault amplification.
     * @param depositor The account to which the initial vault tokens are minted to.
     */
    function initializeSwapCurves(
        address[] calldata assets,
        uint256[] calldata weights,
        uint64 amp,
        address depositor
    ) external;

    /**
     * @notice Creates a connection to the vault toVault on the channel _channelId.
     * @dev if _vaultReceiving is an EVM vault, it can be computes as:
     *     Vyper: convert(_vaultAddress, bytes32)
     *     Solidity: abi.encode(_vaultAddress)
     *     Brownie: brownie.convert.to_bytes(_vaultAddress, type_str="bytes32")
     * setupMaster == ZERO_ADDRESS
     * @param channelId The _channelId of the target vault.
     * @param toVault The bytes32 representation of the target vault
     * @param state Boolean indicating if the connection should be open or closed.
     */
    function setConnection(
        bytes32 channelId,
        bytes calldata toVault,
        bool state
    ) external;

    /**
     * @notice Gives up short term ownership of the vault. This makes the vault unstoppable.
     */
    function finishSetup() external;
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/**
 * @title Derived Vault state
 * @notice Contains all vault state which is derived from vault storage
 */
interface ICatalystV1VaultDerived {
    /** @notice  Returns the current cross-chain unit capacity. */
    function getUnitCapacity() external view returns (uint256);

    function calcSendAsset(address from, uint256 amount) external view returns (uint256);

    /**
     * @notice Computes the output of ReceiveAsset, without executing one.
     * @param to The address of the token to buy.
     * @param U The number of units used to buy to.
     * @return uint256 Number of purchased tokens.
     */
    function calcReceiveAsset(address to, uint256 U) external view returns (uint256);

    /**
     * @notice Computes the output of localSwap, without executing one.
     * @param from The address of the token to sell.
     * @param to The address of the token to buy.
     * @param amount The amount of from token to sell for to token.
     * @return Output denominated in to token.
     */
    function calcLocalSwap(address from, address to, uint256 amount) external view returns (uint256);
}

File 19 of 23 : ICatalystV1VaultErrors.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

error EscrowAlreadyExists(); // 0xed778779
error ExceedsSecurityLimit(); // 0x7c1e66d0
error NotEnoughGas(); // 0xdd629f86
error ReturnInsufficient(uint256,uint256); // 0x24557f05
error UnusedUnitsAfterWithdrawal(uint256); // 0x0289311f
error VaultNotConnected(); // 0x2c64c1b2
error WithdrawRatioNotZero(); // 0xb8003bfa

File 20 of 23 : ICatalystV1VaultEvents.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/**
 * @title Events emitted by Catalyst v1 Vaults
 * @notice Contains all events emitted by the vault
 * @dev When using events to match transations, the combination of: channelId, fromVault, toAccount, toAsset, units, and block number is semi-guranteed to be unique.
 *      If more than 2**32 blocks exist, then all instances are guaranteed to be non-overlapping
 */
interface ICatalystV1VaultEvents {
    /**
     * @notice  Describes an atomic swap between the 2 tokens: _fromAsset and _toAsset.
     * @param account The user / exchange who facilitated the trade (msg.sender)
     * @param fromAsset The asset which was sold in exchange for _toAsset
     * @param toAsset The asset which was purchased with _fromAsset
     * @param fromAmount The number of _fromAsset sold
     * @param toAmount The number of tokens provided to toAccount
     */
    event LocalSwap(
        address indexed account,
        address fromAsset,
        address toAsset,
        uint256 fromAmount,
        uint256 toAmount
    );

    /**
     * @notice Describes the creation of an external swap: Cross-chain swap.
     * @param channelId The target chain identifier
     * @param toVault The target vault.
     * @param toAccount The recipient of the trade. The person who bought the trade is not present.
     * @param fromAsset The asset which was sold in exchange for _toAsset.
     * @param toAssetIndex The token index of the asset to purchase on _toChain.
     * @param fromAmount The number of _fromAsset sold.
     * @param minOut The minimum output to be accepted of fromAsset.
     * @param units The calculated number of units bought. Will be sold to buy _toAsset
     * @param underwriteIncentiveX16 The incentive out of 2**16 - 1 provided to the underwriter.
     * @param fee The number of tokens paid to the vault in fees.
     */
    event SendAsset(
        bytes32 channelId,
        bytes toVault,
        bytes toAccount,
        address fromAsset,
        uint8 toAssetIndex,
        uint256 fromAmount,
        uint256 minOut,
        uint256 units,
        uint256 fee,
        uint16 underwriteIncentiveX16
    );

    /**
     * @notice Describes the arrival of an external swap: Cross-chain swap.
     * If toAccount is used to match trades, remember to convert it into 64 + 1 bytes.
     * @param channelId The target chain identifier
     * @param fromVault The source vault.
     * @param toAccount The recipient of the trade.
     * @param toAsset The asset which was purchased with _fromAsset
     * @param units The number of units sent from the other chain.
     * @param toAmount The number of tokens provided to toAccount
     * @param fromAmount The amount spent to get units on the source side.
     * @param fromAsset The provided asset on the source side.
     * @param sourceBlockNumberMod The block number of the sending transaction mod 2**32 - 1
     */
    event ReceiveAsset(
        bytes32 channelId,
        bytes fromVault,
        address toAccount,
        address toAsset,
        uint256 units,
        uint256 toAmount,
        uint256 fromAmount,
        bytes fromAsset,
        uint32 sourceBlockNumberMod

    );

    /**
     * @notice Describes the creation of a liquidity swap
     * @param channelId The target chain identifier
     * @param toVault The target vault.
     * @param toAccount The recipient of the liquidity. The person who bought the trade is not present.
     * @param fromAmount The number of _fromAsset sold
     * @param minOut An array containing a list of minimum outputs [minVaultTokens, minReferenceAssets]
     * @param units The calculated number of liquidity units bought.
     */
    event SendLiquidity(
        bytes32 channelId,
        bytes toVault,
        bytes toAccount,
        uint256 fromAmount,
        uint256[2] minOut,
        uint256 units
    );

    /**
     * @notice Describes the arrival of a liquidity swap
     * @param channelId The target chain identifier
     * @param fromVault The source vault.
     * @param toAccount The recipient of the liquidity.
     * @param units The number of liquidity units sent from the other chain.
     * @param toAmount The number of vault tokens provided to toAccount
     * @param fromAmount The amount spent to get units on the source side.
     * @param sourceBlockNumberMod The block number of the sending transaction mod 2**32 - 1
     */
    event ReceiveLiquidity(
        bytes32 channelId,
        bytes fromVault,
        address toAccount,
        uint256 units,
        uint256 toAmount,
        uint256 fromAmount,
        uint256 sourceBlockNumberMod
    );

    /**
     * @notice Emitted on liquidity deposits.
     * @param toAccount The depositor. Is credited with _mints vault tokens.
     * @param mint The number of minted vault tokens credited to toAccount
     * @param assets An array of the number of deposited assets.
     */
    event VaultDeposit(address indexed toAccount, uint256 mint, uint256[] assets);

    /**
     * @notice Emitted on liquidity withdrawal.
     * @param toAccount The withdrawer. Is debited _burns vault tokens.
     * @param burn The number of burned vault tokens.
     * @param assets An array of the token amounts returned
     */
    event VaultWithdraw(address indexed toAccount, uint256 burn, uint256[] assets);

    /** @notice Called upon successful asset swap. */
    event SendAssetSuccess(
        bytes32 channelId,
        bytes toAccount,
        uint256 units,
        uint256 escrowAmount,
        address escrowToken,
        uint32 blockNumberMod
    );

    /** @notice Called upon failed asset swap. */
    event SendAssetFailure(
        bytes32 channelId,
        bytes toAccount,
        uint256 units,
        uint256 escrowAmount,
        address escrowToken,
        uint32 blockNumberMod
    );

    /** @notice Called upon successful liquidity swap. */
    event SendLiquiditySuccess(
        bytes32 channelId,
        bytes toAccount,
        uint256 units,
        uint256 escrowAmount,
        uint32 blockNumberMod
    );

    /** @notice Called upon failed liquidity swap. */
    event SendLiquidityFailure(
        bytes32 channelId,
        bytes toAccount,
        uint256 units,
        uint256 escrowAmount,
        uint32 blockNumberMod
    );

    /** @notice Vault setup has been finalised. */
    event FinishSetup();

    /**
     * @notice Emitted on fee administrator adjustment
     * @param administrator The new vault fee administrator
     */
    event SetFeeAdministrator(
        address administrator
    );

    /**
     * @notice Emitted on vault fee adjustment
     * @param fee The new vault fee
     */
    event SetVaultFee(
        uint64 fee
    );

    /**
     * @notice Emitted on governance fee adjustment
     * @param fee The new governance fee
     */
    event SetGovernanceFee(
        uint64 fee
    );

    /**
     * @notice Emitted on weights modification
     * @param targetTime Time at which the weights adjustment must complete.
     * @param targetWeights The desired new weights.
     */
    event SetWeights(
        uint248 targetTime,
        uint256[] targetWeights
    );

    /**
     * @notice Amplification has been modification
     * @param targetTime Time at which the amplification adjustment must complete.
     * @param targetAmplification The desired new amplification.
     */
    event SetAmplification(
        uint48 targetTime,
        uint256 targetAmplification
    );

    /**
     * @notice A connection has been modified
     * @param channelId Target chain identifier.
     * @param toVault Bytes32 representation of the target vault.
     * @param newState Boolean indicating if the connection should be open or closed.
     */
    event SetConnection(
        bytes32 channelId,
        bytes toVault,
        bool newState
    );

    //-- Underwriting Events --//

    /**
     * @notice A swap has been underwritten.
     */
    event SwapUnderwritten(
        bytes32 indexed identifier,
        address toAsset,
        uint256 U,
        uint256 purchasedTokens
    );

}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/**
 * @title Immutable vault state
 * @notice Contains all vault state which doesn't change once set.
 */
interface ICatalystV1VaultImmutables {
    function _chainInterface() external view returns (address);

    function FACTORY() external view returns (address);

    function MATHLIB() external view returns (address);

    /** 
     * @notice To indicate which token is desired on the target vault,the _toAsset is an integer
     * from 0 to MAX_ASSETS indicating which asset the vault should purchase with units.
     */
    function _tokenIndexing(uint256 tokenIndex) external view returns (address);
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { ICatalystV1Structs } from "./ICatalystV1VaultState.sol";

interface ICatalystV1VaultPermissionless {
    /** 
     * @notice Setup a vault.
     * @param name_ Name of the Vault token.
     * @param symbol_ Symbol for the Vault token.
     * @param chainInterface Cross chain interface used for cross-chain swaps. (Can be address(0) to disable cross-chain swaps.)
     * @param vaultFee Vault fee.
     * @param governanceFee Governance fee share.
     * @param feeAdministrator Account that can modify the fees.
     * @param setupMaster Short-term owner of the vault (until finishSetup is called).
     */
    function setup(
        string calldata name_,
        string calldata symbol_,
        address chainInterface,
        uint64 vaultFee,
        uint64 governanceFee,
        address feeAdministrator,
        address setupMaster
    ) external;

    //--- Balance Changes ---//

    /**
     * @notice Deposits a user configurable amount of tokens.
     * @dev Requires approvals for all deposited tokens within the vault.
     * Volatile: It is advised that the deposit matches the vault's %token distribution.
     * Amplified: It is advised that the deposit is as close to 1,1,... as possible.
     *            Otherwise between 1,1,... and the vault's %token distribution.
     * @param tokenAmounts Array of the tokens amounts to be deposited.
     * @param minOut Minimum number of vault tokens to be minted.
     */
    function depositMixed(uint256[] calldata tokenAmounts, uint256 minOut)
        external returns(uint256);

    /**
     * @notice Burns baseAmount and releases the symmetrical share
     * of tokens to the burner. This doesn't change the vault price.
     * @param baseAmount Number of vault tokens to burn.
     */
    function withdrawAll(uint256 baseAmount, uint256[] calldata minOut)
        external returns(uint256[] memory);

    /**
     * @notice Burns vaultTokens and release a token distribution which can be set by the user.
     * @dev Requires approvals for all tokens within the vault.
     * Volatile: It is advised that the deposit matches the vault's %token distribution.
     * Amplified: It is advised that the deposit matches the vault's %token distribution.
     *            Otherwise it should be weighted towards the tokens the vault has more of.
     * @param vaultTokens Number of vault tokens to withdraw.
     * @param withdrawRatio Percentage of units used to withdraw. In the following special scheme: U_0 = U · withdrawRatio[0], U_1 = (U - U_0) · withdrawRatio[1], U_2 = (U - U_0 - U_1) · withdrawRatio[2], .... Is WAD.
     * @param minOut Minimum number of tokens minted.
     */
    function withdrawMixed(
        uint256 vaultTokens,
        uint256[] calldata withdrawRatio,
        uint256[] calldata minOut
    ) external returns(uint256[] memory);

    //--- Swaps ---//

    /**
     * @notice A swap between 2 assets which both are inside the vault. Is atomic.
     * @param fromAsset Asset the user wants to sell.
     * @param toAsset Asset the user wants to buy.
     * @param amount Amount of fromAsset the user wants to sell.
     * @param minOut Minimum output of _toAsset the user wants.
     */
    function localSwap(
        address fromAsset,
        address toAsset,
        uint256 amount,
        uint256 minOut
    ) external returns (uint256);

    /**
     * @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault.
     * @dev Addresses are encoded in 64 + 1 bytes. To encode for EVM, encode as:
     * Solidity: abi.encodePacket(uint8(20), bytes32(0), abi.encode(<vaultAddress>))
     * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
     * @param fromAsset Asset the user wants to sell.
     * @param toAssetIndex Index of the asset the user wants to buy in the target vault.
     * @param amount Number of fromAsset to sell to the vault.
     * @param minOut Minimum number of returned tokens to the toAccount on the target chain.
     * @param fallbackUser If the transaction fails, send the escrowed funds to this address.
     * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16.max))
     * @param calldata_ Data field if a call should be made on the target chain.
     * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(<address>), <data>). At maximum 65535 bytes can be passed.
     * @return uint256 The number of units minted.
     */
    function sendAsset(
        ICatalystV1Structs.RouteDescription calldata routeDescription,
        address fromAsset,
        uint8 toAssetIndex,
        uint256 amount,
        uint256 minOut,
        address fallbackUser,
        uint16 underwriteIncentiveX16,
        bytes calldata calldata_
    ) external payable returns (uint256);

    /**
     * @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault using a fixed number of units.
     * @dev Addresses are encoded in 64 + 1 bytes. To encode for EVM, encode as:
     * Solidity: abi.encodePacket(uint8(20), bytes32(0), abi.encode(<vaultAddress>))
     * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
     * @param fromAsset Asset the user wants to sell.
     * @param toAssetIndex Index of the asset the user wants to buy in the target vault.
     * @param amount Number of fromAsset to sell to the vault.
     * @param minOut Minimum number of returned tokens to the toAccount on the target chain.
     * @param minU Minimum and exact number of units sent.
     * @param fallbackUser If the transaction fails, send the escrowed funds to this address.
     * @param calldata_ Data field if a call should be made on the target chain.
     * Encoding depends on the target chain, with evm being: abi.encodePacket(bytes20(<address>), <data>)
     * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16.max))
     * @return uint256 Number of units minted.
     */
    function sendAssetFixedUnit(
        ICatalystV1Structs.RouteDescription calldata routeDescription,
        address fromAsset,
        uint8 toAssetIndex,
        uint256 amount,
        uint256 minOut,
        uint256 minU,
        address fallbackUser,
        uint16 underwriteIncentiveX16,
        bytes calldata calldata_
    ) external payable returns (uint256);

    /**
     * @notice Completes a cross-chain swap by converting units to the desired token (toAsset)
     * @dev Can only be called by the chainInterface.
     * @param channelId The incoming connection identifier.
     * @param fromVault The source vault.
     * @param toAssetIndex Index of the asset to be purchased with Units.
     * @param toAccount The recipient.
     * @param U Number of units to convert into toAsset.
     * @param minOut Minimum number of tokens bought. Reverts if less.
     * @param fromAmount Used to match cross-chain swap events. The input amount on the sending chain.
     * @param fromAsset Used to match cross-chain swap events. The input asset on the sending chain.
     * @param blockNumberMod Used to match cross-chain swap events. The block number from the host side.
     */
    function receiveAsset(
        bytes32 channelId,
        bytes calldata fromVault,
        uint256 toAssetIndex,
        address toAccount,
        uint256 U,
        uint256 minOut,
        uint256 fromAmount,
        bytes calldata fromAsset,
        uint32 blockNumberMod
    ) external returns(uint256 purchasedTokens);

    /**
     * @notice Initiate a cross-chain liquidity swap by withdrawing tokens and converting them to units.
     * @dev While the description says tokens are withdrawn and then converted to units, vault tokens are converted
     * directly into units through the following equation:
     *      U = ln(PT/(PT-pt)) * \sum W_i
     * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
     * @param vaultTokens Number of vault tokens to exchange.
     * @param minOut Array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets].
     * @param fallbackUser If the transaction fails, send the escrowed funds to this address.
     * @param calldata_ Data field if a call should be made on the target chain.
     * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(<address>), <data>). At maximum 65535 bytes can be passed.
     * @return uint256 Number of units minted.
     */
    function sendLiquidity(
        ICatalystV1Structs.RouteDescription calldata routeDescription,
        uint256 vaultTokens,
        uint256[2] calldata minOut,
        address fallbackUser,
        bytes calldata calldata_
    ) external payable returns (uint256);

    /**
     * @notice Completes a cross-chain liquidity swap by converting units to tokens and depositing.
     * @dev Called exclusively by the chainInterface.
     * @param fromVault Source vault
     * @param toAccount Recipient of the vault tokens
     * @param U Number of units to convert into vault tokens.
     * @param minVaultTokens Minimum number of vault tokens to mint on target vault. Otherwise: Reject
     * @param minReferenceAsset Minimum number of reference asset the vaults tokens are worth. Otherwise: Reject
     * @param fromAmount Used to match cross-chain swap events. The input amount on the sending chain.
     * @param blockNumberMod Used to match cross-chain swap events. The block number from the host side.
     */
    function receiveLiquidity(
        bytes32 channelId,
        bytes calldata fromVault,
        address toAccount,
        uint256 U,
        uint256 minVaultTokens,
        uint256 minReferenceAsset,
        uint256 fromAmount,
        uint32 blockNumberMod
    ) external returns(uint256 purchasedVaultTokens);
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/** @title Extensions to vaults which supports underwriting. */
interface ICatalystV1Underwriting {
    function underwriteAsset(
        bytes32 identifier,
        address toAsset,
        uint256 U,
        uint256 minOut
    ) external returns (uint256);

    function releaseUnderwriteAsset(
        address refundTo,
        bytes32 identifier,
        uint256 escrowAmount,
        address escrowToken,
        bytes32 sourceIdentifier,
        bytes calldata fromVault
    ) external;

    function deleteUnderwriteAsset(
        bytes32 identifier,
        uint256 U,
        uint256 escrowAmount,
        address escrowToken
    ) external;
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solmate/=lib/solmate/src/",
    "GeneralisedIncentives/=lib/GeneralisedIncentives/",
    "@lazyledger/protobuf3-solidity-lib/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/protobuf3-solidity-lib/",
    "@openzeppelin-upgradeable/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/openzeppelin-contracts-upgradeable/",
    "@openzeppelin/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/openzeppelin-contracts/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "base64/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/base64/",
    "clones-with-immutable-args/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/clones-with-immutable-args/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts-upgradeable/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/GeneralisedIncentives/lib/openzeppelin-contracts/contracts/",
    "optimism/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/",
    "proto/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/proto/",
    "protobuf3-solidity-lib/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/protobuf3-solidity-lib/contracts/",
    "safe-contracts/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/safe-contracts/contracts/",
    "solady/=lib/solady/src/",
    "solmate/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/solmate/src/",
    "vibc-core-smart-contracts/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"GARP_","type":"address"},{"internalType":"address","name":"defaultOwner","type":"address"}],"stateMutability":"payable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ChainAlreadySetup","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidBytes65Address","type":"error"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[{"internalType":"bytes1","name":"context","type":"bytes1"}],"name":"InvalidContext","type":"error"},{"inputs":[],"name":"InvalidSourceApplication","type":"error"},{"inputs":[],"name":"MaxUnderwriteDurationTooLong","type":"error"},{"inputs":[],"name":"MaxUnderwriteDurationTooShort","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NoVaultConnection","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"NotEnoughIncentives","type":"error"},{"inputs":[],"name":"SwapAlreadyUnderwritten","type":"error"},{"inputs":[],"name":"SwapRecentlyUnderwritten","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"}],"name":"UnderwriteDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint256","name":"blocksUnitilExpiry","type":"uint256"}],"name":"UnderwriteNotExpired","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":false,"internalType":"address","name":"expirer","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"ExpireUnderwrite","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"}],"name":"FulfillUnderwrite","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMaxUnderwriteDuration","type":"uint256"}],"name":"MaxUnderwriteDuration","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":false,"internalType":"uint48","name":"minGas","type":"uint48"}],"name":"MinGasFor","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"remoteCCI","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"remoteGARP","type":"bytes"}],"name":"RemoteImplementationSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes1","name":"error","type":"bytes1"}],"name":"SwapFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":true,"internalType":"address","name":"underwriter","type":"address"},{"indexed":false,"internalType":"uint96","name":"expiry","type":"uint96"},{"indexed":false,"internalType":"address","name":"targetVault","type":"address"},{"indexed":false,"internalType":"address","name":"toAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"U","type":"uint256"},{"indexed":false,"internalType":"address","name":"toAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"outAmount","type":"uint256"}],"name":"SwapUnderwritten","type":"event"},{"inputs":[],"name":"EXPIRE_CALLER_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXPIRE_CALLER_REWARD_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GARP","outputs":[{"internalType":"contract IIncentivizedMessageEscrow","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNDERWRITING_COLLATERAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNDERWRITING_COLLATERAL_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"chainIdentifierToDestinationAddress","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"bytes","name":"remoteCCI","type":"bytes"},{"internalType":"bytes","name":"remoteGARP","type":"bytes"}],"name":"connectNewChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"evmAddress","type":"address"}],"name":"convertEVMTo65","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"estimateAdditionalCost","outputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"targetVault","type":"address"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"cdata","type":"bytes"}],"name":"expireUnderwrite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"targetVault","type":"address"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"cdata","type":"bytes"}],"name":"getUnderwriteIdentifier","outputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"maxUnderwritingDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"minGasFor","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"destinationIdentifier","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes","name":"acknowledgement","type":"bytes"}],"name":"receiveAck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"sourceIdentifier","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes","name":"fromApplication","type":"bytes"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"receiveMessage","outputs":[{"internalType":"bytes","name":"acknowledgement","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"bytes","name":"toVault","type":"bytes"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"},{"internalType":"uint64","name":"deadline","type":"uint64"}],"internalType":"struct ICatalystV1Structs.RouteDescription","name":"routeDescription","type":"tuple"},{"internalType":"uint8","name":"toAssetIndex","type":"uint8"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"calldata_","type":"bytes"}],"name":"sendCrossChainAsset","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"bytes","name":"toVault","type":"bytes"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"},{"internalType":"uint64","name":"deadline","type":"uint64"}],"internalType":"struct ICatalystV1Structs.RouteDescription","name":"routeDescription","type":"tuple"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256[2]","name":"minOut","type":"uint256[2]"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"bytes","name":"calldata_","type":"bytes"}],"name":"sendCrossChainLiquidity","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMaxUnderwriteDuration","type":"uint256"}],"name":"setMaxUnderwritingDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"uint48","name":"minGas","type":"uint48"}],"name":"setMinGasFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"thisBytes65","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"targetVault","type":"address"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"cdata","type":"bytes"}],"name":"underwrite","outputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"sourceIdentifier","type":"bytes32"},{"internalType":"bytes","name":"fromVault","type":"bytes"},{"internalType":"address","name":"targetVault","type":"address"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"cdata","type":"bytes"}],"name":"underwriteAndCheckConnection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"underwritingStorage","outputs":[{"internalType":"uint256","name":"tokens","type":"uint256"},{"internalType":"address","name":"refundTo","type":"address"},{"internalType":"uint96","name":"expiry","type":"uint96"}],"stateMutability":"view","type":"function"}]

60a0601f620037d338819003918201601f19168301916001600160401b038311848410176200011857808492604094855283398101031262000113576200005460206200004c836200012e565b92016200012e565b613840600281905590916001600160a01b039081169190821562000113577f88e95e1ea5dc9ffb42b44903f6dc9ba92e46f0ae1931bd6e6cbeecdf3d6b9a02936020936080521680638b78c6d8195560007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3604051908152a160405161368f9081620001448239608051818181610b0d01528181610d1a01528181611072015281816112ca0152818161135f015281816118a10152611a2d0152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620001135756fe60a060408181526004918236101561001657600080fd5b600092833560e01c91826311cbf805146119e3575081631e2b8990146119c6578163256929621461197b578163318987c4146116035781634f37a457146115d35781635330a5de1461152057816354d1f13d146114da578163715018a614611479578163898e68ad1461145d5781638da5cb5b1461140957816392006e731461130d57816394aaf73f146112ee578163989e34b31461129d578163ac23728114610f14578163b40f969a14610f19578163ba3c99e614610f14578163bf09469414610eef578163ca9b7b2f14610e5c578163d00689b614610caf578163d6512967146107de578163d94501871461078b578163daf1e1671461076c578163dbaed76b146105d7578163dd4aeedd146103a2578163e5a7783514610376578163e73ae76414610326578163ea901f1a146102e8578163f04e283e1461023c578163f2fde38b146101a5575063fee81cf41461016f57600080fd5b346101a15760206003193601126101a15760209161018b611ad1565b9063389a75e1600c525281600c20549051908152f35b5080fd5b839060206003193601126101a1576101bb611ad1565b906101c4612a94565b8160601b15610231575073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b637448fbae8352601cfd5b8360206003193601126102e557610251611ad1565b610259612a94565b63389a75e1600c528082526020600c2092835442116102da57508173ffffffffffffffffffffffffffffffffffffffff929355167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b636f5e88188352601cfd5b80fd5b5050346101a15760206003193601126101a1576103229061030f61030a611ad1565b6125fd565b9051918291602083526020830190611bae565b0390f35b90503461037257602060031936011261037257906103608161032294610359943581528060205220825193848092611c44565b0383611d57565b51918291602083526020830190611bae565b8280fd5b5050346101a15760209061039b61038c36611af4565b96959095949194939293612135565b9051908152f35b91905034610372576103c9906103b736611af4565b928699948684979c988a959495612acc565b9586885260036020528488209260018401549373ffffffffffffffffffffffffffffffffffffffff908186169081156105a857908b94939291549660a01c90330361052a575b508984526003602052600060018986208281550155808716803b15610526578492836084928d948c5197889687957fb9ff629b00000000000000000000000000000000000000000000000000000000875286015260248501528a60448501528d1660648401525af1801561051c576104fc575b50507f5d002c935f79d7b578b7c23b1101d4508bb3bd36e5b53c2aa00263ee5ac9f0de9461ffff936104f695936104e09360236103e897889216830260101c920204019461015e86020480956104d9823386612c5a565b0391612c5a565b5133815260208101919091529081906040820190565b0390a280f35b6105099095939495611cf8565b61051857863894939294610482565b8680fd5b86513d84823e3d90fd5b8480fd5b909192935043811161054057908a93929161040f565b82889143810390811161057a5760249251917f62141db5000000000000000000000000000000000000000000000000000000008352820152fd5b6011827f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b6024848c8b51917fae029d69000000000000000000000000000000000000000000000000000000008352820152fd5b8383346101a1576101206003193601126101a15767ffffffffffffffff6024358181116107685761060b9036908601611a86565b91906044359073ffffffffffffffffffffffffffffffffffffffff9182811695868203610763576064359284841684036107635760c43594851685036107635760e4359561ffff87168703610763576101043590811161075f576106be916106788c602093369101611a86565b9a90998d8651958694859384937f66ae79e600000000000000000000000000000000000000000000000000000000855280359085015289602485015260448401916120af565b03915afa908115610755578991610716575b50156106ee57506106ea96975060a4359160843591612135565b5080f35b8890517fea66ca6d000000000000000000000000000000000000000000000000000000008152fd5b90506020813d60201161074d575b8161073160209383611d57565b8101031261074957518015158103610749578a6106d0565b8880fd5b3d9150610724565b82513d8b823e3d90fd5b8980fd5b600080fd5b8380fd5b5050346101a157816003193601126101a1576103229061030f306125fd565b9050346103725760206003193601126103725760609282913581526003602052209060018254920154815192835273ffffffffffffffffffffffffffffffffffffffff8116602084015260a01c90820152f35b6003198484610100368401126101a15783359367ffffffffffffffff80861161076857610140868301958736030112610768576024359560ff871687036105265760a4359273ffffffffffffffffffffffffffffffffffffffff928385168095036107635760c4359061ffff821682036107635760e435908111610cab57908899610870899a98999336908601611a86565b9290918a61089d60648901809c3597888152600160205265ffffffffffff80948192205416928392611e94565b1610610c9d5750808c8e8080526001602052205416908160848a01916108c283611e94565b1610610c4b57505060e487016108d781611ea7565b600b3a023a8104600b143a151715610c1f57600a900491906bffffffffffffffffffffffff168211610bc3575050908c9291604161091960448a018096611f02565b905003610b9b578d604161093160248b018093611f02565b905003610b7357918e8e9f928e9f979695939e809e9f889661095291611f02565b9761095d9194611f02565b90818984519a8b976020890160805260805137860191602083018d8152370191604435602084015260f81b7fff00000000000000000000000000000000000000000000000000000000000000169082015260643560418201526084356061820152036061810185526081016109d29085611d57565b7fffff00000000000000000000000000000000000000000000000000000000000080918d519c888e9960208b01527f1400000000000000000000000000000000000000000000000000000000000000978860218c01528160228c01523360428c01525160628b018160805191610a4792611b8b565b8a01976062890152606388015260838701524360e01b7fffffffff000000000000000000000000000000000000000000000000000000001660a387015260f01b1660a78501528260f01b1660a984015260ab830137018a60ab82015203608b8101865260ab01610ab79086611d57565b808952886020528589209261012401610acf90611f53565b8651978896879586957f65a686de000000000000000000000000000000000000000000000000000000008752860194610b0795611f97565b039134907f0000000000000000000000000000000000000000000000000000000000000000165af18015610b6757610b3d578280f35b81610b5c92903d10610b60575b610b548183611d57565b810190611f68565b8280f35b503d610b4a565b505051903d90823e3d90fd5b878d517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b868c517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b610c1b8d610bd18a93611ea7565b90519384937f6de7824600000000000000000000000000000000000000000000000000000000855284019092916bffffffffffffffffffffffff6020916040840195845216910152565b0390fd5b60248f60118b7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b610c1b8d610c598a93611e94565b90517f6de7824600000000000000000000000000000000000000000000000000000000815265ffffffffffff94851693810193845293166020830152829160400190565b87610c1b8e610c598f611e94565b8780fd5b9050346103725760806003193601126103725780359267ffffffffffffffff9160443583811161037257610ce69036908301611a86565b95909360643590811161076857610d009036908401611a86565b92909473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163303610e3457610d49886120ee565b610d5588519182611d57565b888152602098898201923682820111610cab578188928c92863783010152519020828552848852610d90610d97888720895192838092611c44565b0382611d57565b88815191012003610e0e577fff000000000000000000000000000000000000000000000000000000000000008761032288610dff60218a8a8a80610ddc81858e612b7b565b87519a16898b015289938585013782019083820152036001810187520185611d57565b51928284938452830190611bae565b85517e3923e0000000000000000000000000000000000000000000000000000000008152fd5b5085517f48f5c3ed000000000000000000000000000000000000000000000000000000008152fd5b90503461037257816003193601126103725735906024359165ffffffffffff8316809303610763577f0f610a3dcd6165f6c2336e534655b0895a80b36322401c17b5bd0eea058ed1b992610eae612a94565b8185526001602052828520817fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000082541617905582519182526020820152a180f35b5050346101a15760209061039b610f0536611af4565b96959095949194939293612acc565b611ab4565b8383346101a15760606003193601126101a1578235906024359367ffffffffffffffff9485811161052657610f519036908301611a86565b91909360443587811161051857610f6b9036908401611a86565b939095610f76612a94565b6041820361127557828852602091888352610f93878a2054611bf1565b61124d5783895288835286892099811161122157610fb18a54611bf1565b99601f8b116111de575b89809b50979899601f83116001146111125760609383611058937f4bdc885fecec9b48853a8df98337140d59c2ebd57b0386bd50133873b969a331979561104a948d91611107575b508260011b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8460031b1c19161790555b8b5195869589875286015260608501916120af565b8281038a840152878b6120af565b0390a173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691823b156105265784906110de8751988996879586947fffbeacc700000000000000000000000000000000000000000000000000000000865285015289602485015260448401916120af565b03925af19081156110fe57506110f2575080f35b6110fb90611cf8565b80f35b513d84823e3d90fd5b905083013538611003565b818952848920907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084168a5b8181106111c45750936110589361104a9360609793837f4bdc885fecec9b48853a8df98337140d59c2ebd57b0386bd50133873b969a3319a981061118c575b5050600182811b019055611035565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88560031b161c19908501351690558f8061117d565b8287013584558e9b5060019093019291870191870161113e565b808a52838a20601f830160051c81019b858410611217575b601f0160051c019a5b8b811061120c5750610fbb565b8a81556001016111ff565b909b508b906111f6565b6024896041877f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8487517fb8e35614000000000000000000000000000000000000000000000000000000008152fd5b8386517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b5050346101a157816003193601126101a1576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5050346101a157816003193601126101a1576020906002549051908152f35b8383346101a157816003193601126101a15780805180947f92006e730000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9283156113fe578280946113bb575b50505173ffffffffffffffffffffffffffffffffffffffff9190911681526020810191909152604090f35b91935091508282813d83116113f7575b6113d58183611d57565b810103126102e5575061032260206113ec83612075565b920151929084611390565b503d6113cb565b5051903d90823e3d90fd5b5050346101a157816003193601126101a15760209073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754915191168152f35b5050346101a157816003193601126101a1576020905160238152f35b83806003193601126102e55761148d612a94565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35580f35b83806003193601126102e55763389a75e1600c52338152806020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c928280a280f35b919050346103725760206003193601126103725781359161153f612a94565b61012c8311156115ac576206270083116115855750816020917f88e95e1ea5dc9ffb42b44903f6dc9ba92e46f0ae1931bd6e6cbeecdf3d6b9a029360025551908152a180f35b90517f3f6368aa000000000000000000000000000000000000000000000000000000008152fd5b90517f6229dcd0000000000000000000000000000000000000000000000000000000008152fd5b905034610372576020600319360112610372578160209365ffffffffffff92358152600185522054169051908152f35b905060031960c0813601126107685781359167ffffffffffffffff8084116119775761014084830193853603011261197757366084116119775760a435908111611977576116549036908301611a86565b926064850190803594858952600160205265ffffffffffff80898b205416808261167d87611e94565b16106119695750898052600160205280898b205416908160848a01916116a283611e94565b161061195b57505060e487016116b781611ea7565b600b3a023a8104600b143a15171561192f57600a900491906bffffffffffffffffffffffff168211611921575050604487019360416116f68685611f02565b9050036118f95760248801604161170d8286611f02565b9050036118d157838361183e93611887979561173f8f8f611735819f9e9d9b9860e899611f02565b9b8c949198611f02565b938492519c8d997f010000000000000000000000000000000000000000000000000000000000000060208c01527f140000000000000000000000000000000000000000000000000000000000000060218c01528560228c01523360428c015260628b0137880192606284019081523701916024356062840152604435608284015260643560a284015260843560c28401527fffffffff000000000000000000000000000000000000000000000000000000004360e01b1660e28401527fffff0000000000000000000000000000000000000000000000000000000000008260f01b1660e684015285830137018c838201520360c8810185520183611d57565b84895288602052611855610124878b209801611f53565b91865197889687967f65a686de0000000000000000000000000000000000000000000000000000000088528701611f97565b03813473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af18015610b6757610b3d578280f35b868a517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b8589517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b610c1b8a610bd18993611ea7565b60248c60118a7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b610c1b8a610c598993611e94565b86610c1b8b610c5988611e94565b8580fd5b83806003193601126102e55763389a75e1600c523381526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d8280a280f35b5050346101a157816003193601126101a1576020905161015e8152f35b8490346101a15760606003193601126101a15760443567ffffffffffffffff811161037257611a159036908501611a86565b9173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163303611a5f57506110fb929335611d98565b807f48f5c3ed00000000000000000000000000000000000000000000000000000000869252fd5b9181601f840112156107635782359167ffffffffffffffff8311610763576020838186019501011161076357565b346107635760006003193601126107635760206040516103e88152f35b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361076357565b9060e06003198301126107635773ffffffffffffffffffffffffffffffffffffffff91600435838116810361076357926024358181168103610763579260443592606435926084359081168103610763579160a43561ffff81168103610763579160c4359067ffffffffffffffff821161076357611b7491600401611a86565b9091565b359065ffffffffffff8216820361076357565b60005b838110611b9e5750506000910152565b8181015183820152602001611b8e565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093611bea81518092818752878088019101611b8b565b0116010190565b90600182811c92168015611c3a575b6020831014611c0b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691611c00565b805460009392611c5382611bf1565b91828252602093600191600181169081600014611cbb5750600114611c7a575b5050505050565b90939495506000929192528360002092846000945b838610611ca757505050500101903880808080611c73565b805485870183015294019385908201611c8f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168685015250505090151560051b010191503880808080611c73565b67ffffffffffffffff8111611d0c57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff821117611d0c57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611d0c57604052565b8215611e65577fff0000000000000000000000000000000000000000000000000000000000000082351680611e025750826001116107635760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611e0094019201906128db565b565b60207fe3d0bbb47fe45a6223645bd4352bb4d9262d1acaa2c0ffd75fa050fca0439b7f91604051908152a1826001116107635760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611e009401920190612708565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b3565ffffffffffff811681036107635790565b356bffffffffffffffffffffffff811681036107635790565b81810292918115918404141715611ed357565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610763570180359067ffffffffffffffff82116107635760200191813603831361076357565b3567ffffffffffffffff811681036107635790565b9190826040910312610763576020825192015190565b35906bffffffffffffffffffffffff8216820361076357565b9395949290611fc692611fb891610140908752806020880152860190611c44565b908482036040860152611bae565b9365ffffffffffff80611fd883611b78565b166060850152611fea60208301611b78565b16608084015260408101359073ffffffffffffffffffffffffffffffffffffffff82168092036107635760a0918285015261202760608201611f7e565b6bffffffffffffffffffffffff80911660c086015261204860808301611f7e565b1660e085015201359067ffffffffffffffff90818316809303610763576101209261010085015216910152565b519073ffffffffffffffffffffffffffffffffffffffff8216820361076357565b9060416080928183526020830137600060618201520190565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b67ffffffffffffffff8111611d0c57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b91908201809211611ed357565b909392979695949561214d818789878d888b89612acc565b98896000526003602052604060002097600189015460a01c80612535575b506201000091808060101b04831481151715611ed3578261ffff8316810311611ed35761ffff8216831461250657604051927f119f8e680000000000000000000000000000000000000000000000000000000084528c600485015273ffffffffffffffffffffffffffffffffffffffff8916602485015286604485015261ffff831690039060101b046064830152602082608481600073ffffffffffffffffffffffffffffffffffffffff89165af191821561248e576000926124d2575b50600189015473ffffffffffffffffffffffffffffffffffffffff81166124a85761229e9060016bffffffffffffffffffffffff61226960025443612128565b868e551660a081901b7fffffffffffffffffffffffff000000000000000000000000000000000000000016331791909c015550565b61040b808302908382041483151715611ed3576103e86040519104606052306040523360601b602c526f23b872dd000000000000000000000000600c52602060006064601c828c5af13d1560016000511417161561249a57600060605260405261230c9061ffff1682611ec0565b60101c90039561231d878688612c5a565b8160021161076357803560f01c9182612398575b50505073ffffffffffffffffffffffffffffffffffffffff93848092604051988952166020880152166040860152606085015216608083015260a0820152827fb2b54dbcb6f6e94446d5dc07448faa5aff2a1fa2ef8280125cbc19a9b9514c4360c03393a3565b806016979394971161076357600282013560601c9360020161ffff8111611ed35761ffff16908160161161076357811161076357833b156107635761244593600092838a604051978895869485937f5ea79f2600000000000000000000000000000000000000000000000000000000855260048501526060602485015260167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea60648601930191016120af565b6001604483015203925af1801561248e5773ffffffffffffffffffffffffffffffffffffffff958693849261247f575b5092829650612331565b61248890611cf8565b38612475565b6040513d6000823e3d90fd5b637939f4246000526004601cfd5b60046040517fd0c27c9f000000000000000000000000000000000000000000000000000000008152fd5b9091506020813d6020116124fe575b816124ee60209383611d57565b8101031261076357519038612229565b3d91506124e1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6bfffffffffffffffffffffffb8110806125f3575b156125b5576bffffffffffffffffffffffff80431691828260048301161015612578575b5050505b3861216b565b6003190116111561258b5738808061256e565b60046040517f695b3a94000000000000000000000000000000000000000000000000000000008152fd5b6bffffffffffffffffffffffff4316036125725760046040517f695b3a94000000000000000000000000000000000000000000000000000000008152fd5b506004811161254a565b73ffffffffffffffffffffffffffffffffffffffff604051917f1400000000000000000000000000000000000000000000000000000000000000602084015260006021840152166041820152604181526080810181811067ffffffffffffffff821117611d0c5760405290565b9460a094604163ffffffff9599989473ffffffffffffffffffffffffffffffffffffffff94895260c060208a01528160c08a015260e089013760006101218801526101408701986040880152606087015216608085015216910152565b93604163ffffffff93608095989793875260a060208801528160a088015260c087013760006101018601526101208501966040860152606085015216910152565b90918015611e65577fff00000000000000000000000000000000000000000000000000000000000000833516928160421161076357602e81013560601c8415612842577f010000000000000000000000000000000000000000000000000000000000000085146127a357602485604051907f9f7697910000000000000000000000000000000000000000000000000000000082526004820152fd5b90918093945060c411610763578260e4116107635761014492808411610763576101481161076357803b15610763576128289360008094604051968795869485937f16604f4f00000000000000000000000000000000000000000000000000000000855282013560e01c9161012481013591608360c4830135920190600487016126c7565b03925af1801561248e576128395750565b611e0090611cf8565b9093508160c493929311610763578260e41161076357826101251161076357610166928084116107635761016a1161076357803b15610763576128289360008094604051968795869485937f1fd1602b00000000000000000000000000000000000000000000000000000000855281013560e01c9161015282013560601c9161010581013591608360c48301359201906004880161266a565b90918015611e65577fff00000000000000000000000000000000000000000000000000000000000000833516928160421161076357602e81013560601c84156129fb577f0100000000000000000000000000000000000000000000000000000000000000851461297657602485604051907f9f7697910000000000000000000000000000000000000000000000000000000082526004820152fd5b90918093945060c411610763578260e4116107635761014492808411610763576101481161076357803b15610763576128289360008094604051968795869485937feb80cd1100000000000000000000000000000000000000000000000000000000855282013560e01c9161012481013591608360c4830135920190600487016126c7565b9093508160c493929311610763578260e41161076357826101251161076357610166928084116107635761016a1161076357803b15610763576128289360008094604051968795869485937ff754fc3500000000000000000000000000000000000000000000000000000000855281013560e01c9161015282013560601c9161010581013591608360c48301359201906004880161266a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543303612abe57565b6382b429006000526004601cfd5b969391849396929591957fffffffffffffffffffffffffffffffffffffffff000000000000000000000000978892604051998a998160208c019d60601b168d5260601b1660348a01526048890152606888015260601b16608886015260f01b7fffff00000000000000000000000000000000000000000000000000000000000016609c850152609e8401378101609e81016000905203607e81018252609e01612b759082611d57565b51902090565b91908115611e65577fff00000000000000000000000000000000000000000000000000000000000000813516928260831015611e65576014608383013560f81c03612c30578260a411610763576084820135612c30578260b0116107635760a482013560a01c612c30578315612c26577f0100000000000000000000000000000000000000000000000000000000000000809414612c195750505090565b612c239350612e14565b90565b612c239350612ca3565b60046040517fe6c4247b000000000000000000000000000000000000000000000000000000008152fd5b60109260209260145260345260446000938480936fa9059cbb00000000000000000000000082525af13d156001835114171615612c9657603452565b6390b8ec1890526004601cfd5b919081604211610763578160831161076357606f81013560601c61016c838111610763578360e41015611e6557604051907f904e0a0100000000000000000000000000000000000000000000000000000000825260e484013560f81c6004830152602082602481865afa91821561248e57600092612da8575b508460c411610763578460e4116107635784610105116107635785612d929361016a86013560f01c93612d8d60018801947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe948a019089018760b08b013560601c60e58c013560c48d01358789612acc565b613074565b15612d9f57505050600090565b612c239261323c565b9091506020813d602011612ddc575b81612dc460209383611d57565b8101031261076357612dd590612075565b9038612d1c565b3d9150612db7565b3d15612e0f573d90612df5826120ee565b91612e036040519384611d57565b82523d6000602084013e565b606090565b90600092806042116107685780608311610768578060c411610768578060e41161076857610104908082116105265761012481811161197757610144948286116105185761014893838511610cab57906020929160405197889384937f34420dbd00000000000000000000000000000000000000000000000000000000855260048501526101006024850152612eaf83850160018b01612096565b9260b08a013560601c604486015260c48a0135606486015260e48a0135608486015289013560a485015288013560c484015287013560e01c60e4830152038188606f88013560601c5af1859481613040575b50612f1b575050505050612c23612f16612de4565b6134d0565b61014a918183116119775783013560f01c9384612f3b575b505050505090565b61015e91808311610518578385013560601c9386810180911161301357808411610cab571161197757823b1561197757908580949392612fdb604051978896879586947f5ea79f260000000000000000000000000000000000000000000000000000000086526004860152606060248601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec60648601930191016120af565b82604483015203925af1801561300857612ff9575b80808080612f33565b61300290611cf8565b38612ff0565b6040513d84823e3d90fd5b6024887f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b9094506020813d60201161306c575b8161305c60209383611d57565b8101031261197757519338612f01565b3d915061304f565b94909293919360009486865260036020526040862093845492600186019273ffffffffffffffffffffffffffffffffffffffff938481541697881561322d578a905573ffffffffffffffffffffffffffffffffffffffff8a164360a01b7fffffffffffffffffffffffff000000000000000000000000000000000000000016179055831692833b156107495786898995604182968e96610144966040519a8b998a987fc86380d9000000000000000000000000000000000000000000000000000000008a5260048a015260248901528c6044890152166064870152608486015260c060a48601528160c486015260e4850137816101258401525af180156132225761320f575b506023810291818304602314821517156131e2577f8ff233d871d7b0eb7094811a82d2a3abe0a4a3ff0066b7f402fbdd5b88f023789594926103e86131cb6131db969461ffff6131d5951690611ec0565b60101c9104612128565b91612c5a565b80a2600190565b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b61321b90959195611cf8565b933861317a565b6040513d88823e3d90fd5b50505050505050505050905090565b90600092806042116107685780608311610768578060e41015613459578060c411610768578060e4116107685761010592818411610526576101259082821161197757610166838111610518578361016a1161051857906020929161332d60405197889485947f13193e6a000000000000000000000000000000000000000000000000000000008652600486015261012060248601526132e3610124860160018b01612096565b9160e48a013560f81c604487015260b08a013560601c606487015260c48a0135608487015260e58a013560a487015289013560c48601526003198583030160e48601528801612096565b9086013560e01c610104830152038187606f87013560601c5af1849381613425575b506133635750505050612c23612f16612de4565b61016e908082116105265761016c83013560f01c938461338557505050505090565b61018291808311610518578385013560601c9386810180911161301357808411610cab571161197757823b1561197757908580949392612fdb604051978896879586947f5ea79f260000000000000000000000000000000000000000000000000000000086526004860152606060248601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec60648601930191016120af565b9093506020813d602011613451575b8161344160209383611d57565b810103126105265751923861334f565b3d9150613434565b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b90602082519201517fffffffffffffffff0000000000000000000000000000000000000000000000009081811693600881106134c157505050565b60080360031b82901b16169150565b6134d990613486565b6135176040517f7c1e66d00000000000000000000000000000000000000000000000000000000060208201526004815261351281611d3b565b613486565b7fffffffffffffffff0000000000000000000000000000000000000000000000009182169190811682146136335781816135806040517f24557f050000000000000000000000000000000000000000000000000000000060208201526004815261351281611d3b565b161461360d576135bf6040517f2c64c1b20000000000000000000000000000000000000000000000000000000060208201526004815261351281611d3b565b16146135e9577f100000000000000000000000000000000000000000000000000000000000000090565b7f130000000000000000000000000000000000000000000000000000000000000090565b50507f120000000000000000000000000000000000000000000000000000000000000090565b50507f11000000000000000000000000000000000000000000000000000000000000009056fea2646970667358221220808e4fc618233f231535d012f6c96d52b17d5442bd4565852c1ba551e5a0ba0664736f6c634300081600330000000000000000000000003c5c5436bca59042cbc835276e51428781366d85000000000000000000000000ca7ca70655639717e97c2d0ed817a5369cca8cce

Deployed Bytecode

0x60a060408181526004918236101561001657600080fd5b600092833560e01c91826311cbf805146119e3575081631e2b8990146119c6578163256929621461197b578163318987c4146116035781634f37a457146115d35781635330a5de1461152057816354d1f13d146114da578163715018a614611479578163898e68ad1461145d5781638da5cb5b1461140957816392006e731461130d57816394aaf73f146112ee578163989e34b31461129d578163ac23728114610f14578163b40f969a14610f19578163ba3c99e614610f14578163bf09469414610eef578163ca9b7b2f14610e5c578163d00689b614610caf578163d6512967146107de578163d94501871461078b578163daf1e1671461076c578163dbaed76b146105d7578163dd4aeedd146103a2578163e5a7783514610376578163e73ae76414610326578163ea901f1a146102e8578163f04e283e1461023c578163f2fde38b146101a5575063fee81cf41461016f57600080fd5b346101a15760206003193601126101a15760209161018b611ad1565b9063389a75e1600c525281600c20549051908152f35b5080fd5b839060206003193601126101a1576101bb611ad1565b906101c4612a94565b8160601b15610231575073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b637448fbae8352601cfd5b8360206003193601126102e557610251611ad1565b610259612a94565b63389a75e1600c528082526020600c2092835442116102da57508173ffffffffffffffffffffffffffffffffffffffff929355167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b636f5e88188352601cfd5b80fd5b5050346101a15760206003193601126101a1576103229061030f61030a611ad1565b6125fd565b9051918291602083526020830190611bae565b0390f35b90503461037257602060031936011261037257906103608161032294610359943581528060205220825193848092611c44565b0383611d57565b51918291602083526020830190611bae565b8280fd5b5050346101a15760209061039b61038c36611af4565b96959095949194939293612135565b9051908152f35b91905034610372576103c9906103b736611af4565b928699948684979c988a959495612acc565b9586885260036020528488209260018401549373ffffffffffffffffffffffffffffffffffffffff908186169081156105a857908b94939291549660a01c90330361052a575b508984526003602052600060018986208281550155808716803b15610526578492836084928d948c5197889687957fb9ff629b00000000000000000000000000000000000000000000000000000000875286015260248501528a60448501528d1660648401525af1801561051c576104fc575b50507f5d002c935f79d7b578b7c23b1101d4508bb3bd36e5b53c2aa00263ee5ac9f0de9461ffff936104f695936104e09360236103e897889216830260101c920204019461015e86020480956104d9823386612c5a565b0391612c5a565b5133815260208101919091529081906040820190565b0390a280f35b6105099095939495611cf8565b61051857863894939294610482565b8680fd5b86513d84823e3d90fd5b8480fd5b909192935043811161054057908a93929161040f565b82889143810390811161057a5760249251917f62141db5000000000000000000000000000000000000000000000000000000008352820152fd5b6011827f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b6024848c8b51917fae029d69000000000000000000000000000000000000000000000000000000008352820152fd5b8383346101a1576101206003193601126101a15767ffffffffffffffff6024358181116107685761060b9036908601611a86565b91906044359073ffffffffffffffffffffffffffffffffffffffff9182811695868203610763576064359284841684036107635760c43594851685036107635760e4359561ffff87168703610763576101043590811161075f576106be916106788c602093369101611a86565b9a90998d8651958694859384937f66ae79e600000000000000000000000000000000000000000000000000000000855280359085015289602485015260448401916120af565b03915afa908115610755578991610716575b50156106ee57506106ea96975060a4359160843591612135565b5080f35b8890517fea66ca6d000000000000000000000000000000000000000000000000000000008152fd5b90506020813d60201161074d575b8161073160209383611d57565b8101031261074957518015158103610749578a6106d0565b8880fd5b3d9150610724565b82513d8b823e3d90fd5b8980fd5b600080fd5b8380fd5b5050346101a157816003193601126101a1576103229061030f306125fd565b9050346103725760206003193601126103725760609282913581526003602052209060018254920154815192835273ffffffffffffffffffffffffffffffffffffffff8116602084015260a01c90820152f35b6003198484610100368401126101a15783359367ffffffffffffffff80861161076857610140868301958736030112610768576024359560ff871687036105265760a4359273ffffffffffffffffffffffffffffffffffffffff928385168095036107635760c4359061ffff821682036107635760e435908111610cab57908899610870899a98999336908601611a86565b9290918a61089d60648901809c3597888152600160205265ffffffffffff80948192205416928392611e94565b1610610c9d5750808c8e8080526001602052205416908160848a01916108c283611e94565b1610610c4b57505060e487016108d781611ea7565b600b3a023a8104600b143a151715610c1f57600a900491906bffffffffffffffffffffffff168211610bc3575050908c9291604161091960448a018096611f02565b905003610b9b578d604161093160248b018093611f02565b905003610b7357918e8e9f928e9f979695939e809e9f889661095291611f02565b9761095d9194611f02565b90818984519a8b976020890160805260805137860191602083018d8152370191604435602084015260f81b7fff00000000000000000000000000000000000000000000000000000000000000169082015260643560418201526084356061820152036061810185526081016109d29085611d57565b7fffff00000000000000000000000000000000000000000000000000000000000080918d519c888e9960208b01527f1400000000000000000000000000000000000000000000000000000000000000978860218c01528160228c01523360428c01525160628b018160805191610a4792611b8b565b8a01976062890152606388015260838701524360e01b7fffffffff000000000000000000000000000000000000000000000000000000001660a387015260f01b1660a78501528260f01b1660a984015260ab830137018a60ab82015203608b8101865260ab01610ab79086611d57565b808952886020528589209261012401610acf90611f53565b8651978896879586957f65a686de000000000000000000000000000000000000000000000000000000008752860194610b0795611f97565b039134907f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85165af18015610b6757610b3d578280f35b81610b5c92903d10610b60575b610b548183611d57565b810190611f68565b8280f35b503d610b4a565b505051903d90823e3d90fd5b878d517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b868c517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b610c1b8d610bd18a93611ea7565b90519384937f6de7824600000000000000000000000000000000000000000000000000000000855284019092916bffffffffffffffffffffffff6020916040840195845216910152565b0390fd5b60248f60118b7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b610c1b8d610c598a93611e94565b90517f6de7824600000000000000000000000000000000000000000000000000000000815265ffffffffffff94851693810193845293166020830152829160400190565b87610c1b8e610c598f611e94565b8780fd5b9050346103725760806003193601126103725780359267ffffffffffffffff9160443583811161037257610ce69036908301611a86565b95909360643590811161076857610d009036908401611a86565b92909473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85163303610e3457610d49886120ee565b610d5588519182611d57565b888152602098898201923682820111610cab578188928c92863783010152519020828552848852610d90610d97888720895192838092611c44565b0382611d57565b88815191012003610e0e577fff000000000000000000000000000000000000000000000000000000000000008761032288610dff60218a8a8a80610ddc81858e612b7b565b87519a16898b015289938585013782019083820152036001810187520185611d57565b51928284938452830190611bae565b85517e3923e0000000000000000000000000000000000000000000000000000000008152fd5b5085517f48f5c3ed000000000000000000000000000000000000000000000000000000008152fd5b90503461037257816003193601126103725735906024359165ffffffffffff8316809303610763577f0f610a3dcd6165f6c2336e534655b0895a80b36322401c17b5bd0eea058ed1b992610eae612a94565b8185526001602052828520817fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000082541617905582519182526020820152a180f35b5050346101a15760209061039b610f0536611af4565b96959095949194939293612acc565b611ab4565b8383346101a15760606003193601126101a1578235906024359367ffffffffffffffff9485811161052657610f519036908301611a86565b91909360443587811161051857610f6b9036908401611a86565b939095610f76612a94565b6041820361127557828852602091888352610f93878a2054611bf1565b61124d5783895288835286892099811161122157610fb18a54611bf1565b99601f8b116111de575b89809b50979899601f83116001146111125760609383611058937f4bdc885fecec9b48853a8df98337140d59c2ebd57b0386bd50133873b969a331979561104a948d91611107575b508260011b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8460031b1c19161790555b8b5195869589875286015260608501916120af565b8281038a840152878b6120af565b0390a173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d851691823b156105265784906110de8751988996879586947fffbeacc700000000000000000000000000000000000000000000000000000000865285015289602485015260448401916120af565b03925af19081156110fe57506110f2575080f35b6110fb90611cf8565b80f35b513d84823e3d90fd5b905083013538611003565b818952848920907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084168a5b8181106111c45750936110589361104a9360609793837f4bdc885fecec9b48853a8df98337140d59c2ebd57b0386bd50133873b969a3319a981061118c575b5050600182811b019055611035565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88560031b161c19908501351690558f8061117d565b8287013584558e9b5060019093019291870191870161113e565b808a52838a20601f830160051c81019b858410611217575b601f0160051c019a5b8b811061120c5750610fbb565b8a81556001016111ff565b909b508b906111f6565b6024896041877f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8487517fb8e35614000000000000000000000000000000000000000000000000000000008152fd5b8386517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b5050346101a157816003193601126101a1576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85168152f35b5050346101a157816003193601126101a1576020906002549051908152f35b8383346101a157816003193601126101a15780805180947f92006e730000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85165afa9283156113fe578280946113bb575b50505173ffffffffffffffffffffffffffffffffffffffff9190911681526020810191909152604090f35b91935091508282813d83116113f7575b6113d58183611d57565b810103126102e5575061032260206113ec83612075565b920151929084611390565b503d6113cb565b5051903d90823e3d90fd5b5050346101a157816003193601126101a15760209073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754915191168152f35b5050346101a157816003193601126101a1576020905160238152f35b83806003193601126102e55761148d612a94565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35580f35b83806003193601126102e55763389a75e1600c52338152806020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c928280a280f35b919050346103725760206003193601126103725781359161153f612a94565b61012c8311156115ac576206270083116115855750816020917f88e95e1ea5dc9ffb42b44903f6dc9ba92e46f0ae1931bd6e6cbeecdf3d6b9a029360025551908152a180f35b90517f3f6368aa000000000000000000000000000000000000000000000000000000008152fd5b90517f6229dcd0000000000000000000000000000000000000000000000000000000008152fd5b905034610372576020600319360112610372578160209365ffffffffffff92358152600185522054169051908152f35b905060031960c0813601126107685781359167ffffffffffffffff8084116119775761014084830193853603011261197757366084116119775760a435908111611977576116549036908301611a86565b926064850190803594858952600160205265ffffffffffff80898b205416808261167d87611e94565b16106119695750898052600160205280898b205416908160848a01916116a283611e94565b161061195b57505060e487016116b781611ea7565b600b3a023a8104600b143a15171561192f57600a900491906bffffffffffffffffffffffff168211611921575050604487019360416116f68685611f02565b9050036118f95760248801604161170d8286611f02565b9050036118d157838361183e93611887979561173f8f8f611735819f9e9d9b9860e899611f02565b9b8c949198611f02565b938492519c8d997f010000000000000000000000000000000000000000000000000000000000000060208c01527f140000000000000000000000000000000000000000000000000000000000000060218c01528560228c01523360428c015260628b0137880192606284019081523701916024356062840152604435608284015260643560a284015260843560c28401527fffffffff000000000000000000000000000000000000000000000000000000004360e01b1660e28401527fffff0000000000000000000000000000000000000000000000000000000000008260f01b1660e684015285830137018c838201520360c8810185520183611d57565b84895288602052611855610124878b209801611f53565b91865197889687967f65a686de0000000000000000000000000000000000000000000000000000000088528701611f97565b03813473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85165af18015610b6757610b3d578280f35b868a517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b8589517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b610c1b8a610bd18993611ea7565b60248c60118a7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b610c1b8a610c598993611e94565b86610c1b8b610c5988611e94565b8580fd5b83806003193601126102e55763389a75e1600c523381526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d8280a280f35b5050346101a157816003193601126101a1576020905161015e8152f35b8490346101a15760606003193601126101a15760443567ffffffffffffffff811161037257611a159036908501611a86565b9173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85163303611a5f57506110fb929335611d98565b807f48f5c3ed00000000000000000000000000000000000000000000000000000000869252fd5b9181601f840112156107635782359167ffffffffffffffff8311610763576020838186019501011161076357565b346107635760006003193601126107635760206040516103e88152f35b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361076357565b9060e06003198301126107635773ffffffffffffffffffffffffffffffffffffffff91600435838116810361076357926024358181168103610763579260443592606435926084359081168103610763579160a43561ffff81168103610763579160c4359067ffffffffffffffff821161076357611b7491600401611a86565b9091565b359065ffffffffffff8216820361076357565b60005b838110611b9e5750506000910152565b8181015183820152602001611b8e565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093611bea81518092818752878088019101611b8b565b0116010190565b90600182811c92168015611c3a575b6020831014611c0b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691611c00565b805460009392611c5382611bf1565b91828252602093600191600181169081600014611cbb5750600114611c7a575b5050505050565b90939495506000929192528360002092846000945b838610611ca757505050500101903880808080611c73565b805485870183015294019385908201611c8f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168685015250505090151560051b010191503880808080611c73565b67ffffffffffffffff8111611d0c57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff821117611d0c57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611d0c57604052565b8215611e65577fff0000000000000000000000000000000000000000000000000000000000000082351680611e025750826001116107635760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611e0094019201906128db565b565b60207fe3d0bbb47fe45a6223645bd4352bb4d9262d1acaa2c0ffd75fa050fca0439b7f91604051908152a1826001116107635760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611e009401920190612708565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b3565ffffffffffff811681036107635790565b356bffffffffffffffffffffffff811681036107635790565b81810292918115918404141715611ed357565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610763570180359067ffffffffffffffff82116107635760200191813603831361076357565b3567ffffffffffffffff811681036107635790565b9190826040910312610763576020825192015190565b35906bffffffffffffffffffffffff8216820361076357565b9395949290611fc692611fb891610140908752806020880152860190611c44565b908482036040860152611bae565b9365ffffffffffff80611fd883611b78565b166060850152611fea60208301611b78565b16608084015260408101359073ffffffffffffffffffffffffffffffffffffffff82168092036107635760a0918285015261202760608201611f7e565b6bffffffffffffffffffffffff80911660c086015261204860808301611f7e565b1660e085015201359067ffffffffffffffff90818316809303610763576101209261010085015216910152565b519073ffffffffffffffffffffffffffffffffffffffff8216820361076357565b9060416080928183526020830137600060618201520190565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b67ffffffffffffffff8111611d0c57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b91908201809211611ed357565b909392979695949561214d818789878d888b89612acc565b98896000526003602052604060002097600189015460a01c80612535575b506201000091808060101b04831481151715611ed3578261ffff8316810311611ed35761ffff8216831461250657604051927f119f8e680000000000000000000000000000000000000000000000000000000084528c600485015273ffffffffffffffffffffffffffffffffffffffff8916602485015286604485015261ffff831690039060101b046064830152602082608481600073ffffffffffffffffffffffffffffffffffffffff89165af191821561248e576000926124d2575b50600189015473ffffffffffffffffffffffffffffffffffffffff81166124a85761229e9060016bffffffffffffffffffffffff61226960025443612128565b868e551660a081901b7fffffffffffffffffffffffff000000000000000000000000000000000000000016331791909c015550565b61040b808302908382041483151715611ed3576103e86040519104606052306040523360601b602c526f23b872dd000000000000000000000000600c52602060006064601c828c5af13d1560016000511417161561249a57600060605260405261230c9061ffff1682611ec0565b60101c90039561231d878688612c5a565b8160021161076357803560f01c9182612398575b50505073ffffffffffffffffffffffffffffffffffffffff93848092604051988952166020880152166040860152606085015216608083015260a0820152827fb2b54dbcb6f6e94446d5dc07448faa5aff2a1fa2ef8280125cbc19a9b9514c4360c03393a3565b806016979394971161076357600282013560601c9360020161ffff8111611ed35761ffff16908160161161076357811161076357833b156107635761244593600092838a604051978895869485937f5ea79f2600000000000000000000000000000000000000000000000000000000855260048501526060602485015260167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea60648601930191016120af565b6001604483015203925af1801561248e5773ffffffffffffffffffffffffffffffffffffffff958693849261247f575b5092829650612331565b61248890611cf8565b38612475565b6040513d6000823e3d90fd5b637939f4246000526004601cfd5b60046040517fd0c27c9f000000000000000000000000000000000000000000000000000000008152fd5b9091506020813d6020116124fe575b816124ee60209383611d57565b8101031261076357519038612229565b3d91506124e1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6bfffffffffffffffffffffffb8110806125f3575b156125b5576bffffffffffffffffffffffff80431691828260048301161015612578575b5050505b3861216b565b6003190116111561258b5738808061256e565b60046040517f695b3a94000000000000000000000000000000000000000000000000000000008152fd5b6bffffffffffffffffffffffff4316036125725760046040517f695b3a94000000000000000000000000000000000000000000000000000000008152fd5b506004811161254a565b73ffffffffffffffffffffffffffffffffffffffff604051917f1400000000000000000000000000000000000000000000000000000000000000602084015260006021840152166041820152604181526080810181811067ffffffffffffffff821117611d0c5760405290565b9460a094604163ffffffff9599989473ffffffffffffffffffffffffffffffffffffffff94895260c060208a01528160c08a015260e089013760006101218801526101408701986040880152606087015216608085015216910152565b93604163ffffffff93608095989793875260a060208801528160a088015260c087013760006101018601526101208501966040860152606085015216910152565b90918015611e65577fff00000000000000000000000000000000000000000000000000000000000000833516928160421161076357602e81013560601c8415612842577f010000000000000000000000000000000000000000000000000000000000000085146127a357602485604051907f9f7697910000000000000000000000000000000000000000000000000000000082526004820152fd5b90918093945060c411610763578260e4116107635761014492808411610763576101481161076357803b15610763576128289360008094604051968795869485937f16604f4f00000000000000000000000000000000000000000000000000000000855282013560e01c9161012481013591608360c4830135920190600487016126c7565b03925af1801561248e576128395750565b611e0090611cf8565b9093508160c493929311610763578260e41161076357826101251161076357610166928084116107635761016a1161076357803b15610763576128289360008094604051968795869485937f1fd1602b00000000000000000000000000000000000000000000000000000000855281013560e01c9161015282013560601c9161010581013591608360c48301359201906004880161266a565b90918015611e65577fff00000000000000000000000000000000000000000000000000000000000000833516928160421161076357602e81013560601c84156129fb577f0100000000000000000000000000000000000000000000000000000000000000851461297657602485604051907f9f7697910000000000000000000000000000000000000000000000000000000082526004820152fd5b90918093945060c411610763578260e4116107635761014492808411610763576101481161076357803b15610763576128289360008094604051968795869485937feb80cd1100000000000000000000000000000000000000000000000000000000855282013560e01c9161012481013591608360c4830135920190600487016126c7565b9093508160c493929311610763578260e41161076357826101251161076357610166928084116107635761016a1161076357803b15610763576128289360008094604051968795869485937ff754fc3500000000000000000000000000000000000000000000000000000000855281013560e01c9161015282013560601c9161010581013591608360c48301359201906004880161266a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543303612abe57565b6382b429006000526004601cfd5b969391849396929591957fffffffffffffffffffffffffffffffffffffffff000000000000000000000000978892604051998a998160208c019d60601b168d5260601b1660348a01526048890152606888015260601b16608886015260f01b7fffff00000000000000000000000000000000000000000000000000000000000016609c850152609e8401378101609e81016000905203607e81018252609e01612b759082611d57565b51902090565b91908115611e65577fff00000000000000000000000000000000000000000000000000000000000000813516928260831015611e65576014608383013560f81c03612c30578260a411610763576084820135612c30578260b0116107635760a482013560a01c612c30578315612c26577f0100000000000000000000000000000000000000000000000000000000000000809414612c195750505090565b612c239350612e14565b90565b612c239350612ca3565b60046040517fe6c4247b000000000000000000000000000000000000000000000000000000008152fd5b60109260209260145260345260446000938480936fa9059cbb00000000000000000000000082525af13d156001835114171615612c9657603452565b6390b8ec1890526004601cfd5b919081604211610763578160831161076357606f81013560601c61016c838111610763578360e41015611e6557604051907f904e0a0100000000000000000000000000000000000000000000000000000000825260e484013560f81c6004830152602082602481865afa91821561248e57600092612da8575b508460c411610763578460e4116107635784610105116107635785612d929361016a86013560f01c93612d8d60018801947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe948a019089018760b08b013560601c60e58c013560c48d01358789612acc565b613074565b15612d9f57505050600090565b612c239261323c565b9091506020813d602011612ddc575b81612dc460209383611d57565b8101031261076357612dd590612075565b9038612d1c565b3d9150612db7565b3d15612e0f573d90612df5826120ee565b91612e036040519384611d57565b82523d6000602084013e565b606090565b90600092806042116107685780608311610768578060c411610768578060e41161076857610104908082116105265761012481811161197757610144948286116105185761014893838511610cab57906020929160405197889384937f34420dbd00000000000000000000000000000000000000000000000000000000855260048501526101006024850152612eaf83850160018b01612096565b9260b08a013560601c604486015260c48a0135606486015260e48a0135608486015289013560a485015288013560c484015287013560e01c60e4830152038188606f88013560601c5af1859481613040575b50612f1b575050505050612c23612f16612de4565b6134d0565b61014a918183116119775783013560f01c9384612f3b575b505050505090565b61015e91808311610518578385013560601c9386810180911161301357808411610cab571161197757823b1561197757908580949392612fdb604051978896879586947f5ea79f260000000000000000000000000000000000000000000000000000000086526004860152606060248601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec60648601930191016120af565b82604483015203925af1801561300857612ff9575b80808080612f33565b61300290611cf8565b38612ff0565b6040513d84823e3d90fd5b6024887f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b9094506020813d60201161306c575b8161305c60209383611d57565b8101031261197757519338612f01565b3d915061304f565b94909293919360009486865260036020526040862093845492600186019273ffffffffffffffffffffffffffffffffffffffff938481541697881561322d578a905573ffffffffffffffffffffffffffffffffffffffff8a164360a01b7fffffffffffffffffffffffff000000000000000000000000000000000000000016179055831692833b156107495786898995604182968e96610144966040519a8b998a987fc86380d9000000000000000000000000000000000000000000000000000000008a5260048a015260248901528c6044890152166064870152608486015260c060a48601528160c486015260e4850137816101258401525af180156132225761320f575b506023810291818304602314821517156131e2577f8ff233d871d7b0eb7094811a82d2a3abe0a4a3ff0066b7f402fbdd5b88f023789594926103e86131cb6131db969461ffff6131d5951690611ec0565b60101c9104612128565b91612c5a565b80a2600190565b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b61321b90959195611cf8565b933861317a565b6040513d88823e3d90fd5b50505050505050505050905090565b90600092806042116107685780608311610768578060e41015613459578060c411610768578060e4116107685761010592818411610526576101259082821161197757610166838111610518578361016a1161051857906020929161332d60405197889485947f13193e6a000000000000000000000000000000000000000000000000000000008652600486015261012060248601526132e3610124860160018b01612096565b9160e48a013560f81c604487015260b08a013560601c606487015260c48a0135608487015260e58a013560a487015289013560c48601526003198583030160e48601528801612096565b9086013560e01c610104830152038187606f87013560601c5af1849381613425575b506133635750505050612c23612f16612de4565b61016e908082116105265761016c83013560f01c938461338557505050505090565b61018291808311610518578385013560601c9386810180911161301357808411610cab571161197757823b1561197757908580949392612fdb604051978896879586947f5ea79f260000000000000000000000000000000000000000000000000000000086526004860152606060248601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec60648601930191016120af565b9093506020813d602011613451575b8161344160209383611d57565b810103126105265751923861334f565b3d9150613434565b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b90602082519201517fffffffffffffffff0000000000000000000000000000000000000000000000009081811693600881106134c157505050565b60080360031b82901b16169150565b6134d990613486565b6135176040517f7c1e66d00000000000000000000000000000000000000000000000000000000060208201526004815261351281611d3b565b613486565b7fffffffffffffffff0000000000000000000000000000000000000000000000009182169190811682146136335781816135806040517f24557f050000000000000000000000000000000000000000000000000000000060208201526004815261351281611d3b565b161461360d576135bf6040517f2c64c1b20000000000000000000000000000000000000000000000000000000060208201526004815261351281611d3b565b16146135e9577f100000000000000000000000000000000000000000000000000000000000000090565b7f130000000000000000000000000000000000000000000000000000000000000090565b50507f120000000000000000000000000000000000000000000000000000000000000090565b50507f11000000000000000000000000000000000000000000000000000000000000009056fea2646970667358221220808e4fc618233f231535d012f6c96d52b17d5442bd4565852c1ba551e5a0ba0664736f6c63430008160033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85000000000000000000000000ca7ca70655639717e97c2d0ed817a5369cca8cce

-----Decoded View---------------
Arg [0] : GARP_ (address): 0x3C5C5436BCa59042cBC835276E51428781366d85
Arg [1] : defaultOwner (address): 0xCa7ca70655639717e97C2D0eD817A5369cCA8cCE

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85
Arg [1] : 000000000000000000000000ca7ca70655639717e97c2d0ed817a5369cca8cce


Deployed Bytecode Sourcemap

1949:52777:9:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;:::i;:::-;11885:237:6;;;;;;;;;1949:52777:9;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;:::i;:::-;12478:70:6;;;:::i;:::-;8479:183;;;;;;6813:405;;;;;;;;1949:52777:9;6813:405:6;;;1949:52777:9;;8479:183:6;;;;;;1949:52777:9;;;-1:-1:-1;;1949:52777:9;;;;;;;:::i;:::-;12478:70:6;;:::i;:::-;10506:526;;;;;;1949:52777:9;10506:526:6;;;;;;;;;;;6813:405;10506:526;;;6813:405;;;;;;1949:52777:9;6813:405:6;;;1949:52777:9;;10506:526:6;;;;;;1949:52777:9;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;46832:193;1949:52777;;;;:::i;:::-;46832:193;;;;;;;;;;;;;;:::i;:::-;1949:52777;;;;47082:19;1949:52777;;;;;47238:24;;;;1949:52777;;;;;;;47276:27;;;47272:74;;1949:52777;;;;;;;;;;47803:10;;:27;47799:220;;1949:52777;;;;;47082:19;1949:52777;;;47238:24;1949:52777;;;;;;;;;;;48229:95;;;;;1949:52777;;;;;;;;;48229:95;;;;;1624:4:10;48229:95:9;;;;1949:52777;;;;;;;;;;;;;;;;48229:95;;;;;;;;1949:52777;6200:4;;49841:115;6200:4;1949:52777;6200:4;49841:115;6200:4;;49810:10;6200:4;6100:2;6200:4;1949:52777;;;;;;48685:2;1949:52777;;;;;;6397:3;1949:52777;;;47803:10;;49587:11;47803:10;;49587:11;;:::i;:::-;1949:52777;49810:10;;:::i;:::-;1949:52777;47803:10;1949:52777;;;;;;;;;;;;;;;;;49841:115;;;;1949:52777;;48229:95;;;;;;;;:::i;:::-;1949:52777;;48229:95;;;;;;;;1949:52777;;;;48229:95;1949:52777;;1624:4:10;1949:52777:9;;1624:4:10;;;;48229:95:9;1949:52777;;;47799:220;47940:12;;;;;;47915:37;;47911:97;;47799:220;;;;;;;47911:97;47940:12;;;;1949:52777;;;;;;;;;;47961:47;;;;;;1949:52777;47961:47;1949:52777;;;;;;;;;;47272:74;1949:52777;;;;;47312:34;;;;;;1949:52777;47312:34;1949:52777;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;36097:75;;;;;;;1624:4:10;36097:75:9;;1949:52777;;36097:75;;;1949:52777;;;;;;;;;;;:::i;:::-;36097:75;;;;;;;;;;;;;1949:52777;36096:76;;36092:108;;1949:52777;36211:179;1949:52777;;;;;;;;36211:179;;:::i;:::-;;1949:52777;;36092:108;1949:52777;;;36181:19;;;;36097:75;;;1949:52777;36097:75;;1949:52777;36097:75;;;;;;1949:52777;36097:75;;;:::i;:::-;;;1949:52777;;;;;;;;;;;;36097:75;;;1949:52777;;;;36097:75;;;-1:-1:-1;36097:75:9;;;1949:52777;;1624:4:10;1949:52777:9;;1624:4:10;;;;1949:52777:9;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;918:4:5;895:29;918:4;895:29;:::i;1949:52777:9:-;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;7377:66;1949:52777;;;;;;;7377:66;;1949:52777;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9860:26;;;;10009:24;1949:52777;9860:26;;1949:52777;;;;;;;;;;;;;;;;;;10009:24;;;;:::i;:::-;1949:52777;10009:48;10005:129;;1949:52777;;;;;;;;;;;;;10202:19;;1949:52777;10202:19;;;;;;:::i;:::-;1949:52777;10202:35;10198:103;;10440:23;;1949:52777;10440:23;;;;;:::i;:::-;10480:2;10466:11;1949:52777;10466:11;1949:52777;;10480:2;1949:52777;10466:11;1949:52777;;;;;10485:2;1949:52777;;;;;;10440:47;-1:-1:-1;10436:127:9;;10656:26;;;;;;247:2:5;10656:26:9;1949:52777;10656:26;;;;;:::i;:::-;219:30:5;;;10637:78:9;;10764:24;247:2:5;10764:24:9;1949:52777;10764:24;;;;;:::i;:::-;219:30:5;;;10745:76:9;;16581:24;;;;;;;;;;;;;;;;;;;;:::i;:::-;16667:26;;;;;:::i;:::-;1949:52777;;;;;1580:4:10;;;1949:52777:9;1580:4:10;;;;1624;;;;;;1949:52777:9;1624:4:10;;;;;;;1949:52777:9;;;;1624:4:10;;1949:52777:9;;;;;1580:4:10;;;1949:52777:9;;;247:2:5;1580:4:10;;1949:52777:9;;;1580:4:10;;;1949:52777:9;1580:4:10;;;;;;;;;;;;:::i;:::-;1624;1949:52777:9;;;;1580:4:10;;;;1949:52777:9;1580:4:10;;1949:52777:9;;1580:4:10;;;;;1949:52777:9;1580:4:10;;;;1949:52777:9;16455:10;1580:4:10;;;1949:52777:9;;1580:4:10;;;;;;;;;;:::i;:::-;;;;;;;1949:52777:9;1580:4:10;;;1949:52777:9;1580:4:10;;;1949:52777:9;17261:12;1949:52777;1624:4:10;;;1580;;;1624;;;;1580;;;1624;;;;;1580;;;1624;1580;;;1624;;;1580;1624;;;1580;;;;;;;;;;;;:::i;:::-;1949:52777:9;;;;;;;;;17791:25;;;;;;:::i;:::-;1949:52777;;17554:272;;;;;;;1624:4:10;17554:272:9;;;;;;;;:::i;:::-;;17580:9;;17554:4;;1949:52777;17554:272;;;;;;;;1949:52777;;;17554:272;;;;;;-1:-1:-1;17554:272:9;;;;;;;:::i;:::-;;;;;:::i;:::-;1949:52777;;;17554:272;;;;;;1949:52777;;;1624:4:10;;;;;;;;10745:76:9;1949:52777;;;10798:23;;;;10637:78;1949:52777;;;10692:23;;;;10436:127;10496:67;10539:23;;;;;:::i;:::-;1949:52777;;10496:67;;;;;;;;1949:52777;;;;;;;;;;;;;;;;;10496:67;;;;1949:52777;;;;;;;;;;10198:103;10246:55;10281:19;;;;;:::i;:::-;1949:52777;;10246:55;;;1949:52777;;;;10246:55;;;1949:52777;;;;;;;;;;;;;;;10005:129;10109:24;10066:68;10109:24;;;;:::i;1949:52777::-;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;10902:4;;;1949:52777;10902:4;1949:52777;10880:10;:27;10876:55;;1949:52777;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;1624:4:10;;;;;;;;;;;1949:52777:9;11066:26;;1949:52777;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;11096:69;11066:99;11062:138;;1949:52777;26764:42;1949:52777;26764:42;1949:52777;;26764:42;;;;;;;;;:::i;:::-;1949:52777;;;;;;;;;;;;;1624:4:10;;;;;;;;1949:52777:9;;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;11062:138::-;1949:52777;;11174:26;;;;10876:55;1949:52777;;;10916:15;;;;1949:52777;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;8070:34;12478:70:6;;;:::i;:::-;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;8070:34;1949:52777;;;;;;;;;;35257:193;1949:52777;;;:::i;:::-;35257:193;;;;;;;;;;;:::i;1949:52777::-;;:::i;:::-;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;12478:70:6;;;;;:::i;:::-;247:2:5;219:30;;335:69;;1949:52777:9;;;;;;;;;;;;;;:::i;:::-;13543:96;;1949:52777;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;13761:63;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;13761:63;;;1949:52777;13886:4;1949:52777;13886:57;;;;;;1949:52777;;;;;13886:57;;;;;;;1624:4:10;13886:57:9;;;;1949:52777;;;;;;;;;;;:::i;:::-;13886:57;;;;;;;;;;;;1949:52777;;;13886:57;;;;:::i;:::-;1949:52777;;13886:57;1949:52777;1624:4:10;1949:52777:9;;1624:4:10;;;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13761:63;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1949:52777:9;;;;;;;247:2:5;1949:52777:9;;;;;;13543:96;1949:52777;;;13620:19;;;;335:69:5;1949:52777:9;;;381:23:5;;;;1949:52777:9;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;5558:48;1949:52777;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;7135:78;1949:52777;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;11548:29;;1624:4:10;11548:29:9;;:4;1949:52777;11548:4;1949:52777;11548:29;;;;;;;;1949:52777;11548:29;;;1949:52777;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;11548:29;;;;;;;;;;;;;;;;;;;:::i;:::-;;;1949:52777;;;;;;;;;;:::i;:::-;;;1624:4:10;11548:29:9;;;;;;;;;;;1949:52777;;1624:4:10;;;;;;;;1949:52777:9;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;11523:61:6;1949:52777:9;11523:61:6;;1949:52777:9;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;6100:2;1949:52777;;;;;;-1:-1:-1;;1949:52777:9;;;;;12478:70:6;;:::i;:::-;6813:405;;;;;;;;;;1949:52777:9;;;;;-1:-1:-1;;1949:52777:9;;;;;9831:339:6;;;;;;;;;;;;;;;;1949:52777:9;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;12478:70:6;;;:::i;:::-;5450:20:9;8888:57;;;8884:101;;5199:19;9110:56;;9106:99;;5199:19;;1949:52777;5199:19;9290:47;5199:19;9216:50;5199:19;1949:52777;;;;9290:47;1949:52777;;9106:99;1949:52777;;9175:30;;;;8884:101;1949:52777;;8954:31;;;;1949:52777;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9860:26;;;;1949:52777;;;;;;;;;;;;;;;;;10009:24;;;;;:::i;:::-;1949:52777;10009:48;10005:129;;1949:52777;;;;;;;;;;;;;10202:19;;1949:52777;10202:19;;;;;;:::i;:::-;1949:52777;10202:35;10198:103;;10440:23;;;;;;;;:::i;:::-;10480:2;10466:11;1949:52777;10466:11;1949:52777;;10480:2;1949:52777;10466:11;1949:52777;;;;;10485:2;1949:52777;;;;;;10440:47;-1:-1:-1;10436:127:9;;10656:26;;1949:52777;10656:26;;;247:2:5;10656:26:9;;;;:::i;:::-;219:30:5;;;10637:78:9;;1949:52777;10764:24;;247:2:5;10764:24:9;;;;:::i;:::-;219:30:5;;;10745:76:9;;20142:24;;1624:4:10;20142:24:9;20543:272;20142:24;;20222:26;20142:24;;;;;;;;;1624:4:10;20142:24:9;;:::i;:::-;20222:26;;;;;;:::i;:::-;1949:52777;;;;1624:4:10;;;1949:52777:9;;1624:4:10;;1949:52777:9;;1624:4:10;;;1949:52777:9;1624:4:10;;;;1949:52777:9;20047:10;1624:4:10;;;1949:52777:9;1624:4:10;;;;;;;;;;;;;;;1949:52777:9;;;1624:4:10;;;1949:52777:9;;1624:4:10;;;;1949:52777:9;9860:26;1624:4:10;;;;1949:52777:9;;;1624:4:10;;;1949:52777:9;1624:4:10;20439:12:9;1949:52777;1624:4:10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1949:52777:9;;;;;;20780:25;;1949:52777;;;20780:25;;;:::i;:::-;1949:52777;;;20543:272;;;;;1624:4:10;20543:272:9;;;;;:::i;:::-;;20569:9;;1949:52777;20543:4;1949:52777;20543:272;;;;;;;;1949:52777;;;10745:76;1949:52777;;;10798:23;;;;10637:78;1949:52777;;;10692:23;;;;10436:127;10496:67;10539:23;;;;;:::i;1949:52777::-;;;;;;;;;;10198:103;10246:55;10281:19;;;;;:::i;10005:129::-;10109:24;10066:68;10109:24;;;;:::i;1949:52777::-;;;;;;;-1:-1:-1;;1949:52777:9;;;;;9239:383:6;;;;;;7972:9;9132:15;1949:52777:9;9239:383:6;;;;;;;;;1949:52777:9;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;6397:3;1949:52777;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;:::i;:::-;10902:4;1949:52777;10902:4;1949:52777;10880:10;:27;10876:55;;1949:52777;25200:928;1949:52777;;;25200:928;:::i;10876:55::-;10916:15;;;;;;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;6200:4;1949:52777;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;-1:-1:-1;;1949:52777:9;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;-1:-1:-1;1949:52777:9;;;;;-1:-1:-1;1949:52777:9;;;-1:-1:-1;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;1949:52777:9;;;;;;;;-1:-1:-1;1949:52777:9;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;25200:928::-;1949:52777;;;;;25720:18;;1949:52777;25752:18;25748:240;;1949:52777;;26117:1;1949:52777;;;26117:1;1949:52777;26061:60;1949:52777;;;;26061:60;;:::i;:::-;25200:928::o;25748:240::-;1949:52777;25791:22;1949:52777;;;;;;25791:22;1949:52777;25973:1;1949:52777;;;25973:1;1949:52777;25917:60;1949:52777;;;;25917:60;;:::i;1949:52777::-;;25736:1;1949:52777;;;;;25736:1;1949:52777;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1624:4:10:-;;1949:52777:9;;;1624:4:10;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::o;:::-;;1949:52777:9;;;;;;;;1624:4:10:o;:::-;;;;;;;;;;;1949:52777:9;;;1624:4:10;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;1949:52777:9;;;;;;:::i;:::-;;1624:4:10;;;1949:52777:9;;1624:4:10;;;1949:52777:9;:::i;:::-;;1624:4:10;;;1949:52777:9;1624:4:10;;;1949:52777:9;;;;;;;;;;1624:4:10;;;;;1949:52777:9;1624:4:10;;;;;:::i;:::-;1949:52777:9;;;;1624:4:10;;;1949:52777:9;1624:4:10;;;;;:::i;:::-;1949:52777:9;1624:4:10;;;1949:52777:9;1624:4:10;;1949:52777:9;;;;;;1624:4:10;;;;;;;;;;;1949:52777:9;1624:4:10;;;:::o;1949:52777:9:-;;;;;;;;;;:::o;:::-;;;;;;;;;;;1624:4:10;-1:-1:-1;1624:4:10;;;;1949:52777:9;;:::o;:::-;;;;;;;;;;;;;;1624:4:10;-1:-1:-1;1624:4:10;;;;;;1949:52777:9;;;;;:::o;:::-;;;;;;;;;;;;;:::o;5953:1::-;;;;;;;;;;:::o;38277:8063::-;;;;;;;;;39191:193;;;;;;;;;;:::i;:::-;1949:52777;;-1:-1:-1;1949:52777:9;41523:19;1949:52777;;;-1:-1:-1;1949:52777:9;41695:22;;;;1949:52777;;;41735:19;41731:1189;;38277:8063;43456:7;;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;43327:239;1624:4:10;43327:239:9;;;;;;1949:52777;;;;5953:1;;;1949:52777;5953:1;;;;1949:52777;;;;;;;;;;5953:1;;;1949:52777;;;5953:1;1949:52777;-1:-1:-1;1949:52777:9;;;43327:239;;;;;;;-1:-1:-1;43327:239:9;;;38277:8063;41695:22;;;;1949:52777;;;;43909:76;;44260:41;1949:52777;41695:22;1949:52777;44029:56;44061:23;1949:52777;44037:12;44029:56;:::i;:::-;5199:19;;;1949:52777;5953:1;;;;;;44240:10;5953:1;41695:22;;;;5953:1;-1:-1:-1;5953:1:9;44260:41;5953:1;1949:52777;;;;;;;;;;;;;;6200:4;1949:52777;8326:988:8;1949:52777:9;;5953:1;8326:988:8;44834:4:9;1949:52777;8326:988:8;44240:10:9;5953:1;8326:988:8;;;;;;1949:52777:9;-1:-1:-1;5953:1:9;8326:988:8;;;;;;;41695:22:9;-1:-1:-1;8326:988:8;;;;;;;-1:-1:-1;5953:1:9;8326:988:8;1949:52777:9;8326:988:8;45052:49:9;;1949:52777;;45052:49;;:::i;:::-;1949:52777;;;;45426:15;;;;;;:::i;:::-;1949:52777;44061:23;1949:52777;;;5953:1;;;;45824:19;;45820:273;;38277:8063;1949:52777;;;;;;;;;;;;;;;5953:1;;1949:52777;;;5953:1;;1949:52777;5953:1;;;1949:52777;;5953:1;;;1949:52777;;5953:1;;1949:52777;44240:10;46116:217;5953:1;44240:10;46116:217;;38277:8063::o;45820:273::-;1949:52777;45904:4;1949:52777;;;;;;;44061:23;1949:52777;;5953:1;;;;44061:23;5953:1;1949:52777;5953:1;;;;1949:52777;;;;45904:4;1949:52777;;;;;;;45999:83;;;;;5953:1;1949:52777;-1:-1:-1;1949:52777:9;;;;;45999:83;;;;;;;1624:4:10;45999:83:9;;43327:239;45999:83;;1949:52777;5953:1;;;;;45904:4;1949:52777;5953:1;;;1949:52777;;;;5953:1;:::i;:::-;41695:22;5953:1;;;;45999:83;;;;;;;;1949:52777;45999:83;;;;;;;45820:273;;;;;;;;45999:83;;;;:::i;:::-;;;;;1949:52777;;1624:4:10;-1:-1:-1;1624:4:10;;;;;8326:988:8;;-1:-1:-1;8326:988:8;43327:239:9;8326:988:8;;43909:76:9;43327:239;1949:52777;;43960:25;;;;43327:239;;;;1949:52777;43327:239;;1949:52777;43327:239;;;;;;1949:52777;43327:239;;;:::i;:::-;;;5953:1;;;;1624:4:10;43327:239:9;;;;;;;-1:-1:-1;43327:239:9;;1949:52777;;-1:-1:-1;1949:52777:9;;;;;-1:-1:-1;1949:52777:9;41731:1189;5953:1;42053:50;;42052:88;;;41731:1189;42048:858;;;1949:52777;42405:12;;1949:52777;5953:1;;;;;;1949:52777;42364:54;;42360:404;;42048:858;;;;;41731:1189;;;42360:404;-1:-1:-1;;5953:1:9;1949:52777;42652:54;;42648:93;;42360:404;;;;;42648:93;5953:1;1949:52777;;42715:26;;;;42048:858;1949:52777;42839:12;1949:52777;42814:38;42048:858;42810:77;5953:1;1949:52777;;42861:26;;;;42052:88;42109:30;5953:1;42109:30;;42052:88;;428:387:5;1949:52777:9;;;;;;;;;650:1:5;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;428:387:5;:::o;3289:3:10:-;;;;1949:52777:9;1624:4:10;3289:3;;;;1949:52777:9;3289:3:10;1949:52777:9;;3289:3:10;;;;;;;;;1949:52777:9;;;;1624:4:10;-1:-1:-1;1624:4:10;;;;1949:52777:9;;;3289:3:10;;;;1949:52777:9;;3289:3:10;;1949:52777:9;;3289:3:10;;;1949:52777:9;1624:4:10;3289:3;;;:::o;4053:::-;;1949:52777:9;1624:4:10;4053:3;;;;;;1949:52777:9;;4053:3:10;;;;;;;;;1949:52777:9;;;;1624:4:10;-1:-1:-1;1624:4:10;;;;1949:52777:9;;;4053:3:10;;;;1949:52777:9;;4053:3:10;;1949:52777:9;1624:4:10;4053:3;;3289;4053:::o;23010:1909:9:-;;;1949:52777;;;;;23124:17;;1949:52777;;;2023:2:10;1949:52777:9;;;1925:2:10;1949:52777:9;;5953:1;;;23320:26;;23316:758;;1949:52777;24087:30;;24083:660;;1949:52777;;;;24889:23;;;;;;;1949:52777;24889:23;24083:660;1949:52777;;;;;;2478:3:10;1949:52777:9;;;;2565:3:10;1949:52777:9;;;3956:3:10;1949:52777:9;;;;;;4053:3:10;1949:52777:9;;;24140:592;;;;;;1949:52777;1801:1:10;1949:52777:9;;;;24140:592;;;;;;;1624:4:10;24140:592:9;;1949:52777;;3289:3:10;1949:52777:9;;;3908:3:10;1949:52777:9;;2565:3:10;1949:52777:9;2293:3:10;2478;1949:52777:9;;2565:3:10;1949:52777:9;;24140:592;1949:52777;24140:592;;;:::i;:::-;;;;;;;;;;;24133:599;:::o;24140:592::-;;;;:::i;23316:758::-;1949:52777;;;;2478:3:10;1949:52777:9;;;;;;;2565:3:10;1949:52777:9;;;;2941:3:10;1949:52777:9;;;3192:3:10;1949:52777:9;;;;;;3289:3:10;1949:52777:9;;;23369:694;;;;;;1949:52777;1801:1:10;1949:52777:9;;;;23369:694;;;;;;;1624:4:10;23369:694:9;;1949:52777;;3289:3:10;1949:52777:9;;;3088:3:10;1949:52777:9;;5953:1;;;1949:52777;2893:3:10;1949:52777:9;;2565:3:10;1949:52777:9;2293:3:10;2478;1949:52777:9;;2565:3:10;1949:52777:9;;23369:694;1949:52777;23369:694;;;:::i;20948:1936::-;;;1949:52777;;;;;21062:17;;1949:52777;;;2023:2:10;1949:52777:9;;;1925:2:10;1949:52777:9;;5953:1;;;21258:26;;21254:771;;1949:52777;22038:30;;22034:674;;1949:52777;;;;22854:23;;;;;;;1949:52777;22854:23;22034:674;1949:52777;;;;;;2478:3:10;1949:52777:9;;;;2565:3:10;1949:52777:9;;;3956:3:10;1949:52777:9;;;;;;4053:3:10;1949:52777:9;;;22084:593;;;;;;1949:52777;1801:1:10;1949:52777:9;;;;22084:593;;;;;;;1624:4:10;22084:593:9;;1949:52777;;3289:3:10;1949:52777:9;;;3908:3:10;1949:52777:9;;2565:3:10;1949:52777:9;2293:3:10;2478;1949:52777:9;;2565:3:10;1949:52777:9;;22084:593;1949:52777;22084:593;;;:::i;21254:771::-;1949:52777;;;;2478:3:10;1949:52777:9;;;;;;;2565:3:10;1949:52777:9;;;;2941:3:10;1949:52777:9;;;3192:3:10;1949:52777:9;;;;;;3289:3:10;1949:52777:9;;;21300:694;;;;;;1949:52777;1801:1:10;1949:52777:9;;;;21300:694;;;;;;;1624:4:10;21300:694:9;;1949:52777;;3289:3:10;1949:52777:9;;;3088:3:10;1949:52777:9;;5953:1;;;1949:52777;2893:3:10;1949:52777:9;;2565:3:10;1949:52777:9;2293:3:10;2478;1949:52777:9;;2565:3:10;1949:52777:9;;21300:694;1949:52777;21300:694;;;:::i;7292:355:6:-;7390:251;;;;;;7292:355::o;7390:251::-;;;;;;;34235:612:9;;;;;;;;;;;5953:1;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:4:10;;;;1949:52777:9;;;1624:4:10;1949:52777:9;;;1624:4:10;;;1949:52777:9;1624:4:10;;-1:-1:-1;1624:4:10;;1949:52777:9;;;;;;;;;;;;:::i;:::-;;34541:299;;34235:612;:::o;27234:1587::-;;;1949:52777;;;;;27383:17;;1949:52777;;;2293:3:10;1949:52777:9;;;;27553:2;2293:3:10;1949:52777:9;;27521:27;1949:52777;;27515:40;27511:69;;1949:52777;5953:1;1949:52777;;;2336:3:10;1949:52777:9;;2565:3:10;27615:94:9;;1949:52777;2379:3:10;1949:52777:9;;;5953:1;1949:52777;;2379:3:10;1949:52777:9;;27751:97;;28198:26;;28194:125;;1949:52777;28332:30;;;28328:133;;28785:29;;;27234:1587;:::o;28328:133::-;28403:47;;;;:::i;:::-;28378:72;:::o;28194:125::-;28265:43;;;;:::i;27751:97::-;27832:16;1949:52777;;27832:16;;;;11375:939:8;11502:806;11375:939;11502:806;11375:939;11502:806;;;;;;;;;;;;;;;;;;;;;;;;;;;;11375:939::o;11502:806::-;;;;;;;52549:2175:9;;;1949:52777;2023:2:10;1949:52777:9;;;;2293:3:10;1949:52777:9;;;2150:3:10;1949:52777:9;;5953:1;;;3435:3:10;1949:52777:9;;;;;;2565:3:10;1949:52777:9;;;;;;53238:54;1624:4:10;53238:54:9;;2565:3:10;1949:52777:9;;53180:29;1949:52777;;53238:54;;;2747:3:10;53238:54:9;;2747:3:10;53238:54:9;;;;;;;;;-1:-1:-1;53238:54:9;;;52549:2175;1949:52777;;2478:3:10;1949:52777:9;;;;2565:3:10;1949:52777:9;;;;2893:3:10;1949:52777:9;;;;54096:181;1949:52777;3289:3:10;1949:52777:9;;5953:1;;;1949:52777;53780:189;1843:1:10;1949:52777:9;;;;;;;;;;2379:3:10;1949:52777:9;;5953:1;;;2796:3:10;1949:52777:9;;2565:3:10;2478;1949:52777:9;;2565:3:10;53780:189:9;;;:::i;:::-;54096:181;:::i;:::-;54292:17;54288:205;;54688:29;;;-1:-1:-1;52549:2175:9;:::o;54288:205::-;54431:51;;;:::i;53238:54::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;2747:3:10;;;;1949:52777:9;;;:::i;:::-;53238:54;;;;;;;-1:-1:-1;53238:54:9;;4199:3:10;;;;;;;;;;:::i;:::-;1949:52777:9;;;;;;;:::i;:::-;4199:3:10;;;-1:-1:-1;4199:3:10;;;;:::o;:::-;1949:52777:9;4199:3:10;:::o;31380:2456:9:-;;-1:-1:-1;1949:52777:9;;2023:2:10;1949:52777:9;;;;2293:3:10;1949:52777:9;;;;2478:3:10;1949:52777:9;;;;2565:3:10;1949:52777:9;;;3762:3:10;1949:52777:9;;;;;;3908:3:10;1949:52777:9;;;;;3956:3:10;1949:52777:9;;;;;;4053:3:10;1949:52777:9;;;;;;;;;;;;31826:862;;;;;1624:4:10;31826:862:9;;1949:52777;31826:862;;1949:52777;3859:3:10;;;;;;;;;1843:1;1949:52777:9;;3859:3:10;:::i;:::-;1949:52777:9;2379:3:10;1949:52777:9;;5953:1;;;3859:3:10;;;1949:52777:9;2478:3:10;1949:52777:9;;2565:3:10;3859;;;1949:52777:9;2565:3:10;1949:52777:9;;2565:3:10;3859;;;1949:52777:9;;;2565:3:10;3859;;;1949:52777:9;;;2565:3:10;2478;3859;;1949:52777:9;;;3289:3:10;1949:52777:9;;2565:3:10;3859;;3289;31826:862:9;1949:52777;;2150:3:10;1949:52777:9;;5953:1;;;31826:862;;;;;;;31380:2456;-1:-1:-1;31822:2008:9;;33756:74;;;;;33802:17;33756:74;;:::i;:::-;33802:17;:::i;31822:2008::-;4150:3:10;1949:52777:9;;;;;;;;5953:1;;;32847:15;;32843:877;;31822:2008;33733:11;;;;;;:::o;32843:877::-;5953:1;1949:52777;;;;;;;;;5953:1;;;;;;;;;;;;1949:52777;;;;;;;;33486:88;;;;;1949:52777;;;;;;5953:1;1949:52777;;33486:88;;;;;;;1624:4:10;33486:88:9;;1949:52777;33486:88;;1949:52777;5953:1;3859:3:10;5953:1:9;;;1949:52777;3859:3:10;5953:1:9;;1949:52777;;;;5953:1;:::i;:::-;;3859:3:10;5953:1:9;;;33486:88;;;;;;;;;;32843:877;;;;;;;33486:88;;;;:::i;:::-;;;;;1949:52777;;1624:4:10;1949:52777:9;;1624:4:10;;;;5953:1:9;3859:3:10;1949:52777:9;;;;;;;;31826:862;;;;1949:52777;31826:862;;1949:52777;31826:862;;;;;;1949:52777;31826:862;;;:::i;:::-;;;5953:1;;;;1624:4:10;31826:862:9;;;;;;;-1:-1:-1;31826:862:9;;50143:2400;;;;;;;-1:-1:-1;1949:52777:9;;;;50455:19;1949:52777;;;;;;;;50700:24;;;;1949:52777;;;;;;;50815:22;;;50811:59;;1949:52777;;;5953:1;;;51203:12;5953:1;;;;;;;1949:52777;;51314:131;;;;;;1949:52777;;;;;;;;;;;;;51314:131;;;;;1624:4:10;51314:131:9;;;;;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:4:10;;;;;;51314:131:9;;;;;;;;50143:2400;1949:52777;6100:2;1949:52777;;;;;;6100:2;1949:52777;;;;;;;52444:51;1949:52777;;;6200:4;52245:57;52415:12;1949:52777;;;52319:37;1949:52777;;52245:57;;:::i;:::-;52307:2;1949:52777;;;52319:37;:::i;:::-;52415:12;;:::i;:::-;52444:51;;50700:24;50143:2400;:::o;1949:52777::-;;;;;;;51314:131;1949:52777;;51314:131;;;;;;;:::i;:::-;;;;;;1949:52777;;1624:4:10;1949:52777:9;;1624:4:10;;;;50811:59:9;50839:31;;;;;;;;;;;;;:::o;28827:2547::-;;-1:-1:-1;1949:52777:9;;2023:2:10;1949:52777:9;;;;2293:3:10;1949:52777:9;;;;2565:3:10;1949:52777:9;;;;;2478:3:10;1949:52777:9;;;;2565:3:10;1949:52777:9;;;2893:3:10;1949:52777:9;;;;;;2941:3:10;1949:52777:9;;;;;;3192:3:10;1949:52777:9;;;;;;3289:3:10;1949:52777:9;;;;;;;2990:3:10;1949:52777:9;;29277:960;;;;;1624:4:10;29277:960:9;;1949:52777;29277:960;;1949:52777;2990:3:10;;;;;;;;;1843:1;1949:52777:9;;2990:3:10;:::i;:::-;1949:52777:9;2565:3:10;1949:52777:9;;29540:29;1949:52777;;2990:3:10;;;2747;2379;1949:52777:9;;5953:1;;;2990:3:10;;;1949:52777:9;2478:3:10;1949:52777:9;;2565:3:10;2990;;;1949:52777:9;2796:3:10;1949:52777:9;;2565:3:10;2990;;;1949:52777:9;;;2565:3:10;2478;2990;;1949:52777:9;-1:-1:-1;;2990:3:10;;;;2565;2990;;;1949:52777:9;;2990:3:10;:::i;:::-;1949:52777:9;;;3289:3:10;1949:52777:9;;2990:3:10;;;3289;29277:960:9;1949:52777;;2150:3:10;1949:52777:9;;5953:1;;;29277:960;;;;;;;28827:2547;-1:-1:-1;29273:2095:9;;31294:74;;;;31340:17;31294:74;;:::i;29273:2095::-;3483:3:10;1949:52777:9;;;;;;3435:3:10;1949:52777:9;;5953:1;;;30390:15;;30386:872;;31271:11;;;;;;:::o;30386:872::-;5953:1;1949:52777;;;;;;;;;5953:1;;;;;;;;;;;;1949:52777;;;;;;;;31029:83;;;;;1949:52777;;;;;;5953:1;1949:52777;;31029:83;;;;;;;1624:4:10;31029:83:9;;1949:52777;31029:83;;1949:52777;5953:1;2990:3:10;5953:1:9;;;1949:52777;2990:3:10;5953:1:9;;1949:52777;;;;5953:1;:::i;29277:960::-;;;;1949:52777;29277:960;;1949:52777;29277:960;;;;;;1949:52777;29277:960;;;:::i;:::-;;;5953:1;;;;1624:4:10;29277:960:9;;;;;;;-1:-1:-1;29277:960:9;;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;-1:-1:-1;1949:52777:9:o;11903:983::-;12162:11;11903:983;12162:11;:::i;:::-;12538:61;1949:52777;;12568:29;12545:53;;;;;;;;;;:::i;:::-;12538:61;:::i;:::-;1949:52777;;;;;;;;12538:80;;12534:97;;1949:52777;;12645:59;1949:52777;;12675:27;12545:53;12652:51;;;12545:53;12652:51;;;;;:::i;12645:59::-;1949:52777;12645:78;12641:95;;12750:58;1949:52777;;12780:26;12545:53;12757:50;;;12545:53;12757:50;;;;;:::i;12750:58::-;1949:52777;12750:77;12746:94;;1949:52777;11903:983;:::o;12746:94::-;1949:52777;12829:11;:::o;12641:95::-;12725:11;;1949:52777;12725:11;:::o;12534:97::-;12620:11;;1949:52777;12620:11;:::o

Swarm Source

ipfs://808e4fc618233f231535d012f6c96d52b17d5442bd4565852c1ba551e5a0ba06

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.