AVAX Price: $22.45 (+5.05%)
Gas: 6.8 nAVAX
 

Overview

AVAX Balance

Avalanche C-Chain LogoAvalanche C-Chain LogoAvalanche C-Chain Logo0 AVAX

AVAX Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SquidRouter

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 99999 runs

Other Settings:
paris EvmVersion
File 1 of 49 : SquidRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {ISquidRouter} from "../interfaces/ISquidRouter.sol";
import {ISquidMulticall} from "../interfaces/ISquidMulticall.sol";
import {AxelarExpressExecutable} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/express/AxelarExpressExecutable.sol";
import {IAxelarGasService} from "@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarGasService.sol";
import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol";
import {IERC20} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol";
import {SafeTokenTransfer, SafeTokenTransferFrom, TokenTransferFailed} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol";
import {Upgradable} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol";
import {RoledPausable} from "../libraries/RoledPausable.sol";

contract SquidRouter is ISquidRouter, AxelarExpressExecutable, Upgradable, RoledPausable {
    using SafeTokenTransferFrom for IERC20;
    using SafeTokenTransfer for IERC20;

    IAxelarGasService private immutable gasService;
    ISquidMulticall private immutable squidMulticall;

    constructor(
        address _gateway,
        address _gasService,
        address _multicall
    ) AxelarExpressExecutable(_gateway) {
        if (
            _gateway == address(0) ||
            _gasService == address(0) ||
            _multicall == address(0)
        ) revert ZeroAddressProvided();

        gasService = IAxelarGasService(_gasService);
        squidMulticall = ISquidMulticall(_multicall);
    }

    function bridgeCall(
        string calldata bridgedTokenSymbol,
        uint256 amount,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address gasRefundRecipient,
        bool enableExpress
    ) external payable whenNotPaused {
        address bridgedTokenAddress = gateway.tokenAddresses(bridgedTokenSymbol);

        IERC20(bridgedTokenAddress).safeTransferFrom(msg.sender, address(this), amount);

        _bridgeCall(
            bridgedTokenSymbol,
            bridgedTokenAddress,
            destinationChain,
            destinationAddress,
            payload,
            gasRefundRecipient,
            enableExpress
        );
    }

    function callBridge(
        address token,
        uint256 amount,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress
    ) external payable whenNotPaused {
        fundAndRunMulticall(token, amount, calls);

        address bridgedTokenAddress = gateway.tokenAddresses(bridgedTokenSymbol);
        uint256 bridgedTokenAmount = IERC20(bridgedTokenAddress).balanceOf(address(this));

        _approve(bridgedTokenAddress, address(gateway), bridgedTokenAmount);
        gateway.sendToken(destinationChain, destinationAddress, bridgedTokenSymbol, bridgedTokenAmount);
    }

    function callBridgeCall(
        address token,
        uint256 amount,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address gasRefundRecipient,
        bool enableExpress
    ) external payable whenNotPaused {
        fundAndRunMulticall(token, amount, calls);

        address bridgedTokenAddress = gateway.tokenAddresses(bridgedTokenSymbol);

        _bridgeCall(
            bridgedTokenSymbol,
            bridgedTokenAddress,
            destinationChain,
            destinationAddress,
            payload,
            gasRefundRecipient,
            enableExpress
        );
    }

    function contractId() external pure override returns (bytes32 id) {
        id = keccak256("squid-router");
    }

    function fundAndRunMulticall(
        address token,
        uint256 amount,
        ISquidMulticall.Call[] memory calls
    ) public payable whenNotPaused {
        uint256 valueToSend;

        if (token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
            valueToSend = amount;
        } else {
            _transferTokenToMulticall(token, amount);
        }

        squidMulticall.run{value: valueToSend}(calls);
    }

    function _executeWithToken(
        string calldata,
        string calldata,
        bytes calldata payload,
        string calldata bridgedTokenSymbol,
        uint256
    ) internal override {
        (ISquidMulticall.Call[] memory calls, address refundRecipient) = abi.decode(
            payload,
            (ISquidMulticall.Call[], address)
        );

        address bridgedTokenAddress = gateway.tokenAddresses(bridgedTokenSymbol);
        uint256 contractBalance = IERC20(bridgedTokenAddress).balanceOf(address(this));

        _approve(bridgedTokenAddress, address(squidMulticall), contractBalance);

        try squidMulticall.run(calls) {
            emit CrossMulticallExecuted(keccak256(payload));
        } catch (bytes memory reason) {
            // Refund tokens to refund recipient if swap fails
            IERC20(bridgedTokenAddress).safeTransfer(refundRecipient, contractBalance);
            emit CrossMulticallFailed(keccak256(payload), reason, refundRecipient);
        }
    }

    function _bridgeCall(
        string calldata bridgedTokenSymbol,
        address bridgedTokenAddress,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address gasRefundRecipient,
        bool enableExpress
    ) private {
        uint256 bridgedTokenBalance = IERC20(bridgedTokenAddress).balanceOf(address(this));

        if (address(this).balance > 0) {
            if (enableExpress) {
                gasService.payNativeGasForExpressCallWithToken{value: address(this).balance}(
                    address(this),
                    destinationChain,
                    destinationAddress,
                    payload,
                    bridgedTokenSymbol,
                    bridgedTokenBalance,
                    gasRefundRecipient
                );
            } else {
                gasService.payNativeGasForContractCallWithToken{value: address(this).balance}(
                    address(this),
                    destinationChain,
                    destinationAddress,
                    payload,
                    bridgedTokenSymbol,
                    bridgedTokenBalance,
                    gasRefundRecipient
                );
            }
        }

        _approve(bridgedTokenAddress, address(gateway), bridgedTokenBalance);
        gateway.callContractWithToken(
            destinationChain,
            destinationAddress,
            payload,
            bridgedTokenSymbol,
            bridgedTokenBalance
        );
    }

    function _approve(address token, address spender, uint256 amount) private {
        uint256 allowance = IERC20(token).allowance(address(this), spender);
        if (allowance < amount) {
            if (allowance > 0) {
                _approveCall(token, spender, 0);
            }
            _approveCall(token, spender, type(uint256).max);
        }
    }

    function _approveCall(address token, address spender, uint256 amount) private {
        // Unlimited approval is not security issue since the contract doesn't store tokens
        (bool success, ) = token.call(abi.encodeWithSelector(IERC20.approve.selector, spender, amount));
        if (!success) revert ApprovalFailed();
    }

    function _transferTokenToMulticall(address token, uint256 amount) private {
        (bool success, bytes memory returnData) = token.call(
            abi.encodeWithSelector(IERC20.transferFrom.selector, msg.sender, address(squidMulticall), amount)
        );
        bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
        if (!transferred || token.code.length == 0) revert TokenTransferFailed();
    }

    function _setup(bytes calldata data) internal override {
        address _pauser = abi.decode(data, (address));
        if (_pauser == address(0)) revert ZeroAddressProvided();
        _setPauser(_pauser);
    }
}

File 2 of 49 : IAxelarGasService.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IUpgradable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IUpgradable.sol';

/**
 * @title IAxelarGasService Interface
 * @notice This is an interface for the AxelarGasService contract which manages gas payments
 * and refunds for cross-chain communication on the Axelar network.
 * @dev This interface inherits IUpgradable
 */
interface IAxelarGasService is IUpgradable {
    error InvalidAddress();
    error NotCollector();
    error InvalidAmounts();

    event GasPaidForContractCall(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event GasPaidForContractCallWithToken(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        string symbol,
        uint256 amount,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event NativeGasPaidForContractCall(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event NativeGasPaidForContractCallWithToken(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        string symbol,
        uint256 amount,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event GasPaidForExpressCall(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event GasPaidForExpressCallWithToken(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        string symbol,
        uint256 amount,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event NativeGasPaidForExpressCall(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event NativeGasPaidForExpressCallWithToken(
        address indexed sourceAddress,
        string destinationChain,
        string destinationAddress,
        bytes32 indexed payloadHash,
        string symbol,
        uint256 amount,
        uint256 gasFeeAmount,
        address refundAddress
    );

    event GasAdded(bytes32 indexed txHash, uint256 indexed logIndex, address gasToken, uint256 gasFeeAmount, address refundAddress);

    event NativeGasAdded(bytes32 indexed txHash, uint256 indexed logIndex, uint256 gasFeeAmount, address refundAddress);

    event ExpressGasAdded(bytes32 indexed txHash, uint256 indexed logIndex, address gasToken, uint256 gasFeeAmount, address refundAddress);

    event NativeExpressGasAdded(bytes32 indexed txHash, uint256 indexed logIndex, uint256 gasFeeAmount, address refundAddress);

    event Refunded(bytes32 indexed txHash, uint256 indexed logIndex, address payable receiver, address token, uint256 amount);

    /**
     * @notice Pay for gas using ERC20 tokens for a contract call on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call
     * @param gasToken The address of the ERC20 token used to pay for gas
     * @param gasFeeAmount The amount of tokens to pay for gas
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payGasForContractCall(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    ) external;

    /**
     * @notice Pay for gas using ERC20 tokens for a contract call with tokens on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call with tokens will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call with tokens
     * @param symbol The symbol of the token to be sent with the call
     * @param amount The amount of tokens to be sent with the call
     * @param gasToken The address of the ERC20 token used to pay for gas
     * @param gasFeeAmount The amount of tokens to pay for gas
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payGasForContractCallWithToken(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    ) external;

    /**
     * @notice Pay for gas using native currency for a contract call on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payNativeGasForContractCall(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundAddress
    ) external payable;

    /**
     * @notice Pay for gas using native currency for a contract call with tokens on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call with tokens will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call with tokens
     * @param symbol The symbol of the token to be sent with the call
     * @param amount The amount of tokens to be sent with the call
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payNativeGasForContractCallWithToken(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount,
        address refundAddress
    ) external payable;

    /**
     * @notice Pay for gas using ERC20 tokens for an express contract call on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to express execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call
     * @param gasToken The address of the ERC20 token used to pay for gas
     * @param gasFeeAmount The amount of tokens to pay for gas
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payGasForExpressCall(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    ) external;

    /**
     * @notice Pay for gas using ERC20 tokens for an express contract call with tokens on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to express execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call with tokens will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call with tokens
     * @param symbol The symbol of the token to be sent with the call
     * @param amount The amount of tokens to be sent with the call
     * @param gasToken The address of the ERC20 token used to pay for gas
     * @param gasFeeAmount The amount of tokens to pay for gas
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payGasForExpressCallWithToken(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    ) external;

    /**
     * @notice Pay for gas using native currency for an express contract call on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payNativeGasForExpressCall(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundAddress
    ) external payable;

    /**
     * @notice Pay for gas using native currency for an express contract call with tokens on a destination chain.
     * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
     * @param sender The address making the payment
     * @param destinationChain The target chain where the contract call with tokens will be made
     * @param destinationAddress The target address on the destination chain
     * @param payload Data payload for the contract call with tokens
     * @param symbol The symbol of the token to be sent with the call
     * @param amount The amount of tokens to be sent with the call
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function payNativeGasForExpressCallWithToken(
        address sender,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount,
        address refundAddress
    ) external payable;

    /**
     * @notice Add additional gas payment using ERC20 tokens after initiating a cross-chain call.
     * @dev This function can be called on the source chain after calling the gateway to execute a remote contract.
     * @param txHash The transaction hash of the cross-chain call
     * @param logIndex The log index for the cross-chain call
     * @param gasToken The ERC20 token address used to add gas
     * @param gasFeeAmount The amount of tokens to add as gas
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function addGas(
        bytes32 txHash,
        uint256 logIndex,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    ) external;

    /**
     * @notice Add additional gas payment using native currency after initiating a cross-chain call.
     * @dev This function can be called on the source chain after calling the gateway to execute a remote contract.
     * @param txHash The transaction hash of the cross-chain call
     * @param logIndex The log index for the cross-chain call
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function addNativeGas(
        bytes32 txHash,
        uint256 logIndex,
        address refundAddress
    ) external payable;

    /**
     * @notice Add additional gas payment using ERC20 tokens after initiating an express cross-chain call.
     * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract.
     * @param txHash The transaction hash of the cross-chain call
     * @param logIndex The log index for the cross-chain call
     * @param gasToken The ERC20 token address used to add gas
     * @param gasFeeAmount The amount of tokens to add as gas
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function addExpressGas(
        bytes32 txHash,
        uint256 logIndex,
        address gasToken,
        uint256 gasFeeAmount,
        address refundAddress
    ) external;

    /**
     * @notice Add additional gas payment using native currency after initiating an express cross-chain call.
     * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract.
     * @param txHash The transaction hash of the cross-chain call
     * @param logIndex The log index for the cross-chain call
     * @param refundAddress The address where refunds, if any, should be sent
     */
    function addNativeExpressGas(
        bytes32 txHash,
        uint256 logIndex,
        address refundAddress
    ) external payable;

    /**
     * @notice Allows the gasCollector to collect accumulated fees from the contract.
     * @dev Use address(0) as the token address for native currency.
     * @param receiver The address to receive the collected fees
     * @param tokens Array of token addresses to be collected
     * @param amounts Array of amounts to be collected for each respective token address
     */
    function collectFees(
        address payable receiver,
        address[] calldata tokens,
        uint256[] calldata amounts
    ) external;

    /**
     * @notice Refunds gas payment to the receiver in relation to a specific cross-chain transaction.
     * @dev Only callable by the gasCollector.
     * @dev Use address(0) as the token address to refund native currency.
     * @param txHash The transaction hash of the cross-chain call
     * @param logIndex The log index for the cross-chain call
     * @param receiver The address to receive the refund
     * @param token The token address to be refunded
     * @param amount The amount to refund
     */
    function refund(
        bytes32 txHash,
        uint256 logIndex,
        address payable receiver,
        address token,
        uint256 amount
    ) external;

    /**
     * @notice Returns the address of the designated gas collector.
     * @return address of the gas collector
     */
    function gasCollector() external returns (address);
}

File 3 of 49 : IAxelarGateway.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.9;

interface IAxelarGateway {
    /**********\
    |* Errors *|
    \**********/

    error NotSelf();
    error NotProxy();
    error InvalidCodeHash();
    error SetupFailed();
    error InvalidAuthModule();
    error InvalidTokenDeployer();
    error InvalidAmount();
    error InvalidChainId();
    error InvalidCommands();
    error TokenDoesNotExist(string symbol);
    error TokenAlreadyExists(string symbol);
    error TokenDeployFailed(string symbol);
    error TokenContractDoesNotExist(address token);
    error BurnFailed(string symbol);
    error MintFailed(string symbol);
    error InvalidSetMintLimitsParams();
    error ExceedMintLimit(string symbol);

    /**********\
    |* Events *|
    \**********/

    event TokenSent(address indexed sender, string destinationChain, string destinationAddress, string symbol, uint256 amount);

    event ContractCall(
        address indexed sender,
        string destinationChain,
        string destinationContractAddress,
        bytes32 indexed payloadHash,
        bytes payload
    );

    event ContractCallWithToken(
        address indexed sender,
        string destinationChain,
        string destinationContractAddress,
        bytes32 indexed payloadHash,
        bytes payload,
        string symbol,
        uint256 amount
    );

    event Executed(bytes32 indexed commandId);

    event TokenDeployed(string symbol, address tokenAddresses);

    event ContractCallApproved(
        bytes32 indexed commandId,
        string sourceChain,
        string sourceAddress,
        address indexed contractAddress,
        bytes32 indexed payloadHash,
        bytes32 sourceTxHash,
        uint256 sourceEventIndex
    );

    event ContractCallApprovedWithMint(
        bytes32 indexed commandId,
        string sourceChain,
        string sourceAddress,
        address indexed contractAddress,
        bytes32 indexed payloadHash,
        string symbol,
        uint256 amount,
        bytes32 sourceTxHash,
        uint256 sourceEventIndex
    );

    event TokenMintLimitUpdated(string symbol, uint256 limit);

    event OperatorshipTransferred(bytes newOperatorsData);

    event Upgraded(address indexed implementation);

    /********************\
    |* Public Functions *|
    \********************/

    function sendToken(
        string calldata destinationChain,
        string calldata destinationAddress,
        string calldata symbol,
        uint256 amount
    ) external;

    function callContract(
        string calldata destinationChain,
        string calldata contractAddress,
        bytes calldata payload
    ) external;

    function callContractWithToken(
        string calldata destinationChain,
        string calldata contractAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount
    ) external;

    function isContractCallApproved(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        address contractAddress,
        bytes32 payloadHash
    ) external view returns (bool);

    function isContractCallAndMintApproved(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        address contractAddress,
        bytes32 payloadHash,
        string calldata symbol,
        uint256 amount
    ) external view returns (bool);

    function validateContractCall(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash
    ) external returns (bool);

    function validateContractCallAndMint(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash,
        string calldata symbol,
        uint256 amount
    ) external returns (bool);

    /***********\
    |* Getters *|
    \***********/

    function authModule() external view returns (address);

    function tokenDeployer() external view returns (address);

    function tokenMintLimit(string memory symbol) external view returns (uint256);

    function tokenMintAmount(string memory symbol) external view returns (uint256);

    function allTokensFrozen() external view returns (bool);

    function implementation() external view returns (address);

    function tokenAddresses(string memory symbol) external view returns (address);

    function tokenFrozen(string memory symbol) external view returns (bool);

    function isCommandExecuted(bytes32 commandId) external view returns (bool);

    function adminEpoch() external view returns (uint256);

    function adminThreshold(uint256 epoch) external view returns (uint256);

    function admins(uint256 epoch) external view returns (address[] memory);

    /*******************\
    |* Admin Functions *|
    \*******************/

    function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external;

    function upgrade(
        address newImplementation,
        bytes32 newImplementationCodeHash,
        bytes calldata setupParams
    ) external;

    /**********************\
    |* External Functions *|
    \**********************/

    function setup(bytes calldata params) external;

    function execute(bytes calldata input) external;
}

File 4 of 49 : AxelarExpressExecutable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IAxelarGateway } from '../interfaces/IAxelarGateway.sol';
import { ExpressExecutorTracker } from './ExpressExecutorTracker.sol';

import { SafeTokenTransferFrom, SafeTokenTransfer } from '../libs/SafeTransfer.sol';
import { IERC20 } from '../interfaces/IERC20.sol';

contract AxelarExpressExecutable is ExpressExecutorTracker {
    using SafeTokenTransfer for IERC20;
    using SafeTokenTransferFrom for IERC20;

    IAxelarGateway public immutable gateway;

    constructor(address gateway_) {
        if (gateway_ == address(0)) revert InvalidAddress();

        gateway = IAxelarGateway(gateway_);
    }

    function execute(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) external {
        bytes32 payloadHash = keccak256(payload);

        if (!gateway.validateContractCall(commandId, sourceChain, sourceAddress, payloadHash))
            revert NotApprovedByGateway();

        address expressExecutor = _popExpressExecutor(commandId, sourceChain, sourceAddress, payloadHash);

        if (expressExecutor != address(0)) {
            // slither-disable-next-line reentrancy-events
            emit ExpressExecutionFulfilled(commandId, sourceChain, sourceAddress, payloadHash, expressExecutor);
        } else {
            _execute(sourceChain, sourceAddress, payload);
        }
    }

    function executeWithToken(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload,
        string calldata tokenSymbol,
        uint256 amount
    ) external {
        bytes32 payloadHash = keccak256(payload);
        if (
            !gateway.validateContractCallAndMint(
                commandId,
                sourceChain,
                sourceAddress,
                payloadHash,
                tokenSymbol,
                amount
            )
        ) revert NotApprovedByGateway();

        address expressExecutor = _popExpressExecutorWithToken(
            commandId,
            sourceChain,
            sourceAddress,
            payloadHash,
            tokenSymbol,
            amount
        );

        if (expressExecutor != address(0)) {
            // slither-disable-next-line reentrancy-events
            emit ExpressExecutionWithTokenFulfilled(
                commandId,
                sourceChain,
                sourceAddress,
                payloadHash,
                tokenSymbol,
                amount,
                expressExecutor
            );

            address gatewayToken = gateway.tokenAddresses(tokenSymbol);
            IERC20(gatewayToken).safeTransfer(expressExecutor, amount);
        } else {
            _executeWithToken(sourceChain, sourceAddress, payload, tokenSymbol, amount);
        }
    }

    function expressExecute(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) external payable virtual {
        if (gateway.isCommandExecuted(commandId)) revert AlreadyExecuted();

        address expressExecutor = msg.sender;
        bytes32 payloadHash = keccak256(payload);

        emit ExpressExecuted(commandId, sourceChain, sourceAddress, payloadHash, expressExecutor);

        _setExpressExecutor(commandId, sourceChain, sourceAddress, payloadHash, expressExecutor);

        _execute(sourceChain, sourceAddress, payload);
    }

    function expressExecuteWithToken(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount
    ) external payable virtual {
        if (gateway.isCommandExecuted(commandId)) revert AlreadyExecuted();

        address expressExecutor = msg.sender;
        address gatewayToken = gateway.tokenAddresses(symbol);
        bytes32 payloadHash = keccak256(payload);

        emit ExpressExecutedWithToken(
            commandId,
            sourceChain,
            sourceAddress,
            payloadHash,
            symbol,
            amount,
            expressExecutor
        );

        _setExpressExecutorWithToken(
            commandId,
            sourceChain,
            sourceAddress,
            payloadHash,
            symbol,
            amount,
            expressExecutor
        );

        IERC20(gatewayToken).safeTransferFrom(expressExecutor, address(this), amount);

        _executeWithToken(sourceChain, sourceAddress, payload, symbol, amount);
    }

    function _execute(
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) internal virtual {}

    function _executeWithToken(
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload,
        string calldata tokenSymbol,
        uint256 amount
    ) internal virtual {}
}

File 5 of 49 : ExpressExecutorTracker.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IAxelarExpressExecutable } from '../interfaces/IAxelarExpressExecutable.sol';

abstract contract ExpressExecutorTracker is IAxelarExpressExecutable {
    bytes32 internal constant PREFIX_EXPRESS_EXECUTE = keccak256('express-execute');
    bytes32 internal constant PREFIX_EXPRESS_EXECUTE_WITH_TOKEN = keccak256('express-execute-with-token');

    function _expressExecuteSlot(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash
    ) internal pure returns (bytes32 slot) {
        slot = keccak256(abi.encode(PREFIX_EXPRESS_EXECUTE, commandId, sourceChain, sourceAddress, payloadHash));
    }

    function _expressExecuteWithTokenSlot(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash,
        string calldata symbol,
        uint256 amount
    ) internal pure returns (bytes32 slot) {
        slot = keccak256(
            abi.encode(
                PREFIX_EXPRESS_EXECUTE_WITH_TOKEN,
                commandId,
                sourceChain,
                sourceAddress,
                payloadHash,
                symbol,
                amount
            )
        );
    }

    function getExpressExecutor(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash
    ) external view returns (address expressExecutor) {
        bytes32 slot = _expressExecuteSlot(commandId, sourceChain, sourceAddress, payloadHash);

        assembly {
            expressExecutor := sload(slot)
        }
    }

    function getExpressExecutorWithToken(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash,
        string calldata symbol,
        uint256 amount
    ) external view returns (address expressExecutor) {
        bytes32 slot = _expressExecuteWithTokenSlot(commandId, sourceChain, sourceAddress, payloadHash, symbol, amount);

        assembly {
            expressExecutor := sload(slot)
        }
    }

    function _setExpressExecutor(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash,
        address expressExecutor
    ) internal {
        bytes32 slot = _expressExecuteSlot(commandId, sourceChain, sourceAddress, payloadHash);
        address currentExecutor;

        assembly {
            currentExecutor := sload(slot)
        }

        if (currentExecutor != address(0)) revert ExpressExecutorAlreadySet();

        assembly {
            sstore(slot, expressExecutor)
        }
    }

    function _setExpressExecutorWithToken(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash,
        string calldata symbol,
        uint256 amount,
        address expressExecutor
    ) internal {
        bytes32 slot = _expressExecuteWithTokenSlot(commandId, sourceChain, sourceAddress, payloadHash, symbol, amount);
        address currentExecutor;

        assembly {
            currentExecutor := sload(slot)
        }

        if (currentExecutor != address(0)) revert ExpressExecutorAlreadySet();

        assembly {
            sstore(slot, expressExecutor)
        }
    }

    function _popExpressExecutor(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash
    ) internal returns (address expressExecutor) {
        bytes32 slot = _expressExecuteSlot(commandId, sourceChain, sourceAddress, payloadHash);

        assembly {
            expressExecutor := sload(slot)
            if expressExecutor {
                sstore(slot, 0)
            }
        }
    }

    function _popExpressExecutorWithToken(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash,
        string calldata symbol,
        uint256 amount
    ) internal returns (address expressExecutor) {
        bytes32 slot = _expressExecuteWithTokenSlot(commandId, sourceChain, sourceAddress, payloadHash, symbol, amount);

        assembly {
            expressExecutor := sload(slot)
            if expressExecutor {
                sstore(slot, 0)
            }
        }
    }
}

File 6 of 49 : IAxelarExecutable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IAxelarGateway } from './IAxelarGateway.sol';

interface IAxelarExecutable {
    error InvalidAddress();
    error NotApprovedByGateway();

    function gateway() external view returns (IAxelarGateway);

    function execute(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) external;

    function executeWithToken(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload,
        string calldata tokenSymbol,
        uint256 amount
    ) external;
}

File 7 of 49 : IAxelarExpressExecutable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IAxelarExecutable } from './IAxelarExecutable.sol';

/**
 * @title IAxelarExpressExecutable
 * @notice Interface for the Axelar Express Executable contract.
 */
interface IAxelarExpressExecutable is IAxelarExecutable {
    // Custom errors
    error AlreadyExecuted();
    error InsufficientValue();
    error ExpressExecutorAlreadySet();

    /**
     * @notice Emitted when an express execution is successfully performed.
     * @param commandId The unique identifier for the command.
     * @param sourceChain The source chain.
     * @param sourceAddress The source address.
     * @param payloadHash The hash of the payload.
     * @param expressExecutor The address of the express executor.
     */
    event ExpressExecuted(
        bytes32 indexed commandId,
        string sourceChain,
        string sourceAddress,
        bytes32 payloadHash,
        address indexed expressExecutor
    );

    /**
     * @notice Emitted when an express execution with a token is successfully performed.
     * @param commandId The unique identifier for the command.
     * @param sourceChain The source chain.
     * @param sourceAddress The source address.
     * @param payloadHash The hash of the payload.
     * @param symbol The token symbol.
     * @param amount The amount of tokens.
     * @param expressExecutor The address of the express executor.
     */
    event ExpressExecutedWithToken(
        bytes32 indexed commandId,
        string sourceChain,
        string sourceAddress,
        bytes32 payloadHash,
        string symbol,
        uint256 indexed amount,
        address indexed expressExecutor
    );

    /**
     * @notice Emitted when an express execution is fulfilled.
     * @param commandId The commandId for the contractCall.
     * @param sourceChain The source chain.
     * @param sourceAddress The source address.
     * @param payloadHash The hash of the payload.
     * @param expressExecutor The address of the express executor.
     */
    event ExpressExecutionFulfilled(
        bytes32 indexed commandId,
        string sourceChain,
        string sourceAddress,
        bytes32 payloadHash,
        address indexed expressExecutor
    );

    /**
     * @notice Emitted when an express execution with a token is fulfilled.
     * @param commandId The commandId for the contractCallWithToken.
     * @param sourceChain The source chain.
     * @param sourceAddress The source address.
     * @param payloadHash The hash of the payload.
     * @param symbol The token symbol.
     * @param amount The amount of tokens.
     * @param expressExecutor The address of the express executor.
     */
    event ExpressExecutionWithTokenFulfilled(
        bytes32 indexed commandId,
        string sourceChain,
        string sourceAddress,
        bytes32 payloadHash,
        string symbol,
        uint256 indexed amount,
        address indexed expressExecutor
    );

    /**
     * @notice Returns the express executor for a given command.
     * @param commandId The commandId for the contractCall.
     * @param sourceChain The source chain.
     * @param sourceAddress The source address.
     * @param payloadHash The hash of the payload.
     * @return expressExecutor The address of the express executor.
     */
    function getExpressExecutor(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash
    ) external view returns (address expressExecutor);

    /**
     * @notice Returns the express executor with token for a given command.
     * @param commandId The commandId for the contractCallWithToken.
     * @param sourceChain The source chain.
     * @param sourceAddress The source address.
     * @param payloadHash The hash of the payload.
     * @param symbol The token symbol.
     * @param amount The amount of tokens.
     * @return expressExecutor The address of the express executor.
     */
    function getExpressExecutorWithToken(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash,
        string calldata symbol,
        uint256 amount
    ) external view returns (address expressExecutor);

    /**
     * @notice Express executes a contract call.
     * @param commandId The commandId for the contractCall.
     * @param sourceChain The source chain.
     * @param sourceAddress The source address.
     * @param payload The payload data.
     */
    function expressExecute(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) external payable;

    /**
     * @notice Express executes a contract call with token.
     * @param commandId The commandId for the contractCallWithToken.
     * @param sourceChain The source chain.
     * @param sourceAddress The source address.
     * @param payload The payload data.
     * @param symbol The token symbol.
     * @param amount The amount of token.
     */
    function expressExecuteWithToken(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount
    ) external payable;
}

File 8 of 49 : IAxelarGateway.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IGovernable } from './IGovernable.sol';

interface IAxelarGateway is IGovernable {
    /**********\
    |* Errors *|
    \**********/

    error NotSelf();
    error NotProxy();
    error InvalidCodeHash();
    error SetupFailed();
    error InvalidAuthModule();
    error InvalidTokenDeployer();
    error InvalidAmount();
    error InvalidChainId();
    error InvalidCommands();
    error TokenDoesNotExist(string symbol);
    error TokenAlreadyExists(string symbol);
    error TokenDeployFailed(string symbol);
    error TokenContractDoesNotExist(address token);
    error BurnFailed(string symbol);
    error MintFailed(string symbol);
    error InvalidSetMintLimitsParams();
    error ExceedMintLimit(string symbol);

    /**********\
    |* Events *|
    \**********/

    event TokenSent(
        address indexed sender,
        string destinationChain,
        string destinationAddress,
        string symbol,
        uint256 amount
    );

    event ContractCall(
        address indexed sender,
        string destinationChain,
        string destinationContractAddress,
        bytes32 indexed payloadHash,
        bytes payload
    );

    event ContractCallWithToken(
        address indexed sender,
        string destinationChain,
        string destinationContractAddress,
        bytes32 indexed payloadHash,
        bytes payload,
        string symbol,
        uint256 amount
    );

    event Executed(bytes32 indexed commandId);

    event TokenDeployed(string symbol, address tokenAddresses);

    event ContractCallApproved(
        bytes32 indexed commandId,
        string sourceChain,
        string sourceAddress,
        address indexed contractAddress,
        bytes32 indexed payloadHash,
        bytes32 sourceTxHash,
        uint256 sourceEventIndex
    );

    event ContractCallApprovedWithMint(
        bytes32 indexed commandId,
        string sourceChain,
        string sourceAddress,
        address indexed contractAddress,
        bytes32 indexed payloadHash,
        string symbol,
        uint256 amount,
        bytes32 sourceTxHash,
        uint256 sourceEventIndex
    );

    event TokenMintLimitUpdated(string symbol, uint256 limit);

    event OperatorshipTransferred(bytes newOperatorsData);

    event Upgraded(address indexed implementation);

    /********************\
    |* Public Functions *|
    \********************/

    function sendToken(
        string calldata destinationChain,
        string calldata destinationAddress,
        string calldata symbol,
        uint256 amount
    ) external;

    function callContract(
        string calldata destinationChain,
        string calldata contractAddress,
        bytes calldata payload
    ) external;

    function callContractWithToken(
        string calldata destinationChain,
        string calldata contractAddress,
        bytes calldata payload,
        string calldata symbol,
        uint256 amount
    ) external;

    function isContractCallApproved(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        address contractAddress,
        bytes32 payloadHash
    ) external view returns (bool);

    function isContractCallAndMintApproved(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        address contractAddress,
        bytes32 payloadHash,
        string calldata symbol,
        uint256 amount
    ) external view returns (bool);

    function validateContractCall(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash
    ) external returns (bool);

    function validateContractCallAndMint(
        bytes32 commandId,
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes32 payloadHash,
        string calldata symbol,
        uint256 amount
    ) external returns (bool);

    /***********\
    |* Getters *|
    \***********/

    function authModule() external view returns (address);

    function tokenDeployer() external view returns (address);

    function tokenMintLimit(string memory symbol) external view returns (uint256);

    function tokenMintAmount(string memory symbol) external view returns (uint256);

    function allTokensFrozen() external view returns (bool);

    function implementation() external view returns (address);

    function tokenAddresses(string memory symbol) external view returns (address);

    function tokenFrozen(string memory symbol) external view returns (bool);

    function isCommandExecuted(bytes32 commandId) external view returns (bool);

    function adminEpoch() external view returns (uint256);

    function adminThreshold(uint256 epoch) external view returns (uint256);

    function admins(uint256 epoch) external view returns (address[] memory);

    /*******************\
    |* Admin Functions *|
    \*******************/

    function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external;

    function upgrade(
        address newImplementation,
        bytes32 newImplementationCodeHash,
        bytes calldata setupParams
    ) external;

    /**********************\
    |* External Functions *|
    \**********************/

    function setup(bytes calldata params) external;

    function execute(bytes calldata input) external;
}

File 9 of 49 : IContractIdentifier.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// General interface for upgradable contracts
interface IContractIdentifier {
    /**
     * @notice Returns the contract ID. It can be used as a check during upgrades.
     * @dev Meant to be overridden in derived contracts.
     * @return bytes32 The contract ID
     */
    function contractId() external pure returns (bytes32);
}

File 10 of 49 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    error InvalidAccount();

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 11 of 49 : IGovernable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title IGovernable Interface
 * @notice This is an interface used by the AxelarGateway contract to manage governance and mint limiter roles.
 */
interface IGovernable {
    error NotGovernance();
    error NotMintLimiter();
    error InvalidGovernance();
    error InvalidMintLimiter();

    event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance);
    event MintLimiterTransferred(address indexed previousGovernance, address indexed newGovernance);

    /**
     * @notice Returns the governance address.
     * @return address of the governance
     */
    function governance() external view returns (address);

    /**
     * @notice Returns the mint limiter address.
     * @return address of the mint limiter
     */
    function mintLimiter() external view returns (address);

    /**
     * @notice Transfer the governance role to another address.
     * @param newGovernance The new governance address
     */
    function transferGovernance(address newGovernance) external;

    /**
     * @notice Transfer the mint limiter role to another address.
     * @param newGovernance The new mint limiter address
     */
    function transferMintLimiter(address newGovernance) external;
}

File 12 of 49 : IInitProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IProxy } from './IProxy.sol';

// General interface for upgradable contracts
interface IInitProxy is IProxy {
    function init(
        address implementationAddress,
        address newOwner,
        bytes memory params
    ) external;
}

File 13 of 49 : IOwnable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title IOwnable Interface
 * @notice IOwnable is an interface that abstracts the implementation of a
 * contract with ownership control features. It's commonly used in upgradable
 * contracts and includes the functionality to get current owner, transfer
 * ownership, and propose and accept ownership.
 */
interface IOwnable {
    error NotOwner();
    error InvalidOwner();
    error InvalidOwnerAddress();

    event OwnershipTransferStarted(address indexed newOwner);
    event OwnershipTransferred(address indexed newOwner);

    /**
     * @notice Returns the current owner of the contract.
     * @return address The address of the current owner
     */
    function owner() external view returns (address);

    /**
     * @notice Returns the address of the pending owner of the contract.
     * @return address The address of the pending owner
     */
    function pendingOwner() external view returns (address);

    /**
     * @notice Transfers ownership of the contract to a new address
     * @param newOwner The address to transfer ownership to
     */
    function transferOwnership(address newOwner) external;

    /**
     * @notice Proposes to transfer the contract's ownership to a new address.
     * The new owner needs to accept the ownership explicitly.
     * @param newOwner The address to transfer ownership to
     */
    function proposeOwnership(address newOwner) external;

    /**
     * @notice Transfers ownership to the pending owner.
     * @dev Can only be called by the pending owner
     */
    function acceptOwnership() external;
}

File 14 of 49 : IProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// General interface for upgradable contracts
interface IProxy {
    error InvalidOwner();
    error InvalidImplementation();
    error SetupFailed();
    error NotOwner();
    error AlreadyInitialized();

    function implementation() external view returns (address);

    function setup(bytes calldata setupParams) external;
}

File 15 of 49 : IUpgradable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IOwnable } from './IOwnable.sol';
import { IContractIdentifier } from './IContractIdentifier.sol';

// General interface for upgradable contracts
interface IUpgradable is IOwnable, IContractIdentifier {
    error InvalidCodeHash();
    error InvalidImplementation();
    error SetupFailed();
    error NotProxy();

    event Upgraded(address indexed newImplementation);

    function implementation() external view returns (address);

    function upgrade(
        address newImplementation,
        bytes32 newImplementationCodeHash,
        bytes calldata params
    ) external;

    function setup(bytes calldata data) external;
}

File 16 of 49 : SafeTransfer.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IERC20 } from '../interfaces/IERC20.sol';

error TokenTransferFailed();

/*
 * @title SafeTokenCall
 * @dev This library is used for performing safe token transfers.
 */
library SafeTokenCall {
    /*
     * @notice Make a safe call to a token contract.
     * @param token The token contract to interact with.
     * @param callData The function call data.
     * @throws TokenTransferFailed error if transfer of token is not successful.
     */
    function safeCall(IERC20 token, bytes memory callData) internal {
        (bool success, bytes memory returnData) = address(token).call(callData);
        bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));

        if (!transferred || address(token).code.length == 0) revert TokenTransferFailed();
    }
}

/*
 * @title SafeTokenTransfer
 * @dev This library safely transfers tokens from the contract to a recipient.
 */
library SafeTokenTransfer {
    /*
     * @notice Transfer tokens to a recipient.
     * @param token The token contract.
     * @param receiver The recipient of the tokens.
     * @param amount The amount of tokens to transfer.
     */
    function safeTransfer(
        IERC20 token,
        address receiver,
        uint256 amount
    ) internal {
        SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount));
    }
}

/*
 * @title SafeTokenTransferFrom
 * @dev This library helps to safely transfer tokens on behalf of a token holder.
 */
library SafeTokenTransferFrom {
    /*
     * @notice Transfer tokens on behalf of a token holder.
     * @param token The token contract.
     * @param from The address of the token holder.
     * @param to The address the tokens are to be sent to.
     * @param amount The amount of tokens to be transferred.
     */
    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount));
    }
}

File 17 of 49 : BaseProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IProxy } from '../interfaces/IProxy.sol';

/**
 * @title BaseProxy Contract
 * @dev This abstract contract implements a basic proxy that stores an implementation address. Fallback function
 * calls are delegated to the implementation. This contract is meant to be inherited by other proxy contracts.
 */
abstract contract BaseProxy is IProxy {
    // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
    // keccak256('owner')
    bytes32 internal constant _OWNER_SLOT = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0;

    /**
     * @dev Returns the current implementation address.
     * @return implementation_ The address of the current implementation contract
     */
    function implementation() public view virtual returns (address implementation_) {
        assembly {
            implementation_ := sload(_IMPLEMENTATION_SLOT)
        }
    }

    /**
     * @dev Shadows the setup function of the implementation contract so it can't be called directly via the proxy.
     * @param params The setup parameters for the implementation contract.
     */
    function setup(bytes calldata params) external {}

    /**
     * @dev Returns the contract ID. It can be used as a check during upgrades. Meant to be implemented in derived contracts.
     * @return bytes32 The contract ID
     */
    function contractId() internal pure virtual returns (bytes32);

    /**
     * @dev Fallback function. Delegates the call to the current implementation contract.
     */
    fallback() external payable virtual {
        address implementation_ = implementation();
        assembly {
            calldatacopy(0, 0, calldatasize())

            let result := delegatecall(gas(), implementation_, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())

            switch result
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }

    /**
     * @dev Payable fallback function. Can be overridden in derived contracts.
     */
    receive() external payable virtual {}
}

File 18 of 49 : InitProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IInitProxy } from '../interfaces/IInitProxy.sol';
import { IContractIdentifier } from '../interfaces/IContractIdentifier.sol';
import { BaseProxy } from './BaseProxy.sol';

/**
 * @title InitProxy Contract
 * @notice A proxy contract that can be initialized to use a specified implementation and owner. Inherits from BaseProxy
 * and implements the IInitProxy interface.
 * @dev This proxy is constructed empty and then later initialized with the implementation contract address, new owner address,
 * and any optional setup parameters.
 */
contract InitProxy is BaseProxy, IInitProxy {
    /**
     * @dev Initializes the contract and sets the caller as the owner of the contract.
     */
    constructor() {
        assembly {
            sstore(_OWNER_SLOT, caller())
        }
    }

    function contractId() internal pure virtual override returns (bytes32) {
        return bytes32(0);
    }

    /**
     * @notice Initializes the proxy contract with the specified implementation, new owner, and any optional setup parameters.
     * @param implementationAddress The address of the implementation contract
     * @param newOwner The address of the new proxy owner
     * @param params Optional parameters to be passed to the setup function of the implementation contract
     * @dev This function is only callable by the owner of the proxy. If the proxy has already been initialized, it will revert.
     * If the contract ID of the implementation is incorrect, it will also revert. It then stores the implementation address and
     * new owner address in the designated storage slots and calls the setup function on the implementation (if setup params exist).
     */
    function init(
        address implementationAddress,
        address newOwner,
        bytes memory params
    ) external {
        address owner;

        assembly {
            owner := sload(_OWNER_SLOT)
        }

        if (msg.sender != owner) revert NotOwner();
        if (implementation() != address(0)) revert AlreadyInitialized();

        bytes32 id = contractId();
        // Skipping the check if contractId() is not set by an inheriting proxy contract
        if (id != bytes32(0) && IContractIdentifier(implementationAddress).contractId() != id)
            revert InvalidImplementation();

        assembly {
            sstore(_IMPLEMENTATION_SLOT, implementationAddress)
            sstore(_OWNER_SLOT, newOwner)
        }

        if (params.length != 0) {
            (bool success, ) = implementationAddress.delegatecall(
                abi.encodeWithSelector(BaseProxy.setup.selector, params)
            );
            if (!success) revert SetupFailed();
        }
    }
}

File 19 of 49 : Upgradable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IUpgradable } from '../interfaces/IUpgradable.sol';
import { Ownable } from '../utils/Ownable.sol';

/**
 * @title Upgradable Contract
 * @notice This contract provides an interface for upgradable smart contracts and includes the functionality to perform upgrades.
 */
abstract contract Upgradable is Ownable, IUpgradable {
    // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
    address internal immutable implementationAddress;

    /**
     * @notice Constructor sets the implementation address to the address of the contract itself
     * @dev This is used in the onlyProxy modifier to prevent certain functions from being called directly
     * on the implementation contract itself.
     * @dev The owner is initially set as address(1) because the actual owner is set within the proxy. It is not
     * set as the zero address because Ownable is designed to throw an error for ownership transfers to the zero address.
     */
    constructor() Ownable(address(1)) {
        implementationAddress = address(this);
    }

    /**
     * @notice Modifier to ensure that a function can only be called by the proxy
     */
    modifier onlyProxy() {
        // Prevent setup from being called on the implementation
        if (address(this) == implementationAddress) revert NotProxy();
        _;
    }

    /**
     * @notice Returns the address of the current implementation
     * @return implementation_ Address of the current implementation
     */
    function implementation() public view returns (address implementation_) {
        assembly {
            implementation_ := sload(_IMPLEMENTATION_SLOT)
        }
    }

    /**
     * @notice Upgrades the contract to a new implementation
     * @param newImplementation The address of the new implementation contract
     * @param newImplementationCodeHash The codehash of the new implementation contract
     * @param params Optional setup parameters for the new implementation contract
     * @dev This function is only callable by the owner.
     */
    function upgrade(
        address newImplementation,
        bytes32 newImplementationCodeHash,
        bytes calldata params
    ) external override onlyOwner {
        if (IUpgradable(newImplementation).contractId() != IUpgradable(this).contractId())
            revert InvalidImplementation();

        if (newImplementationCodeHash != newImplementation.codehash) revert InvalidCodeHash();

        emit Upgraded(newImplementation);

        if (params.length > 0) {
            // slither-disable-next-line controlled-delegatecall
            (bool success, ) = newImplementation.delegatecall(abi.encodeWithSelector(this.setup.selector, params));

            if (!success) revert SetupFailed();
        }

        assembly {
            sstore(_IMPLEMENTATION_SLOT, newImplementation)
        }
    }

    /**
     * @notice Sets up the contract with initial data
     * @param data Initialization data for the contract
     * @dev This function is only callable by the proxy contract.
     */
    function setup(bytes calldata data) external override onlyProxy {
        _setup(data);
    }

    /**
     * @notice Internal function to set up the contract with initial data
     * @param data Initialization data for the contract
     * @dev This function should be implemented in derived contracts.
     */
    function _setup(bytes calldata data) internal virtual {}
}

File 20 of 49 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IOwnable } from '../interfaces/IOwnable.sol';

/**
 * @title Ownable
 * @notice A contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The owner account is set through ownership transfer. This module makes
 * it possible to transfer the ownership of the contract to a new account in one
 * step, as well as to an interim pending owner. In the second flow the ownership does not
 * change until the pending owner accepts the ownership transfer.
 */
abstract contract Ownable is IOwnable {
    // keccak256('owner')
    bytes32 internal constant _OWNER_SLOT = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0;
    // keccak256('ownership-transfer')
    bytes32 internal constant _OWNERSHIP_TRANSFER_SLOT =
        0x9855384122b55936fbfb8ca5120e63c6537a1ac40caf6ae33502b3c5da8c87d1;

    /**
     * @notice Initializes the contract by transferring ownership to the owner parameter.
     * @param _owner Address to set as the initial owner of the contract
     */
    constructor(address _owner) {
        _transferOwnership(_owner);
    }

    /**
     * @notice Modifier that throws an error if called by any account other than the owner.
     */
    modifier onlyOwner() {
        if (owner() != msg.sender) revert NotOwner();

        _;
    }

    /**
     * @notice Returns the current owner of the contract.
     * @return owner_ The current owner of the contract
     */
    function owner() public view returns (address owner_) {
        assembly {
            owner_ := sload(_OWNER_SLOT)
        }
    }

    /**
     * @notice Returns the pending owner of the contract.
     * @return owner_ The pending owner of the contract
     */
    function pendingOwner() public view returns (address owner_) {
        assembly {
            owner_ := sload(_OWNERSHIP_TRANSFER_SLOT)
        }
    }

    /**
     * @notice Transfers ownership of the contract to a new account `newOwner`.
     * @dev Can only be called by the current owner.
     * @param newOwner The address to transfer ownership to
     */
    function transferOwnership(address newOwner) external virtual onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @notice Propose to transfer ownership of the contract to a new account `newOwner`.
     * @dev Can only be called by the current owner. The ownership does not change
     * until the new owner accepts the ownership transfer.
     * @param newOwner The address to transfer ownership to
     */
    function proposeOwnership(address newOwner) external virtual onlyOwner {
        if (newOwner == address(0)) revert InvalidOwnerAddress();

        emit OwnershipTransferStarted(newOwner);

        assembly {
            sstore(_OWNERSHIP_TRANSFER_SLOT, newOwner)
        }
    }

    /**
     * @notice Accepts ownership of the contract.
     * @dev Can only be called by the pending owner
     */
    function acceptOwnership() external virtual {
        address newOwner = pendingOwner();
        if (newOwner != msg.sender) revert InvalidOwner();

        _transferOwnership(newOwner);
    }

    /**
     * @notice Internal function to transfer ownership of the contract to a new account `newOwner`.
     * @dev Called in the constructor to set the initial owner.
     * @param newOwner The address to transfer ownership to
     */
    function _transferOwnership(address newOwner) internal virtual {
        if (newOwner == address(0)) revert InvalidOwnerAddress();

        emit OwnershipTransferred(newOwner);

        assembly {
            sstore(_OWNER_SLOT, newOwner)
            sstore(_OWNERSHIP_TRANSFER_SLOT, 0)
        }
    }
}

File 21 of 49 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

File 22 of 49 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 23 of 49 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

File 24 of 49 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

File 25 of 49 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 26 of 49 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 27 of 49 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 28 of 49 : DepositReceiver.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {ISquidDepositService} from "../interfaces/ISquidDepositService.sol";

contract DepositReceiver {
    constructor(bytes memory delegateData, address refundRecipient) {
        // Reading the implementation of the AxelarDepositService
        // and delegating the call back to it
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, ) = ISquidDepositService(msg.sender).receiverImplementation().delegatecall(delegateData);

        // if not success revert with the original revert data
        if (!success) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                let ptr := mload(0x40)
                let size := returndatasize()
                returndatacopy(ptr, 0, size)
                revert(ptr, size)
            }
        }

        if (refundRecipient == address(0)) refundRecipient = msg.sender;

        selfdestruct(payable(refundRecipient));
    }

    // @dev This function is for receiving Ether from unwrapping WETH9
    // solhint-disable-next-line no-empty-blocks
    receive() external payable {}
}

File 29 of 49 : ReceiverImplementation.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IAxelarGateway} from "@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarGateway.sol";
import {ISquidRouter} from "../interfaces/ISquidRouter.sol";
import {ISquidMulticall} from "../interfaces/ISquidMulticall.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ISquidDepositService} from "../interfaces/ISquidDepositService.sol";

contract ReceiverImplementation {
    using SafeERC20 for IERC20;

    error ZeroAddressProvided();
    error InvalidSymbol();
    error NothingDeposited();

    address private constant nativeCoin = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address immutable router;
    address immutable gateway;

    constructor(address _router, address _gateway) {
        if (_router == address(0) || _gateway == address(0)) revert ZeroAddressProvided();

        router = _router;
        gateway = _gateway;
    }

    // Context: msg.sender == SquidDepositService, this == DepositReceiver
    function receiveAndBridgeCall(
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool enableExpress
    ) external {
        // Checking with AxelarDepositService if need to refund a token
        address tokenToRefund = ISquidDepositService(msg.sender).refundToken();
        if (tokenToRefund != address(0)) {
            _refund(tokenToRefund, refundRecipient);
            return;
        }

        address tokenAddress = IAxelarGateway(gateway).tokenAddresses(bridgedTokenSymbol);
        if (tokenAddress == address(0)) revert InvalidSymbol();
        uint256 amount = IERC20(tokenAddress).balanceOf(address(this));
        if (amount == 0) revert NothingDeposited();

        IERC20(tokenAddress).approve(router, amount);
        ISquidRouter(router).bridgeCall{value: address(this).balance}(
            bridgedTokenSymbol,
            amount,
            destinationChain,
            destinationAddress,
            payload,
            refundRecipient,
            enableExpress
        );
    }

    // Context: msg.sender == SquidDepositService, this == DepositReceiver
    function receiveAndCallBridge(
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        address refundRecipient
    ) external {
        // Checking with AxelarDepositService if need to refund a token
        address tokenToRefund = ISquidDepositService(msg.sender).refundToken();
        if (tokenToRefund != address(0)) {
            _refund(tokenToRefund, refundRecipient);
            return;
        }

        uint256 amount = IERC20(token).balanceOf(address(this));
        if (amount == 0) revert NothingDeposited();

        IERC20(token).approve(router, amount);
        ISquidRouter(router).callBridge{value: address(this).balance}(
            token,
            amount,
            calls,
            bridgedTokenSymbol,
            destinationChain,
            destinationAddress
        );
    }

    function receiveAndCallBridgeCall(
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool enableExpress
    ) external {
        // Checking with AxelarDepositService if need to refund a token
        address tokenToRefund = ISquidDepositService(msg.sender).refundToken();
        if (tokenToRefund != address(0)) {
            _refund(tokenToRefund, refundRecipient);
            return;
        }

        uint256 amount = IERC20(token).balanceOf(address(this));
        if (amount == 0) revert NothingDeposited();

        IERC20(token).approve(router, amount);
        ISquidRouter(router).callBridgeCall{value: address(this).balance}(
            token,
            amount,
            calls,
            bridgedTokenSymbol,
            destinationChain,
            destinationAddress,
            payload,
            refundRecipient,
            enableExpress
        );
    }

    function receiveAndFundAndRunMulticall(
        address token,
        ISquidMulticall.Call[] memory calls,
        address refundRecipient
    ) external {
        // Checking with AxelarDepositService if need to refund a token
        address tokenToRefund = ISquidDepositService(msg.sender).refundToken();

        if (tokenToRefund != address(0)) {
            _refund(tokenToRefund, refundRecipient);
            return;
        }

        uint256 amount = IERC20(token).balanceOf(address(this));
        if (amount == 0) revert NothingDeposited();

        IERC20(token).approve(router, amount);
        ISquidRouter(router).fundAndRunMulticall{value: address(this).balance}(token, amount, calls);
    }

    function _refund(address tokenToRefund, address refundRecipient) private {
        if (refundRecipient == address(0)) refundRecipient = msg.sender;

        if (tokenToRefund != nativeCoin) {
            uint256 contractBalance = IERC20(tokenToRefund).balanceOf(address(this));
            IERC20(tokenToRefund).safeTransfer(refundRecipient, contractBalance);
        }
    }
}

File 30 of 49 : SquidDepositService.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {ISquidDepositService} from "../interfaces/ISquidDepositService.sol";
import {ISquidMulticall} from "../interfaces/ISquidMulticall.sol";
import {IAxelarGateway} from "@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarGateway.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Upgradable} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol";
import {DepositReceiver} from "./DepositReceiver.sol";
import {ReceiverImplementation} from "./ReceiverImplementation.sol";

/// @dev This should be owned by the microservice that is paying for gas.
contract SquidDepositService is Upgradable, ISquidDepositService {
    using SafeERC20 for IERC20;

    // This public storage is for ERC20 token intended to be refunded.
    // It triggers the DepositReceiver/ReceiverImplementation to switch into a refund mode.
    // Address is stored and deleted withing the same refund transaction.
    address public refundToken;

    address private constant nativeCoin = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address immutable gateway;
    address public immutable refundIssuer;
    address public immutable receiverImplementation;

    constructor(address _router, address _gateway, address _refundIssuer) {
        if (_gateway == address(0) || _refundIssuer == address(0)) revert ZeroAddressProvided();

        gateway = _gateway;
        refundIssuer = _refundIssuer;
        receiverImplementation = address(new ReceiverImplementation(_router, _gateway));
    }

    function addressForBridgeCallDeposit(
        bytes32 salt,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool enableExpress
    ) external view returns (address) {
        return
            _depositAddress(
                salt,
                abi.encodeWithSelector(
                    ReceiverImplementation.receiveAndBridgeCall.selector,
                    bridgedTokenSymbol,
                    destinationChain,
                    destinationAddress,
                    payload,
                    refundRecipient,
                    enableExpress
                ),
                refundRecipient
            );
    }

    function addressForCallBridgeDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        address refundRecipient
    ) external view returns (address) {
        return
            _depositAddress(
                salt,
                abi.encodeWithSelector(
                    ReceiverImplementation.receiveAndCallBridge.selector,
                    token,
                    calls,
                    bridgedTokenSymbol,
                    destinationChain,
                    destinationAddress,
                    refundRecipient
                ),
                refundRecipient
            );
    }

    function addressForCallBridgeCallDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool enableExpress
    ) external view returns (address) {
        return
            _depositAddress(
                salt,
                abi.encodeWithSelector(
                    ReceiverImplementation.receiveAndCallBridgeCall.selector,
                    token,
                    calls,
                    bridgedTokenSymbol,
                    destinationChain,
                    destinationAddress,
                    payload,
                    refundRecipient,
                    enableExpress
                ),
                refundRecipient
            );
    }

    function addressForFundAndRunMulticallDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] memory calls,
        address refundRecipient
    ) external view returns (address) {
        return
            _depositAddress(
                salt,
                abi.encodeWithSelector(
                    ReceiverImplementation.receiveAndFundAndRunMulticall.selector,
                    token,
                    calls,
                    refundRecipient
                ),
                refundRecipient
            );
    }

    function bridgeCallDeposit(
        bytes32 salt,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool enableExpress
    ) external {
        new DepositReceiver{salt: salt}(
            abi.encodeWithSelector(
                ReceiverImplementation.receiveAndBridgeCall.selector,
                bridgedTokenSymbol,
                destinationChain,
                destinationAddress,
                payload,
                refundRecipient,
                enableExpress
            ),
            refundRecipient
        );
    }

    function callBridgeDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        address refundRecipient
    ) external {
        new DepositReceiver{salt: salt}(
            abi.encodeWithSelector(
                ReceiverImplementation.receiveAndCallBridge.selector,
                token,
                calls,
                bridgedTokenSymbol,
                destinationChain,
                destinationAddress,
                refundRecipient
            ),
            refundRecipient
        );
    }

    function callBridgeCallDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool express
    ) external {
        new DepositReceiver{salt: salt}(
            abi.encodeWithSelector(
                ReceiverImplementation.receiveAndCallBridgeCall.selector,
                token,
                calls,
                bridgedTokenSymbol,
                destinationChain,
                destinationAddress,
                payload,
                refundRecipient,
                express
            ),
            refundRecipient
        );
    }

    function fundAndRunMulticallDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] memory calls,
        address refundRecipient
    ) external {
        // NOTE: `DepositReceiver` is destroyed in the same runtime context that it is deployed.
        new DepositReceiver{salt: salt}(
            abi.encodeWithSelector(
                ReceiverImplementation.receiveAndFundAndRunMulticall.selector,
                token,
                calls,
                refundRecipient
            ),
            refundRecipient
        );
    }

    /// @dev Refunds ERC20 token from the deposit address if it doesn't match the intended token
    // Only refundRecipient can refund the token that was intended to go cross-chain (if not sent yet)
    function refundBridgeCallDeposit(
        bytes32 salt,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool express,
        address tokenToRefund
    ) external {
        address intendedToken = IAxelarGateway(gateway).tokenAddresses(bridgedTokenSymbol);
        // Allowing only the refundRecipient to refund the intended token
        if (tokenToRefund == intendedToken && msg.sender != refundRecipient) return;

        // Saving to public storage to be accessed by the DepositReceiver
        refundToken = tokenToRefund;

        new DepositReceiver{salt: salt}(
            abi.encodeWithSelector(
                ReceiverImplementation.receiveAndBridgeCall.selector,
                bridgedTokenSymbol,
                destinationChain,
                destinationAddress,
                payload,
                refundRecipient,
                express
            ),
            refundRecipient
        );

        refundToken = address(0);
    }

    function refundCallBridgeDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        address refundRecipient,
        address tokenToRefund
    ) external {
        // Allowing only the refundRecipient to refund the intended token
        if (tokenToRefund == token && msg.sender != refundRecipient) return;

        // Saving to public storage to be accessed by the DepositReceiver
        refundToken = tokenToRefund;
        new DepositReceiver{salt: salt}(
            abi.encodeWithSelector(
                ReceiverImplementation.receiveAndCallBridge.selector,
                token,
                calls,
                bridgedTokenSymbol,
                destinationChain,
                destinationAddress,
                refundRecipient
            ),
            refundRecipient
        );

        refundToken = address(0);
    }

    function refundCallBridgeCallDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool express,
        address tokenToRefund
    ) external {
        // Allowing only the refundRecipient to refund the intended token
        if (tokenToRefund == token && msg.sender != refundRecipient) return;

        // Saving to public storage to be accessed by the DepositReceiver
        refundToken = tokenToRefund;
        new DepositReceiver{salt: salt}(
            abi.encodeWithSelector(
                ReceiverImplementation.receiveAndCallBridgeCall.selector,
                token,
                calls,
                bridgedTokenSymbol,
                destinationChain,
                destinationAddress,
                payload,
                refundRecipient,
                express
            ),
            refundRecipient
        );

        refundToken = address(0);
    }

    function refundFundAndRunMulticallDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] memory calls,
        address refundRecipient,
        address tokenToRefund
    ) external {
        // Allowing only the refundRecipient to refund the intended token
        if (tokenToRefund == token && msg.sender != refundRecipient) return;

        // Saving to public storage to be accessed by the DepositReceiver
        refundToken = tokenToRefund;
        new DepositReceiver{salt: salt}(
            abi.encodeWithSelector(
                ReceiverImplementation.receiveAndFundAndRunMulticall.selector,
                token,
                calls,
                refundRecipient
            ),
            refundRecipient
        );

        refundToken = address(0);
    }

    function refundLockedAsset(address receiver, address token, uint256 amount) external {
        if (msg.sender != refundIssuer) revert NotRefundIssuer();
        if (receiver == address(0)) revert ZeroAddressProvided();

        if (token == nativeCoin) {
            (bool sent, ) = receiver.call{value: amount}("");
            if (!sent) revert NativeTransferFailed();
        } else {
            IERC20(token).safeTransfer(receiver, amount);
        }
    }

    function _depositAddress(
        bytes32 salt,
        bytes memory delegateData,
        address refundRecipient
    ) private view returns (address) {
        /* Convert a hash which is bytes32 to an address which is 20-byte long
        according to https://docs.soliditylang.org/en/v0.8.9/control-structures.html?highlight=create2#salted-contract-creations-create2 */
        return
            address(
                uint160(
                    uint256(
                        keccak256(
                            abi.encodePacked(
                                bytes1(0xff),
                                address(this),
                                salt,
                                // Encoding delegateData and refundRecipient as constructor params
                                keccak256(
                                    abi.encodePacked(
                                        type(DepositReceiver).creationCode,
                                        abi.encode(delegateData, refundRecipient)
                                    )
                                )
                            )
                        )
                    )
                )
            );
    }

    function contractId() external pure returns (bytes32) {
        return keccak256("squid-deposit-service");
    }
}

File 31 of 49 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.9;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    error InvalidAccount();

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 32 of 49 : IRoledPausable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface IRoledPausable {
    event PauserProposed(address indexed currentPauser, address indexed pendingPauser);
    event PauserUpdated(address indexed pendingPauser);
    event Paused();
    event Unpaused();

    error ContractIsPaused();
    error NotPauser();
    error NotPendingPauser();

    function updatePauser(address _newPauser) external;

    function acceptPauser() external;

    function pause() external;

    function unpause() external;

    function paused() external view returns (bool value);

    function pauser() external view returns (address value);

    function pendingPauser() external view returns (address value);
}

File 33 of 49 : ISquidDepositService.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.9;

import {IUpgradable} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IUpgradable.sol";
import {ISquidMulticall} from "./ISquidMulticall.sol";

// This should be owned by the microservice that is paying for gas.
interface ISquidDepositService is IUpgradable {
    error ZeroAddressProvided();
    error NotRefundIssuer();
    error NativeTransferFailed();

    function addressForBridgeCallDeposit(
        bytes32 salt,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool enableExpress
    ) external view returns (address);

    function addressForCallBridgeDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        address refundRecipient
    ) external view returns (address);

    function addressForCallBridgeCallDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool enableExpress
    ) external view returns (address);

    function addressForFundAndRunMulticallDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] memory calls,
        address refundRecipient
    ) external view returns (address);

    function bridgeCallDeposit(
        bytes32 salt,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool enableExpress
    ) external;

    function callBridgeDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        address refundRecipient
    ) external;

    function callBridgeCallDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool express
    ) external;

    function fundAndRunMulticallDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] memory calls,
        address refundRecipient
    ) external;

    function refundBridgeCallDeposit(
        bytes32 salt,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool express,
        address tokenToRefund
    ) external;

    function refundCallBridgeDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        address refundRecipient,
        address tokenToRefund
    ) external;

    function refundCallBridgeCallDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address refundRecipient,
        bool express,
        address tokenToRefund
    ) external;

    function refundFundAndRunMulticallDeposit(
        bytes32 salt,
        address token,
        ISquidMulticall.Call[] memory calls,
        address refundRecipient,
        address tokenToRefund
    ) external;

    function refundLockedAsset(address receiver, address token, uint256 amount) external;

    function receiverImplementation() external returns (address receiver);

    function refundToken() external returns (address);
}

File 34 of 49 : ISquidFeeCollector.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface ISquidFeeCollector {
    event FeeCollected(address token, address integrator, uint256 squidFee, uint256 integratorFee);
    event FeeWithdrawn(address token, address account, uint256 amount);

    error TransferFailed();
    error ExcessiveIntegratorFee();

    function collectFee(address token, uint256 amountToTax, address integratorAddress, uint256 integratorFee) external;

    function withdrawFee(address token) external;

    function getBalance(address token, address account) external view returns (uint256 accountBalance);
}

File 35 of 49 : ISquidMulticall.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface ISquidMulticall {
    enum CallType {
        Default,
        FullTokenBalance,
        FullNativeBalance,
        CollectTokenBalance
    }

    struct Call {
        CallType callType;
        address target;
        uint256 value;
        bytes callData;
        bytes payload;
    }

    error AlreadyRunning();
    error CallFailed(uint256 callPosition, bytes reason);

    function run(Call[] calldata calls) external payable;
}

File 36 of 49 : ISquidRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

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

interface ISquidRouter {
    event CrossMulticallExecuted(bytes32 indexed payloadHash);
    event CrossMulticallFailed(bytes32 indexed payloadHash, bytes reason, address indexed refundRecipient);

    error ZeroAddressProvided();
    error ApprovalFailed();

    function bridgeCall(
        string calldata bridgedTokenSymbol,
        uint256 amount,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address gasRefundRecipient,
        bool enableExpress
    ) external payable;

    function callBridge(
        address token,
        uint256 amount,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress
    ) external payable;

    function callBridgeCall(
        address token,
        uint256 amount,
        ISquidMulticall.Call[] calldata calls,
        string calldata bridgedTokenSymbol,
        string calldata destinationChain,
        string calldata destinationAddress,
        bytes calldata payload,
        address gasRefundRecipient,
        bool enableExpress
    ) external payable;

    function fundAndRunMulticall(address token, uint256 amount, ISquidMulticall.Call[] memory calls) external payable;
}

File 37 of 49 : IAggregationExecutor.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IAggregationExecutor {
    function callBytes(bytes calldata data) external payable; // 0xd9c45357

    // callbytes per swap sequence
    function swapSingleSequence(bytes calldata data) external;

    function finalTransactionProcessing(
        address tokenIn,
        address tokenOut,
        address to,
        bytes calldata destTokenFeeData
    ) external;
}

File 38 of 49 : IExecutorHelper1.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IExecutorHelper1 {
    struct UniSwap {
        address pool;
        address tokenIn;
        address tokenOut;
        address recipient;
        uint256 collectAmount; // amount that should be transferred to the pool
        uint256 limitReturnAmount;
        uint32 swapFee;
        uint32 feePrecision;
        uint32 tokenWeightInput;
    }

    struct StableSwap {
        address pool;
        address tokenFrom;
        address tokenTo;
        uint8 tokenIndexFrom;
        uint8 tokenIndexTo;
        uint256 dx;
        uint256 minDy;
        uint256 poolLength;
        address poolLp;
        bool isSaddle; // true: saddle, false: stable
    }

    struct CurveSwap {
        address pool;
        address tokenFrom;
        address tokenTo;
        int128 tokenIndexFrom;
        int128 tokenIndexTo;
        uint256 dx;
        uint256 minDy;
        bool usePoolUnderlying;
        bool useTriCrypto;
    }

    struct UniSwapV3ProMM {
        address recipient;
        address pool;
        address tokenIn;
        address tokenOut;
        uint256 swapAmount;
        uint256 limitReturnAmount;
        uint160 sqrtPriceLimitX96;
        bool isUniV3; // true = UniV3, false = ProMM
    }

    struct SwapCallbackData {
        bytes path;
        address payer;
    }

    struct SwapCallbackDataPath {
        address pool;
        address tokenIn;
        address tokenOut;
    }

    struct BalancerV2 {
        address vault;
        bytes32 poolId;
        address assetIn;
        address assetOut;
        uint256 amount;
        uint256 limit;
    }

    struct KyberRFQ {
        address rfq;
        bytes order;
        bytes signature;
        uint256 amount;
        address payable target;
    }

    struct DODO {
        address recipient;
        address pool;
        address tokenFrom;
        address tokenTo;
        uint256 amount;
        uint256 minReceiveQuote;
        address sellHelper;
        bool isSellBase;
        bool isVersion2;
    }

    struct GMX {
        address vault;
        address tokenIn;
        address tokenOut;
        uint256 amount;
        uint256 minOut;
        address receiver;
    }

    struct Synthetix {
        address synthetixProxy;
        address tokenIn;
        address tokenOut;
        bytes32 sourceCurrencyKey;
        uint256 sourceAmount;
        bytes32 destinationCurrencyKey;
        uint256 minAmount;
        bool useAtomicExchange;
    }

    function executeUniSwap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);

    function executeStableSwap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);

    function executeCurveSwap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);

    function executeKyberDMMSwap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);

    function executeUniV3ProMMSwap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);

    function executeRfqSwap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);

    function executeBalV2Swap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);

    function executeDODOSwap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);

    function executeVelodromeSwap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);

    function executeGMXSwap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);

    function executeSynthetixSwap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);

    function executeHashflowSwap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);

    function executeCamelotSwap(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);
}

File 39 of 49 : IExecutorHelper2.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IExecutorHelper2 {
    function executeKyberLimitOrder(
        uint256 index,
        bytes memory data,
        uint256 previousAmountOut
    ) external payable returns (uint256);
}

File 40 of 49 : IMetaAggregationRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IAggregationExecutor} from "./IAggregationExecutor.sol";

interface IMetaAggregationRouter {
    struct SwapDescription {
        IERC20 srcToken;
        IERC20 dstToken;
        address[] srcReceivers;
        uint256[] srcAmounts;
        address dstReceiver;
        uint256 amount;
        uint256 minReturnAmount;
        uint256 flags;
        bytes permit;
    }

    function swap(
        IAggregationExecutor caller,
        SwapDescription calldata desc,
        bytes calldata executorData,
        bytes calldata clientData
    ) external payable returns (uint256, uint256);

    function swapSimpleMode(
        IAggregationExecutor caller,
        SwapDescription calldata desc,
        bytes calldata executorData,
        bytes calldata clientData
    ) external returns (uint256, uint256);
}

File 41 of 49 : IMetaAggregationRouterV2.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IAggregationExecutor} from "./IAggregationExecutor.sol";

interface IMetaAggregationRouterV2 {
    struct SwapDescriptionV2 {
        IERC20 srcToken;
        IERC20 dstToken;
        address[] srcReceivers; // transfer src token to these addresses, default
        uint256[] srcAmounts;
        address[] feeReceivers;
        uint256[] feeAmounts;
        address dstReceiver;
        uint256 amount;
        uint256 minReturnAmount;
        uint256 flags;
        bytes permit;
    }

    /// @dev  use for swapGeneric and swap to avoid stack too deep
    struct SwapExecutionParams {
        address callTarget; // call this address
        address approveTarget; // approve this address if _APPROVE_FUND set
        bytes targetData;
        SwapDescriptionV2 desc;
        bytes clientData;
    }

    function swap(SwapExecutionParams calldata execution) external payable returns (uint256, uint256);

    function swapSimpleMode(
        IAggregationExecutor caller,
        SwapDescriptionV2 memory desc,
        bytes calldata executorData,
        bytes calldata clientData
    ) external returns (uint256, uint256);
}

File 42 of 49 : ScaleDataHelper1.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IExecutorHelper1} from "../../interfaces/kyberswap/IExecutorHelper1.sol";

library ScaleDataHelper1 {
    function newUniSwap(bytes memory data, uint256 oldAmount, uint256 newAmount) internal pure returns (bytes memory) {
        IExecutorHelper1.UniSwap memory uniSwap = abi.decode(data, (IExecutorHelper1.UniSwap));
        uniSwap.collectAmount = (uniSwap.collectAmount * newAmount) / oldAmount;
        return abi.encode(uniSwap);
    }

    function newStableSwap(
        bytes memory data,
        uint256 oldAmount,
        uint256 newAmount
    ) internal pure returns (bytes memory) {
        IExecutorHelper1.StableSwap memory stableSwap = abi.decode(data, (IExecutorHelper1.StableSwap));
        stableSwap.dx = (stableSwap.dx * newAmount) / oldAmount;
        return abi.encode(stableSwap);
    }

    function newCurveSwap(
        bytes memory data,
        uint256 oldAmount,
        uint256 newAmount
    ) internal pure returns (bytes memory) {
        IExecutorHelper1.CurveSwap memory curveSwap = abi.decode(data, (IExecutorHelper1.CurveSwap));
        curveSwap.dx = (curveSwap.dx * newAmount) / oldAmount;
        return abi.encode(curveSwap);
    }

    function newKyberDMM(bytes memory data, uint256 oldAmount, uint256 newAmount) internal pure returns (bytes memory) {
        IExecutorHelper1.UniSwap memory kyberDMMSwap = abi.decode(data, (IExecutorHelper1.UniSwap));
        kyberDMMSwap.collectAmount = (kyberDMMSwap.collectAmount * newAmount) / oldAmount;
        return abi.encode(kyberDMMSwap);
    }

    function newUniV3ProMM(
        bytes memory data,
        uint256 oldAmount,
        uint256 newAmount
    ) internal pure returns (bytes memory) {
        IExecutorHelper1.UniSwapV3ProMM memory uniSwapV3ProMM = abi.decode(data, (IExecutorHelper1.UniSwapV3ProMM));
        uniSwapV3ProMM.swapAmount = (uniSwapV3ProMM.swapAmount * newAmount) / oldAmount;

        return abi.encode(uniSwapV3ProMM);
    }

    function newBalancerV2(
        bytes memory data,
        uint256 oldAmount,
        uint256 newAmount
    ) internal pure returns (bytes memory) {
        IExecutorHelper1.BalancerV2 memory balancerV2 = abi.decode(data, (IExecutorHelper1.BalancerV2));
        balancerV2.amount = (balancerV2.amount * newAmount) / oldAmount;
        return abi.encode(balancerV2);
    }

    function newDODO(bytes memory data, uint256 oldAmount, uint256 newAmount) internal pure returns (bytes memory) {
        IExecutorHelper1.DODO memory dodo = abi.decode(data, (IExecutorHelper1.DODO));
        dodo.amount = (dodo.amount * newAmount) / oldAmount;
        return abi.encode(dodo);
    }

    function newVelodrome(
        bytes memory data,
        uint256 oldAmount,
        uint256 newAmount
    ) internal pure returns (bytes memory) {
        IExecutorHelper1.UniSwap memory velodrome = abi.decode(data, (IExecutorHelper1.UniSwap));
        velodrome.collectAmount = (velodrome.collectAmount * newAmount) / oldAmount;
        return abi.encode(velodrome);
    }

    function newGMX(bytes memory data, uint256 oldAmount, uint256 newAmount) internal pure returns (bytes memory) {
        IExecutorHelper1.GMX memory gmx = abi.decode(data, (IExecutorHelper1.GMX));
        gmx.amount = (gmx.amount * newAmount) / oldAmount;
        return abi.encode(gmx);
    }

    function newSynthetix(
        bytes memory data,
        uint256 oldAmount,
        uint256 newAmount
    ) internal pure returns (bytes memory) {
        IExecutorHelper1.Synthetix memory synthetix = abi.decode(data, (IExecutorHelper1.Synthetix));
        synthetix.sourceAmount = (synthetix.sourceAmount * newAmount) / oldAmount;
        return abi.encode(synthetix);
    }

    function newCamelot(bytes memory data, uint256 oldAmount, uint256 newAmount) internal pure returns (bytes memory) {
        IExecutorHelper1.UniSwap memory camelot = abi.decode(data, (IExecutorHelper1.UniSwap));
        camelot.collectAmount = (camelot.collectAmount * newAmount) / oldAmount;
        return abi.encode(camelot);
    }
}

File 43 of 49 : RoledPausable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IRoledPausable} from "../interfaces/IRoledPausable.sol";
import {StorageSlot} from "./StorageSlot.sol";

abstract contract RoledPausable is IRoledPausable {
    using StorageSlot for bytes32;

    bytes32 internal constant PAUSED_SLOT = keccak256("RoledPausable.paused");
    bytes32 internal constant PAUSER_SLOT = keccak256("RoledPausable.pauser");
    bytes32 internal constant PENDING_PAUSER_SLOT = keccak256("RoledPausable.pendingPauser");

    modifier whenNotPaused() {
        if (paused()) revert ContractIsPaused();
        _;
    }

    modifier onlyPauser() {
        if (msg.sender != pauser()) revert NotPauser();
        _;
    }

    constructor() {
        _setPauser(msg.sender);
    }

    function updatePauser(address newPauser) external onlyPauser {
        PENDING_PAUSER_SLOT.setAddress(newPauser);
        emit PauserProposed(msg.sender, newPauser);
    }

    function acceptPauser() external {
        if (msg.sender != pendingPauser()) revert NotPendingPauser();
        _setPauser(msg.sender);
        PENDING_PAUSER_SLOT.setAddress(address(0));
    }

    function pause() external virtual onlyPauser {
        PAUSED_SLOT.setBool(true);
        emit Paused();
    }

    function unpause() external virtual onlyPauser {
        PAUSED_SLOT.setBool(false);
        emit Unpaused();
    }

    function pauser() public view returns (address value) {
        value = PAUSER_SLOT.getAddress();
    }

    function paused() public view returns (bool value) {
        value = PAUSED_SLOT.getBool();
    }

    function pendingPauser() public view returns (address value) {
        value = PENDING_PAUSER_SLOT.getAddress();
    }

    function _setPauser(address _pauser) internal {
        PAUSER_SLOT.setAddress(_pauser);
        emit PauserUpdated(_pauser);
    }
}

File 44 of 49 : StorageSlot.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

library StorageSlot {
    function setUint256(bytes32 slot, uint256 value) internal {
        assembly {
            sstore(slot, value)
        }
    }

    function getUint256(bytes32 slot) internal view returns (uint256 value) {
        assembly {
            value := sload(slot)
        }
    }

    function setAddress(bytes32 slot, address value) internal {
        assembly {
            sstore(slot, value)
        }
    }

    function getAddress(bytes32 slot) internal view returns (address value) {
        assembly {
            value := sload(slot)
        }
    }

    function setBool(bytes32 slot, bool value) internal {
        assembly {
            sstore(slot, value)
        }
    }

    function getBool(bytes32 slot) internal view returns (bool value) {
        assembly {
            value := sload(slot)
        }
    }
}

File 45 of 49 : KyberswapPatcher.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IExecutorHelper1} from "../interfaces/kyberswap/IExecutorHelper1.sol";
import {IExecutorHelper2} from "../interfaces/kyberswap/IExecutorHelper2.sol";
import {IMetaAggregationRouterV2} from "../interfaces/kyberswap/IMetaAggregationRouterV2.sol";
import {IMetaAggregationRouter} from "../interfaces/kyberswap/IMetaAggregationRouter.sol";
import {ScaleDataHelper1} from "../libraries/kyberswap/ScaleDataHelper1.sol";

contract KyberswapPatcher {
    uint256 private constant _PARTIAL_FILL = 0x01;
    uint256 private constant _REQUIRES_EXTRA_ETH = 0x02;
    uint256 private constant _SHOULD_CLAIM = 0x04;
    uint256 private constant _BURN_FROM_MSG_SENDER = 0x08;
    uint256 private constant _BURN_FROM_TX_ORIGIN = 0x10;
    uint256 private constant _SIMPLE_SWAP = 0x20;

    struct Swap {
        bytes data;
        bytes4 functionSelector;
    }

    struct SimpleSwapData {
        address[] firstPools;
        uint256[] firstSwapAmounts;
        bytes[] swapDatas;
        uint256 deadline;
        bytes destTokenFeeData;
    }

    struct SwapExecutorDescription {
        Swap[][] swapSequences;
        address tokenIn;
        address tokenOut;
        uint256 minTotalAmountOut;
        address to;
        uint256 deadline;
        bytes destTokenFeeData;
    }

    struct Data {
        address router;
        bytes inputData;
        uint256 newAmount;
    }

    error CallFailed(string message, bytes reason);

    function safeTransferFrom(address token, address from, address to, uint256 value) internal {
        if (value == 0) return;
        (bool success, bytes memory data) = token.call(
            abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)
        );
        require(success && (data.length == 0 || abi.decode(data, (bool))), "safeTransferFrom: Transfer from fail");
    }

    function safeApprove(address token, address to, uint256 value) internal {
        if (value == 0) return;
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "safeApprove: Approve fail");
    }

    function scaleAndSwap(uint256 newAmount, address router, bytes calldata inputData) external payable {
        bytes4 selector = bytes4(inputData[:4]);
        bytes memory dataToDecode = new bytes(inputData.length - 4);
        bytes memory callData;

        for (uint256 i = 0; i < inputData.length - 4; ++i) {
            dataToDecode[i] = inputData[i + 4];
        }

        if (
            selector == IMetaAggregationRouter.swap.selector ||
            selector == IMetaAggregationRouter.swapSimpleMode.selector
        ) {
            (
                address callTarget,
                IMetaAggregationRouter.SwapDescription memory desc,
                bytes memory targetData,
                bytes memory clientData
            ) = abi.decode(dataToDecode, (address, IMetaAggregationRouter.SwapDescription, bytes, bytes));

            (desc, targetData) = _getScaledInputDataV1(
                desc,
                targetData,
                newAmount,
                selector == IMetaAggregationRouter.swapSimpleMode.selector || _flagsChecked(desc.flags, _SIMPLE_SWAP)
            );
            callData = abi.encodeWithSelector(selector, callTarget, desc, targetData, clientData);

            safeTransferFrom(address(desc.srcToken), msg.sender, address(this), newAmount);
            safeApprove(address(desc.srcToken), router, newAmount);
        } else if (selector == IMetaAggregationRouterV2.swap.selector) {
            IMetaAggregationRouterV2.SwapExecutionParams memory params = abi.decode(
                dataToDecode,
                (IMetaAggregationRouterV2.SwapExecutionParams)
            );

            (params.desc, params.targetData) = _getScaledInputDataV2(
                params.desc,
                params.targetData,
                newAmount,
                _flagsChecked(params.desc.flags, _SIMPLE_SWAP)
            );
            callData = abi.encodeWithSelector(selector, params);

            safeTransferFrom(address(params.desc.srcToken), msg.sender, address(this), newAmount);
            safeApprove(address(params.desc.srcToken), router, newAmount);
        } else if (selector == IMetaAggregationRouterV2.swapSimpleMode.selector) {
            (
                address callTarget,
                IMetaAggregationRouterV2.SwapDescriptionV2 memory desc,
                bytes memory targetData,
                bytes memory clientData
            ) = abi.decode(dataToDecode, (address, IMetaAggregationRouterV2.SwapDescriptionV2, bytes, bytes));

            (desc, targetData) = _getScaledInputDataV2(desc, targetData, newAmount, true);
            callData = abi.encodeWithSelector(selector, callTarget, desc, targetData, clientData);

            safeTransferFrom(address(desc.srcToken), msg.sender, address(this), newAmount);
            safeApprove(address(desc.srcToken), router, newAmount);
        } else revert("KyberswapPatcher: Invalid selector");

        (bool success, bytes memory data) = router.call(callData);
        if (!success) revert CallFailed("KyberswapPatcher: call failed", data);
    }

    function _getScaledInputDataV1(
        IMetaAggregationRouter.SwapDescription memory desc,
        bytes memory executorData,
        uint256 newAmount,
        bool isSimpleMode
    ) internal pure returns (IMetaAggregationRouter.SwapDescription memory, bytes memory) {
        uint256 oldAmount = desc.amount;
        if (oldAmount == newAmount) {
            return (desc, executorData);
        }

        // simple mode swap
        if (isSimpleMode) {
            return (
                _scaledSwapDescriptionV1(desc, oldAmount, newAmount),
                _scaledSimpleSwapData(executorData, oldAmount, newAmount)
            );
        }

        //normal mode swap
        return (
            _scaledSwapDescriptionV1(desc, oldAmount, newAmount),
            _scaledExecutorCallBytesData(executorData, oldAmount, newAmount)
        );
    }

    function _getScaledInputDataV2(
        IMetaAggregationRouterV2.SwapDescriptionV2 memory desc,
        bytes memory executorData,
        uint256 newAmount,
        bool isSimpleMode
    ) internal pure returns (IMetaAggregationRouterV2.SwapDescriptionV2 memory, bytes memory) {
        uint256 oldAmount = desc.amount;
        if (oldAmount == newAmount) {
            return (desc, executorData);
        }

        // simple mode swap
        if (isSimpleMode) {
            return (
                _scaledSwapDescriptionV2(desc, oldAmount, newAmount),
                _scaledSimpleSwapData(executorData, oldAmount, newAmount)
            );
        }

        //normal mode swap
        return (
            _scaledSwapDescriptionV2(desc, oldAmount, newAmount),
            _scaledExecutorCallBytesData(executorData, oldAmount, newAmount)
        );
    }

    function _scaledSwapDescriptionV1(
        IMetaAggregationRouter.SwapDescription memory desc,
        uint256 oldAmount,
        uint256 newAmount
    ) internal pure returns (IMetaAggregationRouter.SwapDescription memory) {
        desc.minReturnAmount = (desc.minReturnAmount * newAmount) / oldAmount;
        if (desc.minReturnAmount == 0) desc.minReturnAmount = 1;
        desc.amount = newAmount;
        for (uint256 i = 0; i < desc.srcReceivers.length; i++) {
            desc.srcAmounts[i] = (desc.srcAmounts[i] * newAmount) / oldAmount;
        }
        return desc;
    }

    function _scaledSwapDescriptionV2(
        IMetaAggregationRouterV2.SwapDescriptionV2 memory desc,
        uint256 oldAmount,
        uint256 newAmount
    ) internal pure returns (IMetaAggregationRouterV2.SwapDescriptionV2 memory) {
        desc.minReturnAmount = (desc.minReturnAmount * newAmount) / oldAmount;
        if (desc.minReturnAmount == 0) desc.minReturnAmount = 1;
        desc.amount = newAmount;
        for (uint256 i = 0; i < desc.srcReceivers.length; i++) {
            desc.srcAmounts[i] = (desc.srcAmounts[i] * newAmount) / oldAmount;
        }
        return desc;
    }

    function _scaledSimpleSwapData(
        bytes memory data,
        uint256 oldAmount,
        uint256 newAmount
    ) internal pure returns (bytes memory) {
        SimpleSwapData memory swapData = abi.decode(data, (SimpleSwapData));
        for (uint256 i = 0; i < swapData.firstPools.length; i++) {
            swapData.firstSwapAmounts[i] = (swapData.firstSwapAmounts[i] * newAmount) / oldAmount;
        }
        return abi.encode(swapData);
    }

    function _scaledExecutorCallBytesData(
        bytes memory data,
        uint256 oldAmount,
        uint256 newAmount
    ) internal pure returns (bytes memory) {
        SwapExecutorDescription memory executorDesc = abi.decode(data, (SwapExecutorDescription));
        executorDesc.minTotalAmountOut = (executorDesc.minTotalAmountOut * newAmount) / oldAmount;
        for (uint256 i = 0; i < executorDesc.swapSequences.length; i++) {
            Swap memory swap = executorDesc.swapSequences[i][0];
            bytes4 functionSelector = swap.functionSelector;

            if (functionSelector == IExecutorHelper1.executeUniSwap.selector) {
                swap.data = ScaleDataHelper1.newUniSwap(swap.data, oldAmount, newAmount);
            } else if (functionSelector == IExecutorHelper1.executeStableSwap.selector) {
                swap.data = ScaleDataHelper1.newStableSwap(swap.data, oldAmount, newAmount);
            } else if (functionSelector == IExecutorHelper1.executeCurveSwap.selector) {
                swap.data = ScaleDataHelper1.newCurveSwap(swap.data, oldAmount, newAmount);
            } else if (functionSelector == IExecutorHelper1.executeKyberDMMSwap.selector) {
                swap.data = ScaleDataHelper1.newKyberDMM(swap.data, oldAmount, newAmount);
            } else if (functionSelector == IExecutorHelper1.executeUniV3ProMMSwap.selector) {
                swap.data = ScaleDataHelper1.newUniV3ProMM(swap.data, oldAmount, newAmount);
            } else if (functionSelector == IExecutorHelper1.executeRfqSwap.selector) {
                revert("KyberswapPatcher: Can not scale RFQ swap");
            } else if (functionSelector == IExecutorHelper1.executeBalV2Swap.selector) {
                swap.data = ScaleDataHelper1.newBalancerV2(swap.data, oldAmount, newAmount);
            } else if (functionSelector == IExecutorHelper1.executeDODOSwap.selector) {
                swap.data = ScaleDataHelper1.newDODO(swap.data, oldAmount, newAmount);
            } else if (functionSelector == IExecutorHelper1.executeVelodromeSwap.selector) {
                swap.data = ScaleDataHelper1.newVelodrome(swap.data, oldAmount, newAmount);
            } else if (functionSelector == IExecutorHelper1.executeGMXSwap.selector) {
                swap.data = ScaleDataHelper1.newGMX(swap.data, oldAmount, newAmount);
            } else if (functionSelector == IExecutorHelper1.executeSynthetixSwap.selector) {
                swap.data = ScaleDataHelper1.newSynthetix(swap.data, oldAmount, newAmount);
            } else if (functionSelector == IExecutorHelper1.executeHashflowSwap.selector) {
                revert("KyberswapPatcher: Can not scale RFQ swap");
            } else if (functionSelector == IExecutorHelper1.executeCamelotSwap.selector) {
                swap.data = ScaleDataHelper1.newCamelot(swap.data, oldAmount, newAmount);
            } else if (functionSelector == IExecutorHelper2.executeKyberLimitOrder.selector) {
                revert("KyberswapPatcher: Can not scale RFQ swap");
            } else revert("AggregationExecutor: Dex type not supported");
        }
        return abi.encode(executorDesc);
    }

    function _flagsChecked(uint256 number, uint256 flag) internal pure returns (bool) {
        return number & flag != 0;
    }
}

File 46 of 49 : SquidFeeCollector.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {Upgradable} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol";
import {ISquidFeeCollector} from "../interfaces/ISquidFeeCollector.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract SquidFeeCollector is ISquidFeeCollector, Upgradable {
    bytes32 private constant BALANCES_PREFIX = keccak256("SquidFeeCollector.balances");
    bytes32 private constant SPECIFIC_FEES_PREFIX = keccak256("SquidFeeCollector.specificFees");
    address public immutable squidTeam;
    // Value expected with 2 decimals
    /// eg. 825 is 8.25%
    uint256 public immutable squidDefaultFee;

    error ZeroAddressProvided();

    constructor(address _squidTeam, uint256 _squidDefaultFee) {
        if (_squidTeam == address(0)) revert ZeroAddressProvided();

        squidTeam = _squidTeam;
        squidDefaultFee = _squidDefaultFee;
    }

    /// @param integratorFee Value expected with 2 decimals
    /// eg. 825 is 8.25%
    function collectFee(address token, uint256 amountToTax, address integratorAddress, uint256 integratorFee) external {
        if (integratorFee > 1000) revert ExcessiveIntegratorFee();

        uint256 specificFee = getSpecificFee(integratorAddress);
        uint256 squidFee = specificFee == 0 ? squidDefaultFee : specificFee;

        uint256 baseFeeAmount = (amountToTax * integratorFee) / 10000;
        uint256 squidFeeAmount = (baseFeeAmount * squidFee) / 10000;
        uint256 integratorFeeAmount = baseFeeAmount - squidFeeAmount;

        _safeTransferFrom(token, msg.sender, baseFeeAmount);
        _setBalance(token, squidTeam, getBalance(token, squidTeam) + squidFeeAmount);
        _setBalance(token, integratorAddress, getBalance(token, integratorAddress) + integratorFeeAmount);

        emit FeeCollected(token, integratorAddress, squidFeeAmount, integratorFeeAmount);
    }

    function withdrawFee(address token) external {
        uint256 balance = getBalance(token, msg.sender);
        _setBalance(token, msg.sender, 0);
        _safeTransfer(token, msg.sender, balance);

        emit FeeWithdrawn(token, msg.sender, balance);
    }

    function setSpecificFee(address integrator, uint256 fee) external onlyOwner {
        bytes32 slot = _computeSpecificFeeSlot(integrator);
        assembly {
            sstore(slot, fee)
        }
    }

    function getBalance(address token, address account) public view returns (uint256 value) {
        bytes32 slot = _computeBalanceSlot(token, account);
        assembly {
            value := sload(slot)
        }
    }

    function getSpecificFee(address integrator) public view returns (uint256 value) {
        bytes32 slot = _computeSpecificFeeSlot(integrator);
        assembly {
            value := sload(slot)
        }
    }

    function contractId() external pure returns (bytes32 id) {
        id = keccak256("squid-fee-collector");
    }

    function _setBalance(address token, address account, uint256 amount) private {
        bytes32 slot = _computeBalanceSlot(token, account);
        assembly {
            sstore(slot, amount)
        }
    }

    function _computeBalanceSlot(address token, address account) private pure returns (bytes32 slot) {
        slot = keccak256(abi.encodePacked(BALANCES_PREFIX, token, account));
    }

    function _computeSpecificFeeSlot(address integrator) private pure returns (bytes32 slot) {
        slot = keccak256(abi.encodePacked(SPECIFIC_FEES_PREFIX, integrator));
    }

    function _safeTransferFrom(address token, address from, uint256 amount) internal {
        (bool success, bytes memory returnData) = token.call(
            abi.encodeWithSelector(IERC20.transferFrom.selector, from, address(this), amount)
        );
        bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
        if (!transferred || token.code.length == 0) revert TransferFailed();
    }

    function _safeTransfer(address token, address to, uint256 amount) internal {
        (bool success, bytes memory returnData) = token.call(
            abi.encodeWithSelector(IERC20.transfer.selector, to, amount)
        );
        bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
        if (!transferred || token.code.length == 0) revert TransferFailed();
    }
}

File 47 of 49 : SquidFeeCollectorProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {InitProxy} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/InitProxy.sol";

contract SquidFeeCollectorProxy is InitProxy {
    function contractId() internal pure override returns (bytes32 id) {
        id = keccak256("squid-fee-collector");
    }
}

File 48 of 49 : SquidMulticall.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {ISquidMulticall} from "../interfaces/ISquidMulticall.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";

contract SquidMulticall is ISquidMulticall, IERC721Receiver, IERC1155Receiver {
    bytes4 private constant ERC165_INTERFACE_ID = 0x01ffc9a7;
    bytes4 private constant ERC721_TOKENRECEIVER_INTERFACE_ID = 0x150b7a02;
    bytes4 private constant ERC1155_TOKENRECEIVER_INTERFACE_ID = 0x4e2312e0;

    bool private isRunning;

    error TransferFailed();

    function run(Call[] calldata calls) external payable {
        // Prevents reentrancy
        if (isRunning) revert AlreadyRunning();
        isRunning = true;

        for (uint256 i = 0; i < calls.length; i++) {
            Call memory call = calls[i];

            if (call.callType == CallType.FullTokenBalance) {
                (address token, uint256 amountParameterPosition) = abi.decode(call.payload, (address, uint256));
                uint256 amount = IERC20(token).balanceOf(address(this));
                _setCallDataParameter(call.callData, amountParameterPosition, amount);
            } else if (call.callType == CallType.FullNativeBalance) {
                call.value = address(this).balance;
            } else if (call.callType == CallType.CollectTokenBalance) {
                address token = abi.decode(call.payload, (address));
                _safeTransferFrom(token, msg.sender, IERC20(token).balanceOf(msg.sender));
                continue;
            }

            (bool success, bytes memory data) = call.target.call{value: call.value}(call.callData);
            if (!success) revert CallFailed(i, data);
        }

        isRunning = false;
    }

    function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
        return
            interfaceId == ERC1155_TOKENRECEIVER_INTERFACE_ID ||
            interfaceId == ERC721_TOKENRECEIVER_INTERFACE_ID ||
            interfaceId == ERC165_INTERFACE_ID;
    }

    function _safeTransferFrom(address token, address from, uint256 amount) private {
        (bool success, bytes memory returnData) = token.call(
            abi.encodeWithSelector(IERC20.transferFrom.selector, from, address(this), amount)
        );
        bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
        if (!transferred || token.code.length == 0) revert TransferFailed();
    }

    function _setCallDataParameter(bytes memory callData, uint256 parameterPosition, uint256 value) private pure {
        assembly {
            // 36 bytes shift because 32 for prefix + 4 for selector
            mstore(add(callData, add(36, mul(parameterPosition, 32))), value)
        }
    }

    function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
        return IERC721Receiver.onERC721Received.selector;
    }

    function onERC1155Received(address, address, uint256, uint256, bytes calldata) external pure returns (bytes4) {
        return IERC1155Receiver.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address,
        address,
        uint256[] calldata,
        uint256[] calldata,
        bytes calldata
    ) external pure returns (bytes4) {
        return IERC1155Receiver.onERC1155BatchReceived.selector;
    }

    // Required to enable ETH reception with .transfer or .send
    receive() external payable {}
}

File 49 of 49 : SquidRouterProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {InitProxy} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/InitProxy.sol";

contract SquidRouterProxy is InitProxy {
    function contractId() internal pure override returns (bytes32 id) {
        id = keccak256("squid-router");
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 99999
  },
  "viaIR": true,
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_gateway","type":"address"},{"internalType":"address","name":"_gasService","type":"address"},{"internalType":"address","name":"_multicall","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyExecuted","type":"error"},{"inputs":[],"name":"ApprovalFailed","type":"error"},{"inputs":[],"name":"ContractIsPaused","type":"error"},{"inputs":[],"name":"ExpressExecutorAlreadySet","type":"error"},{"inputs":[],"name":"InsufficientValue","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidCodeHash","type":"error"},{"inputs":[],"name":"InvalidImplementation","type":"error"},{"inputs":[],"name":"InvalidOwner","type":"error"},{"inputs":[],"name":"InvalidOwnerAddress","type":"error"},{"inputs":[],"name":"NotApprovedByGateway","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"NotPauser","type":"error"},{"inputs":[],"name":"NotPendingPauser","type":"error"},{"inputs":[],"name":"NotProxy","type":"error"},{"inputs":[],"name":"SetupFailed","type":"error"},{"inputs":[],"name":"TokenTransferFailed","type":"error"},{"inputs":[],"name":"ZeroAddressProvided","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"payloadHash","type":"bytes32"}],"name":"CrossMulticallExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"payloadHash","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"reason","type":"bytes"},{"indexed":true,"internalType":"address","name":"refundRecipient","type":"address"}],"name":"CrossMulticallFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"commandId","type":"bytes32"},{"indexed":false,"internalType":"string","name":"sourceChain","type":"string"},{"indexed":false,"internalType":"string","name":"sourceAddress","type":"string"},{"indexed":false,"internalType":"bytes32","name":"payloadHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"expressExecutor","type":"address"}],"name":"ExpressExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"commandId","type":"bytes32"},{"indexed":false,"internalType":"string","name":"sourceChain","type":"string"},{"indexed":false,"internalType":"string","name":"sourceAddress","type":"string"},{"indexed":false,"internalType":"bytes32","name":"payloadHash","type":"bytes32"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"expressExecutor","type":"address"}],"name":"ExpressExecutedWithToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"commandId","type":"bytes32"},{"indexed":false,"internalType":"string","name":"sourceChain","type":"string"},{"indexed":false,"internalType":"string","name":"sourceAddress","type":"string"},{"indexed":false,"internalType":"bytes32","name":"payloadHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"expressExecutor","type":"address"}],"name":"ExpressExecutionFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"commandId","type":"bytes32"},{"indexed":false,"internalType":"string","name":"sourceChain","type":"string"},{"indexed":false,"internalType":"string","name":"sourceAddress","type":"string"},{"indexed":false,"internalType":"bytes32","name":"payloadHash","type":"bytes32"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"expressExecutor","type":"address"}],"name":"ExpressExecutionWithTokenFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"currentPauser","type":"address"},{"indexed":true,"internalType":"address","name":"pendingPauser","type":"address"}],"name":"PauserProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingPauser","type":"address"}],"name":"PauserUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptPauser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"bridgedTokenSymbol","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"destinationChain","type":"string"},{"internalType":"string","name":"destinationAddress","type":"string"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"address","name":"gasRefundRecipient","type":"address"},{"internalType":"bool","name":"enableExpress","type":"bool"}],"name":"bridgeCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"enum ISquidMulticall.CallType","name":"callType","type":"uint8"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct ISquidMulticall.Call[]","name":"calls","type":"tuple[]"},{"internalType":"string","name":"bridgedTokenSymbol","type":"string"},{"internalType":"string","name":"destinationChain","type":"string"},{"internalType":"string","name":"destinationAddress","type":"string"}],"name":"callBridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"enum ISquidMulticall.CallType","name":"callType","type":"uint8"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct ISquidMulticall.Call[]","name":"calls","type":"tuple[]"},{"internalType":"string","name":"bridgedTokenSymbol","type":"string"},{"internalType":"string","name":"destinationChain","type":"string"},{"internalType":"string","name":"destinationAddress","type":"string"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"address","name":"gasRefundRecipient","type":"address"},{"internalType":"bool","name":"enableExpress","type":"bool"}],"name":"callBridgeCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"contractId","outputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commandId","type":"bytes32"},{"internalType":"string","name":"sourceChain","type":"string"},{"internalType":"string","name":"sourceAddress","type":"string"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"execute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commandId","type":"bytes32"},{"internalType":"string","name":"sourceChain","type":"string"},{"internalType":"string","name":"sourceAddress","type":"string"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"string","name":"tokenSymbol","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"executeWithToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commandId","type":"bytes32"},{"internalType":"string","name":"sourceChain","type":"string"},{"internalType":"string","name":"sourceAddress","type":"string"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"expressExecute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commandId","type":"bytes32"},{"internalType":"string","name":"sourceChain","type":"string"},{"internalType":"string","name":"sourceAddress","type":"string"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"expressExecuteWithToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"enum ISquidMulticall.CallType","name":"callType","type":"uint8"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct ISquidMulticall.Call[]","name":"calls","type":"tuple[]"}],"name":"fundAndRunMulticall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"gateway","outputs":[{"internalType":"contract IAxelarGateway","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commandId","type":"bytes32"},{"internalType":"string","name":"sourceChain","type":"string"},{"internalType":"string","name":"sourceAddress","type":"string"},{"internalType":"bytes32","name":"payloadHash","type":"bytes32"}],"name":"getExpressExecutor","outputs":[{"internalType":"address","name":"expressExecutor","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"commandId","type":"bytes32"},{"internalType":"string","name":"sourceChain","type":"string"},{"internalType":"string","name":"sourceAddress","type":"string"},{"internalType":"bytes32","name":"payloadHash","type":"bytes32"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getExpressExecutorWithToken","outputs":[{"internalType":"address","name":"expressExecutor","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"implementation_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"value","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauser","outputs":[{"internalType":"address","name":"value","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingPauser","outputs":[{"internalType":"address","name":"value","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"proposeOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"setup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPauser","type":"address"}],"name":"updatePauser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes32","name":"newImplementationCodeHash","type":"bytes32"},{"internalType":"bytes","name":"params","type":"bytes"}],"name":"upgrade","outputs":[],"stateMutability":"nonpayable","type":"function"}]

610100346200025057601f62003bef38819003918201601f19168301916001600160401b03831184841017620002555780849260609460405283398101031262000250576200004e816200026b565b906200006b604062000063602084016200026b565b92016200026b565b6001600160a01b0392909190831680156200023e57608052826040519160017f04dba622d284ed0014ee4b9a6a68386be1a4c08a4913ae272de89199cc686163600080a260017f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c05560007f9855384122b55936fbfb8ca5120e63c6537a1ac40caf6ae33502b3c5da8c87d1553060a052337f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c37955337fa4336c0cb1e245b95ad204faed7e940d6dc999684fd8b5e1ff597a0c4efca8ab600080a216908115801562000233575b62000224575060c0521660e05260405161396e9081620002818239608051818181610257015281816102ea01528181610470015281816111600152818161166c01528181611c7f01528181611ff2015281816122d2015281816124240152818161254a0152818161267901528181612991015261349f015260a05181610c80015260c0518181816135bb0152613672015260e05181818161051a0152818161105f015281816110d801528181611225015281816118ac0152818161197d01528181611c0501528181611e8901526127230152f35b638474420160e01b8152600490fd5b508383161562000150565b60405163e6c4247b60e01b8152600490fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620002505756fe60c080604052600436101561001357600080fd5b600090813560e01c908163116191b61461294757508063167a6f90146128615780631a98b2e01461235157806321477960146121985780633f4ba83a146120e15780634916065814611f3057806352c41eb614611ae8578063554bab3c14611a2457806358181a80146117f05780635c60da1b1461177f5780635c975abb14611722578063656576361461160f578063710bf3221461151b57806379ba50971461147a5780638291286c146114215780638456cb591461133f578063846a1bc614610ee8578063868a166d14610e475780638da5cb5b14610dd65780639a7165e414610d655780639ded06df14610c165780639fd0506d14610ba5578063a3499c73146108d6578063c7e6a3cc14610838578063e30c3978146107c7578063e4a974cc146101f95763f2fde38b1461014a57600080fd5b346101f65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f657610181612a8b565b7f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c05473ffffffffffffffffffffffffffffffffffffffff339116036101cc576101c9906131a5565b80f35b60046040517f30cd7471000000000000000000000000000000000000000000000000000000008152fd5b80fd5b50610203366129e3565b93969295604099959199989298517fd26ff21000000000000000000000000000000000000000000000000000000000815286600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9081156107bc578c9161078d575b50610763576102d1938b9360208b8d60405198899283927f935b13f60000000000000000000000000000000000000000000000000000000084528560048501526024840191612dfe565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa978815610758578a8a8e988e989b610703575b509282818b88957f5844b8bbe3fd2b0354e73f27bfde28d2e6d991f14139c382876ec4360391a47b8c9a8a6103869f9d9b98859f8f906103668f9e9361037e943691612c61565b602081519101209e8f90604051968796339c88612e6c565b0390a4612f74565b9173ffffffffffffffffffffffffffffffffffffffff8354166106d9576103c992339055309073ffffffffffffffffffffffffffffffffffffffff33911661313b565b8082016040838203126106d55782359067ffffffffffffffff821161064c576103f9602091610457938601612dcb565b9473ffffffffffffffffffffffffffffffffffffffff61041a838701612aae565b169660405193849283927f935b13f60000000000000000000000000000000000000000000000000000000084528560048501526024840191612dfe565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9081156106ca57869161069b575b508573ffffffffffffffffffffffffffffffffffffffff821694604051927f70a082310000000000000000000000000000000000000000000000000000000084523060048501526020846024818a5afa938415610690578394610654575b506105418473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168093613751565b803b156106505761058583929183926040519586809481937ff87ef8000000000000000000000000000000000000000000000000000000000083526004830161328d565b03925af19182610638575b50506105fb577fdd7b1484db8d21f4fbda2407f2920037dc379dd66e18b0851aa9d6c14ef493b9926105f5926105d26105d993886105cc61308e565b9861302c565b3691612c61565b602081519101209260405191829160208352602083019061322f565b0390a380f35b5061060a935036925090612c61565b602081519101207f7c3aa10c5d96985be6de7d2e6fa79bdef95a95a9cb272f4113b3fe1ca89fedae8280a280f35b61064190612b87565b61064c578638610590565b8680fd5b8280fd5b925092506020823d602011610688575b8161067160209383612be6565b810103126106835787915192386104fe565b600080fd5b3d9150610664565b6040513d85823e3d90fd5b6106bd915060203d6020116106c3575b6106b58183612be6565b810190612eac565b386104a0565b503d6106ab565b6040513d88823e3d90fd5b8580fd5b60046040517f725f13f1000000000000000000000000000000000000000000000000000000008152fd5b899b5092848b8288958a8c9a8e9f9d9a97859f9d9a60203d602011610751575b61072d8183612be6565b810161073891612eac565b9f5097505050959850959850509497509a98979a61031f565b503d610723565b6040513d87823e3d90fd5b60046040517f0dc10197000000000000000000000000000000000000000000000000000000008152fd5b6107af915060203d6020116107b5575b6107a78183612be6565b810190612de6565b38610287565b503d61079d565b6040513d8e823e3d90fd5b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760207f9855384122b55936fbfb8ca5120e63c6537a1ac40caf6ae33502b3c5da8c87d15473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b50346101f65760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65767ffffffffffffffff602435818111610650576108899036906004016129b5565b9190926044359182116101f65760206108b785856108aa36600488016129b5565b9160643593600435612ed8565b5473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b50346101f65760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65761090e612a8b565b60443567ffffffffffffffff81116106505761092e9036906004016129b5565b907f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c05473ffffffffffffffffffffffffffffffffffffffff9081339116036101cc578316604051907f8291286c000000000000000000000000000000000000000000000000000000008083526020928381600481865afa908115610b6d578891610b78575b506040519182528382600481305afa918215610b6d578892610b3e575b5003610b1457843f60243503610aea578593604051917fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8680a280610a37575b84867f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5580f35b81610a778695610aa393868401967f9ded06df00000000000000000000000000000000000000000000000000000000885260248501526044840191612dfe565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282612be6565b5190845af4610ab061308e565b5015610ac0578138808080610a10565b60046040517f97905dfb000000000000000000000000000000000000000000000000000000008152fd5b60046040517f8f84fb24000000000000000000000000000000000000000000000000000000008152fd5b60046040517f68155f9a000000000000000000000000000000000000000000000000000000008152fd5b9091508381813d8311610b66575b610b568183612be6565b81010312610683575190386109d0565b503d610b4c565b6040513d8a823e3d90fd5b90508381813d8311610b9e575b610b8f8183612be6565b810103126106835751386109b3565b503d610b85565b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760207f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c3795473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b50346101f65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760043567ffffffffffffffff8111610d6157610c669036906004016129b5565b9073ffffffffffffffffffffffffffffffffffffffff91827f0000000000000000000000000000000000000000000000000000000000000000163014610d3757816020918101031261065057610cbb90612aae565b168015610d0d57807f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c379557fa4336c0cb1e245b95ad204faed7e940d6dc999684fd8b5e1ff597a0c4efca8ab600080a280f35b60046040517f84744201000000000000000000000000000000000000000000000000000000008152fd5b60046040517fbf10dd3a000000000000000000000000000000000000000000000000000000008152fd5b5080fd5b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760207fe330cfee289e568e8774e923acd6e2a00f84be86aa1d02356835d33dbb9397025473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760207f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c05473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b50346101f65760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65767ffffffffffffffff60243581811161065057610e989036906004016129b5565b929060443583811161065057610eb29036906004016129b5565b9390926084359182116101f65760206108b787878787610ed53660048a016129b5565b93909260a4359560643593600435612f74565b506101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f657610f1c612a8b565b67ffffffffffffffff9060443582811161133b57610f3e903690600401612b56565b916064358481116106d557610f579036906004016129b5565b93909260843586811161133757610f729036906004016129b5565b60a49791973582811161133357610f8d9036906004016129b5565b93909260c43590811161132f57610fa89036906004016129b5565b96909573ffffffffffffffffffffffffffffffffffffffff60e4351660e43503610683576101043515156101043503610683577fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c54611305578b9261100e913691612cb6565b90829073ffffffffffffffffffffffffffffffffffffffff811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee036111ee5750506024355b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163b15610650576110bf91839160405180809581947ff87ef8000000000000000000000000000000000000000000000000000000000083526004830161328d565b039173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af180156111e3576111cb575b5050604051977f935b13f6000000000000000000000000000000000000000000000000000000008952602060048a015260208980611147602482018c8c612dfe565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa80156111c0576101c9998b916111a2575b50610104359860e43598613420565b6111ba915060203d81116106c3576106b58183612be6565b38611193565b6040513d8c823e3d90fd5b6111d490612b87565b6111df578838611105565b8880fd5b6040513d84823e3d90fd5b6040517f23b872dd0000000000000000000000000000000000000000000000000000000060208201908152336024808401919091527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1660448401523560648301529294919282916112788160848101610a77565b519082855af161128661308e565b816112d6575b50159081156112cc575b506112a2578a91611048565b60046040517f045c4b02000000000000000000000000000000000000000000000000000000008152fd5b90503b1538611296565b80518015925082156112eb575b50503861128c565b6112fe9250602080918301019101612de6565b38806112e3565b60046040517f6d39fcd0000000000000000000000000000000000000000000000000000000008152fd5b8a80fd5b8980fd5b8780fd5b8380fd5b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65773ffffffffffffffffffffffffffffffffffffffff7f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c379541633036113f75760017fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c557f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e7528180a180f35b60046040517f492f6781000000000000000000000000000000000000000000000000000000008152fd5b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760206040517fc097d45e5a99ca772ab5ec2e5457c2e249760944b95b0b97cbb6b03ec55bae848152f35b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f6577f9855384122b55936fbfb8ca5120e63c6537a1ac40caf6ae33502b3c5da8c87d1543373ffffffffffffffffffffffffffffffffffffffff8216036114f1576101c9906131a5565b60046040517f49e27cff000000000000000000000000000000000000000000000000000000008152fd5b50346101f65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f657611553612a8b565b7f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c05473ffffffffffffffffffffffffffffffffffffffff9081339116036101cc57811680156115e5577fd9be0e8e07417e00f2521db636cb53e316fd288f5051f16d2aa2bf0c3938a8768380a27f9855384122b55936fbfb8ca5120e63c6537a1ac40caf6ae33502b3c5da8c87d15580f35b60046040517fd924e5f4000000000000000000000000000000000000000000000000000000008152fd5b5061161936612acf565b95939094604093929351967fd26ff21000000000000000000000000000000000000000000000000000000000885285600489015273ffffffffffffffffffffffffffffffffffffffff976020816024818c7f0000000000000000000000000000000000000000000000000000000000000000165afa9081156111c0578a91611704575b50610763576116f6966116b0913691612c61565b6020815191012094604051817f6e18757e81c44a367109cbaa499add16f2ae7168aab9715c3cdc36b0f7ccce923392806116ee8b8b8b8b8b86612e3d565b0390a3612ed8565b908154166106d95733905580f35b61171c915060203d81116107b5576107a78183612be6565b3861169c565b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760207fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c546040519015158152f35b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760207f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f657611823612a8b565b6024359060443567ffffffffffffffff811161133b57611847903690600401612dcb565b7fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c546113055783928373ffffffffffffffffffffffffffffffffffffffff9373eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee85821614600014611935575050915b7f00000000000000000000000000000000000000000000000000000000000000001690813b1561193057839161190f916040519485809481937ff87ef8000000000000000000000000000000000000000000000000000000000083526004830161328d565b03925af180156111e3576119205750f35b61192990612b87565b6101f65780f35b505050fd5b6040517f23b872dd000000000000000000000000000000000000000000000000000000006020820190815233602483015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008816166044830152606482019490945294959194909282916119c18160848101610a77565b519082855af16119cf61308e565b816119f5575b50159081156119eb575b506112a25783926118aa565b90503b15386119df565b8051801592508215611a0a575b5050386119d5565b611a1d9250602080918301019101612de6565b3880611a02565b50346101f65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f657611a5c612a8b565b73ffffffffffffffffffffffffffffffffffffffff90817f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c379541633036113f757807fe330cfee289e568e8774e923acd6e2a00f84be86aa1d02356835d33dbb9397025516337f3210edd3f0fc490ffc59a4adae6f48dbda2d8e89afe5b37a0145a54762f3ecf98380a380f35b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60c081360112610d6157611b1c612a8b565b67ffffffffffffffff91602435916044358481116106d557611b42903690600401612b56565b909460643581811161133757611b5c9036906004016129b5565b9360843583811161133357611b759036906004016129b5565b91909360a43590811161132f57611b909036906004016129b5565b9890957fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c54611305578b9a611bc6913691612cb6565b908a73ffffffffffffffffffffffffffffffffffffffff9373eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee85821614600014611e41575050905b827f00000000000000000000000000000000000000000000000000000000000000001690813b15611e3d578b91611c68916040519485809481937ff87ef8000000000000000000000000000000000000000000000000000000000083526004830161328d565b03925af19081156111c0578a91611e29575b5050807f00000000000000000000000000000000000000000000000000000000000000001694604051917f935b13f6000000000000000000000000000000000000000000000000000000008352602092836004820152838180611ce1602482018d8b612dfe565b03818b5afa9081156107bc5784908d92611e09575b5060249192604051928380927f70a0823100000000000000000000000000000000000000000000000000000000825230600483015286165afa9384156107bc578c94611dd4575b50508287611d4a92613751565b853b15611333578997611d9e978995611dae611dbd946040519d8e9c8d9b8c9a7f26ef699d000000000000000000000000000000000000000000000000000000008c52608060048d015260848c0191612dfe565b91868a84030160248b0152612dfe565b92868403016044870152612dfe565b90606483015203925af180156111e3576119205750f35b819c50809294503d8311611e02575b611ded8183612be6565b810103126106835798518a9990918087611d3d565b503d611de3565b60249250611e2390823d84116106c3576106b58183612be6565b91611cf6565b611e3290612b87565b6111df578838611c7a565b8b80fd5b6040517f23b872dd000000000000000000000000000000000000000000000000000000006020820190815233602483015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000088161660448301526064820194909452939c919390928291611ecd8160848101610a77565b519082855af1611edb61308e565b81611f01575b5015908115611ef7575b506112a2578a99611c02565b90503b1538611eeb565b8051801592508215611f16575b505038611ee1565b611f299250602080918301019101612de6565b3880611f0e565b50346101f657611f4e611f4236612acf565b96919492963691612c61565b60208151910120946040517f5f6970c300000000000000000000000000000000000000000000000000000000815285600482015260806024820152611fcb611f9a608483018686612dfe565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8382030160448401528684612dfe565b908760648201526020818073ffffffffffffffffffffffffffffffffffffffff9403818c867f0000000000000000000000000000000000000000000000000000000000000000165af19081156120d65789916120b8575b501561208e5761203687868487878b612ed8565b8881549182612086575b50501695861561207c577f8fe61b2d4701a29265508750790e322b2c214399abdf98472158b8908b660d41946105f59260405195869586612e3d565b5050505050505080f35b558838612040565b60046040517f500c44b4000000000000000000000000000000000000000000000000000000008152fd5b6120d0915060203d81116107b5576107a78183612be6565b38612022565b6040513d8b823e3d90fd5b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65773ffffffffffffffffffffffffffffffffffffffff7f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c379541633036113f757807fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c557fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d169338180a180f35b5060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65767ffffffffffffffff600435818111610650576121e49036906004016129b5565b909160443581811161234d576121fe9036906004016129b5565b60649291923582811161064c576122199036906004016129b5565b9092608435908111611337576122339036906004016129b5565b93909260a4359573ffffffffffffffffffffffffffffffffffffffff80881688036106835760c435988915158a03610683577fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c54611305578b9a604051927f935b13f600000000000000000000000000000000000000000000000000000000845260206004850152602084806122cd602482018787612dfe565b0381847f0000000000000000000000000000000000000000000000000000000000000000165afa938415612342576101c99d9461231e575b50612319906024359030903390871661313b565b613420565b61231991945061233b9060203d81116106c3576106b58183612be6565b9390612305565b6040513d8f823e3d90fd5b8480fd5b50346101f657612360366129e3565b9791959098939692943661237590878a612c61565b8051906020012091898c898d60405193849283927f1876eed90000000000000000000000000000000000000000000000000000000084528c60048501526024840160c0905260c484016123c9908d8d612dfe565b907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9182868203016044870152612401908b8b612dfe565b918b606487015285830301608486015261241a92612dfe565b9060a483015203817f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16935a94602095f1908115612342578d91612842575b501561208e576124858a8c8a8686868b8b8e612f74565b988c8a549a8b61283a575b505073ffffffffffffffffffffffffffffffffffffffff8a169788156125c55750509389936124f38c8a6125319e99967fdb3db9dfc9262f4fe09dbadef104f799d8181ec565e09275d80ed3355aab68d39660209d9c9a60405197889788612e6c565b0390a460405195869283927f935b13f60000000000000000000000000000000000000000000000000000000084528560048501526024840191612dfe565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9283156125ba576101c99373ffffffffffffffffffffffffffffffffffffffff91869161259b575b501661302c565b6125b4915060203d6020116106c3576106b58183612be6565b38612594565b6040513d86823e3d90fd5b989a50985050505050505050808201926040838503126106d55782359367ffffffffffffffff851161064c57612602602091612660968601612dcb565b9173ffffffffffffffffffffffffffffffffffffffff612623838701612aae565b169660405196879283927f935b13f60000000000000000000000000000000000000000000000000000000084528560048501526024840191612dfe565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9384156106ca578694612819575b508573ffffffffffffffffffffffffffffffffffffffff851694604051927f70a082310000000000000000000000000000000000000000000000000000000084523060048501526020846024818a5afa9384156106905783946127e5575b5061274a8473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168094613751565b813b1561065057829161278c916040519485809481937ff87ef8000000000000000000000000000000000000000000000000000000000083526004830161328d565b03925af190816127d2575b506105fb577fdd7b1484db8d21f4fbda2407f2920037dc379dd66e18b0851aa9d6c14ef493b9926105f5926105d26105d993886105cc61308e565b6127de90979197612b87565b9538612797565b9093506020813d602011612811575b8161280160209383612be6565b8101031261065057519238612707565b3d91506127f4565b61283391945060203d6020116106c3576106b58183612be6565b92386126a9565b558c38612490565b61285b915060203d6020116107b5576107a78183612be6565b3861246e565b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f6577fe330cfee289e568e8774e923acd6e2a00f84be86aa1d02356835d33dbb93970273ffffffffffffffffffffffffffffffffffffffff815416330361291d578190337f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c37955337fa4336c0cb1e245b95ad204faed7e940d6dc999684fd8b5e1ff597a0c4efca8ab8380a25580f35b60046040517fb3a270d8000000000000000000000000000000000000000000000000000000008152fd5b905034610d6157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610d615760209073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b9181601f840112156106835782359167ffffffffffffffff8311610683576020838186019501011161068357565b9060c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc830112610683576004359167ffffffffffffffff906024358281116106835781612a33916004016129b5565b939093926044358181116106835783612a4e916004016129b5565b939093926064358381116106835782612a69916004016129b5565b9390939260843591821161068357612a83916004016129b5565b909160a43590565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361068357565b359073ffffffffffffffffffffffffffffffffffffffff8216820361068357565b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820112610683576004359167ffffffffffffffff6024358181116106835783612b1d916004016129b5565b939093926044358381116106835782612b38916004016129b5565b9390939260643591821161068357612b52916004016129b5565b9091565b9181601f840112156106835782359167ffffffffffffffff8311610683576020808501948460051b01011161068357565b67ffffffffffffffff8111612b9b57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff821117612b9b57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612b9b57604052565b67ffffffffffffffff8111612b9b57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b929192612c6d82612c27565b91612c7b6040519384612be6565b829481845281830111610683578281602093846000960137010152565b9080601f8301121561068357816020612cb393359101612c61565b90565b9291909267ffffffffffffffff808511612b9b578460051b604080519460208096612ce382860182612be6565b8099815201928501948186116106835780935b868510612d07575050505050505050565b843586811161068357820160a091828286031261068357855192830183811089821117612d9d57865281356004811015610683578352612d488a8301612aae565b8a84015285820135868401526060808301358981116106835786612d6d918501612c98565b90840152608092838301359389851161068357612d8e878d96879601612c98565b90820152815201940193612cf6565b602460007f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b9080601f8301121561068357816020612cb393359101612cb6565b90816020910312610683575180151581036106835790565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b959493612e5960409492612e679460608a5260608a0191612dfe565b918783036020890152612dfe565b930152565b929093612e8b612cb398969795612e9994608087526080870191612dfe565b918483036020860152612dfe565b9360408201526060818503910152612dfe565b90816020910312610683575173ffffffffffffffffffffffffffffffffffffffff811681036106835790565b94612f5c612f6e94939295612f2a604051978895602087019a7f2a41fec9a0df4e0996b975f71622c7164b0f652ea69d9dbcd6b24e81b20ab5e58c52604088015260a0606088015260c0870191612dfe565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09586868403016080870152612dfe565b9060a083015203908101835282612be6565b51902090565b97939592969096949194604051978896602088019a7febf4535caee8019297b7be3ed867db0d00b69fedcdda98c5e2c41ea6e41a98d58c5260408901526060880160e09052610100880190612fc892612dfe565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09788888303016080890152612ffd92612dfe565b9160a0860152858583030160c086015261301692612dfe565b9060e0830152039081018252612f6e9082612be6565b61308c9273ffffffffffffffffffffffffffffffffffffffff604051937fa9059cbb00000000000000000000000000000000000000000000000000000000602086015216602484015260448301526044825261308782612bca565b6130be565b565b3d156130b9573d9061309f82612c27565b916130ad6040519384612be6565b82523d6000602084013e565b606090565b600073ffffffffffffffffffffffffffffffffffffffff8192169260208151910182855af16130eb61308e565b8161310c575b5015908115613102575b506112a257565b90503b15386130fb565b8051801592508215613121575b5050386130f1565b6131349250602080918301019101612de6565b3880613119565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff928316602482015292909116604483015260648083019390935291815261308c91613087608483612be6565b73ffffffffffffffffffffffffffffffffffffffff811680156115e5577f04dba622d284ed0014ee4b9a6a68386be1a4c08a4913ae272de89199cc686163600080a27f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c05560007f9855384122b55936fbfb8ca5120e63c6537a1ac40caf6ae33502b3c5da8c87d155565b919082519283825260005b8481106132795750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b60208183018101518483018201520161323a565b602080820181835283518091526040918284019381848460051b83010196019460009485925b8584106132c557505050505050505090565b90919293949596977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc082820301845288518051600480821015613373575061336183899384936001965273ffffffffffffffffffffffffffffffffffffffff848201511684830152878101518883015261334e6060808301519060a0809186015284019061322f565b916080809201519181840391015261322f565b9a0194019401929695949391906132b3565b8a60216024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b999b9a9796939298959491909873ffffffffffffffffffffffffffffffffffffffff809a168b5260208b0160e0905260e08b01906133dc92612dfe565b9089820360408b01526133ee92612dfe565b90878203606089015261340092612dfe565b90858203608087015261341292612dfe565b9460a0840152169060c00152565b919992949698999790959760a0526080526040968751927f70a0823100000000000000000000000000000000000000000000000000000000845260009a30600486015273ffffffffffffffffffffffffffffffffffffffff916020866024818660a051165afa958615613747578d96613710575b50476135b3575b50507f000000000000000000000000000000000000000000000000000000000000000016956134cd848860a051613751565b863b1561132f57918a9796959492918894928a519b8c998a9889977fb54170840000000000000000000000000000000000000000000000000000000089526004890160a0905260a489019061352192612dfe565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc94858983030160248a015261355692612dfe565b8387820301604488015260805161356c92612dfe565b9185830301606486015261357f92612dfe565b90608483015203925af19081156135aa5750613599575050565b6135a38291612b87565b6101f65750565b513d84823e3d90fd5b1561366f57817f000000000000000000000000000000000000000000000000000000000000000016904790823b1561366b578c928b8f938a8f89958f8f908d8f9261363195519c8d9b8c9a8b997f2e9b7470000000000000000000000000000000000000000000000000000000008b52608051943060048d0161339f565b03925af1801561366157908c9161364d575b50505b388061349b565b61365690612b87565b61132f578a38613643565b8a513d8e823e3d90fd5b8d80fd5b817f000000000000000000000000000000000000000000000000000000000000000016904790823b1561366b578c928b8f938a8f89958f8f908d8f926136e895519c8d9b8c9a8b997fc62c2002000000000000000000000000000000000000000000000000000000008b52608051943060048d0161339f565b03925af18015613661576136fd575b50613646565b613709909b919b612b87565b99386136f7565b9095506020813d821161373f575b8161372b60209383612be6565b8101031261373b57519438613494565b8c80fd5b3d915061371e565b8b513d8f823e3d90fd5b90604092835180917fdd62ed3e00000000000000000000000000000000000000000000000000000000825230600483015273ffffffffffffffffffffffffffffffffffffffff80941693846024840152826044602095869389165afa91821561392d576000926138fe575b5081106137cb575b5050505050565b61387d575b91600092918380938651908101927f095ea7b300000000000000000000000000000000000000000000000000000000845260248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60448201526044815261383981612bca565b51925af161384561308e565b501561385457808080806137c4565b600490517f8164f842000000000000000000000000000000000000000000000000000000008152fd5b6000808551838101907f095ea7b3000000000000000000000000000000000000000000000000000000008252856024820152826044820152604481526138c281612bca565b519082875af16138d061308e565b506137d057600484517f8164f842000000000000000000000000000000000000000000000000000000008152fd5b90918382813d8311613926575b6139158183612be6565b810103126101f657505190386137bc565b503d61390b565b86513d6000823e3d90fdfea26469706673582212201ace3ff5ec279d48c383ced261047518e1f063a085cfa5c5cfc91bb070b2e12c64736f6c634300081400330000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f780000000000000000000000002d5d7d31f671f86c782533cc367f14109a0827120000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3

Deployed Bytecode

0x60c080604052600436101561001357600080fd5b600090813560e01c908163116191b61461294757508063167a6f90146128615780631a98b2e01461235157806321477960146121985780633f4ba83a146120e15780634916065814611f3057806352c41eb614611ae8578063554bab3c14611a2457806358181a80146117f05780635c60da1b1461177f5780635c975abb14611722578063656576361461160f578063710bf3221461151b57806379ba50971461147a5780638291286c146114215780638456cb591461133f578063846a1bc614610ee8578063868a166d14610e475780638da5cb5b14610dd65780639a7165e414610d655780639ded06df14610c165780639fd0506d14610ba5578063a3499c73146108d6578063c7e6a3cc14610838578063e30c3978146107c7578063e4a974cc146101f95763f2fde38b1461014a57600080fd5b346101f65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f657610181612a8b565b7f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c05473ffffffffffffffffffffffffffffffffffffffff339116036101cc576101c9906131a5565b80f35b60046040517f30cd7471000000000000000000000000000000000000000000000000000000008152fd5b80fd5b50610203366129e3565b93969295604099959199989298517fd26ff21000000000000000000000000000000000000000000000000000000000815286600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f78165afa9081156107bc578c9161078d575b50610763576102d1938b9360208b8d60405198899283927f935b13f60000000000000000000000000000000000000000000000000000000084528560048501526024840191612dfe565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f78165afa978815610758578a8a8e988e989b610703575b509282818b88957f5844b8bbe3fd2b0354e73f27bfde28d2e6d991f14139c382876ec4360391a47b8c9a8a6103869f9d9b98859f8f906103668f9e9361037e943691612c61565b602081519101209e8f90604051968796339c88612e6c565b0390a4612f74565b9173ffffffffffffffffffffffffffffffffffffffff8354166106d9576103c992339055309073ffffffffffffffffffffffffffffffffffffffff33911661313b565b8082016040838203126106d55782359067ffffffffffffffff821161064c576103f9602091610457938601612dcb565b9473ffffffffffffffffffffffffffffffffffffffff61041a838701612aae565b169660405193849283927f935b13f60000000000000000000000000000000000000000000000000000000084528560048501526024840191612dfe565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f78165afa9081156106ca57869161069b575b508573ffffffffffffffffffffffffffffffffffffffff821694604051927f70a082310000000000000000000000000000000000000000000000000000000084523060048501526020846024818a5afa938415610690578394610654575b506105418473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3168093613751565b803b156106505761058583929183926040519586809481937ff87ef8000000000000000000000000000000000000000000000000000000000083526004830161328d565b03925af19182610638575b50506105fb577fdd7b1484db8d21f4fbda2407f2920037dc379dd66e18b0851aa9d6c14ef493b9926105f5926105d26105d993886105cc61308e565b9861302c565b3691612c61565b602081519101209260405191829160208352602083019061322f565b0390a380f35b5061060a935036925090612c61565b602081519101207f7c3aa10c5d96985be6de7d2e6fa79bdef95a95a9cb272f4113b3fe1ca89fedae8280a280f35b61064190612b87565b61064c578638610590565b8680fd5b8280fd5b925092506020823d602011610688575b8161067160209383612be6565b810103126106835787915192386104fe565b600080fd5b3d9150610664565b6040513d85823e3d90fd5b6106bd915060203d6020116106c3575b6106b58183612be6565b810190612eac565b386104a0565b503d6106ab565b6040513d88823e3d90fd5b8580fd5b60046040517f725f13f1000000000000000000000000000000000000000000000000000000008152fd5b899b5092848b8288958a8c9a8e9f9d9a97859f9d9a60203d602011610751575b61072d8183612be6565b810161073891612eac565b9f5097505050959850959850509497509a98979a61031f565b503d610723565b6040513d87823e3d90fd5b60046040517f0dc10197000000000000000000000000000000000000000000000000000000008152fd5b6107af915060203d6020116107b5575b6107a78183612be6565b810190612de6565b38610287565b503d61079d565b6040513d8e823e3d90fd5b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760207f9855384122b55936fbfb8ca5120e63c6537a1ac40caf6ae33502b3c5da8c87d15473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b50346101f65760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65767ffffffffffffffff602435818111610650576108899036906004016129b5565b9190926044359182116101f65760206108b785856108aa36600488016129b5565b9160643593600435612ed8565b5473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b50346101f65760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65761090e612a8b565b60443567ffffffffffffffff81116106505761092e9036906004016129b5565b907f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c05473ffffffffffffffffffffffffffffffffffffffff9081339116036101cc578316604051907f8291286c000000000000000000000000000000000000000000000000000000008083526020928381600481865afa908115610b6d578891610b78575b506040519182528382600481305afa918215610b6d578892610b3e575b5003610b1457843f60243503610aea578593604051917fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8680a280610a37575b84867f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5580f35b81610a778695610aa393868401967f9ded06df00000000000000000000000000000000000000000000000000000000885260248501526044840191612dfe565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282612be6565b5190845af4610ab061308e565b5015610ac0578138808080610a10565b60046040517f97905dfb000000000000000000000000000000000000000000000000000000008152fd5b60046040517f8f84fb24000000000000000000000000000000000000000000000000000000008152fd5b60046040517f68155f9a000000000000000000000000000000000000000000000000000000008152fd5b9091508381813d8311610b66575b610b568183612be6565b81010312610683575190386109d0565b503d610b4c565b6040513d8a823e3d90fd5b90508381813d8311610b9e575b610b8f8183612be6565b810103126106835751386109b3565b503d610b85565b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760207f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c3795473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b50346101f65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760043567ffffffffffffffff8111610d6157610c669036906004016129b5565b9073ffffffffffffffffffffffffffffffffffffffff91827f00000000000000000000000073ccbe3cb0e8436050c01a3f4295bb036717e15c163014610d3757816020918101031261065057610cbb90612aae565b168015610d0d57807f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c379557fa4336c0cb1e245b95ad204faed7e940d6dc999684fd8b5e1ff597a0c4efca8ab600080a280f35b60046040517f84744201000000000000000000000000000000000000000000000000000000008152fd5b60046040517fbf10dd3a000000000000000000000000000000000000000000000000000000008152fd5b5080fd5b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760207fe330cfee289e568e8774e923acd6e2a00f84be86aa1d02356835d33dbb9397025473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760207f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c05473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b50346101f65760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65767ffffffffffffffff60243581811161065057610e989036906004016129b5565b929060443583811161065057610eb29036906004016129b5565b9390926084359182116101f65760206108b787878787610ed53660048a016129b5565b93909260a4359560643593600435612f74565b506101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f657610f1c612a8b565b67ffffffffffffffff9060443582811161133b57610f3e903690600401612b56565b916064358481116106d557610f579036906004016129b5565b93909260843586811161133757610f729036906004016129b5565b60a49791973582811161133357610f8d9036906004016129b5565b93909260c43590811161132f57610fa89036906004016129b5565b96909573ffffffffffffffffffffffffffffffffffffffff60e4351660e43503610683576101043515156101043503610683577fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c54611305578b9261100e913691612cb6565b90829073ffffffffffffffffffffffffffffffffffffffff811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee036111ee5750506024355b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3163b15610650576110bf91839160405180809581947ff87ef8000000000000000000000000000000000000000000000000000000000083526004830161328d565b039173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3165af180156111e3576111cb575b5050604051977f935b13f6000000000000000000000000000000000000000000000000000000008952602060048a015260208980611147602482018c8c612dfe565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f78165afa80156111c0576101c9998b916111a2575b50610104359860e43598613420565b6111ba915060203d81116106c3576106b58183612be6565b38611193565b6040513d8c823e3d90fd5b6111d490612b87565b6111df578838611105565b8880fd5b6040513d84823e3d90fd5b6040517f23b872dd0000000000000000000000000000000000000000000000000000000060208201908152336024808401919091527f0000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd373ffffffffffffffffffffffffffffffffffffffff1660448401523560648301529294919282916112788160848101610a77565b519082855af161128661308e565b816112d6575b50159081156112cc575b506112a2578a91611048565b60046040517f045c4b02000000000000000000000000000000000000000000000000000000008152fd5b90503b1538611296565b80518015925082156112eb575b50503861128c565b6112fe9250602080918301019101612de6565b38806112e3565b60046040517f6d39fcd0000000000000000000000000000000000000000000000000000000008152fd5b8a80fd5b8980fd5b8780fd5b8380fd5b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65773ffffffffffffffffffffffffffffffffffffffff7f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c379541633036113f75760017fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c557f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e7528180a180f35b60046040517f492f6781000000000000000000000000000000000000000000000000000000008152fd5b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760206040517fc097d45e5a99ca772ab5ec2e5457c2e249760944b95b0b97cbb6b03ec55bae848152f35b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f6577f9855384122b55936fbfb8ca5120e63c6537a1ac40caf6ae33502b3c5da8c87d1543373ffffffffffffffffffffffffffffffffffffffff8216036114f1576101c9906131a5565b60046040517f49e27cff000000000000000000000000000000000000000000000000000000008152fd5b50346101f65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f657611553612a8b565b7f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c05473ffffffffffffffffffffffffffffffffffffffff9081339116036101cc57811680156115e5577fd9be0e8e07417e00f2521db636cb53e316fd288f5051f16d2aa2bf0c3938a8768380a27f9855384122b55936fbfb8ca5120e63c6537a1ac40caf6ae33502b3c5da8c87d15580f35b60046040517fd924e5f4000000000000000000000000000000000000000000000000000000008152fd5b5061161936612acf565b95939094604093929351967fd26ff21000000000000000000000000000000000000000000000000000000000885285600489015273ffffffffffffffffffffffffffffffffffffffff976020816024818c7f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f78165afa9081156111c0578a91611704575b50610763576116f6966116b0913691612c61565b6020815191012094604051817f6e18757e81c44a367109cbaa499add16f2ae7168aab9715c3cdc36b0f7ccce923392806116ee8b8b8b8b8b86612e3d565b0390a3612ed8565b908154166106d95733905580f35b61171c915060203d81116107b5576107a78183612be6565b3861169c565b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760207fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c546040519015158152f35b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65760207f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f657611823612a8b565b6024359060443567ffffffffffffffff811161133b57611847903690600401612dcb565b7fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c546113055783928373ffffffffffffffffffffffffffffffffffffffff9373eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee85821614600014611935575050915b7f0000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd31690813b1561193057839161190f916040519485809481937ff87ef8000000000000000000000000000000000000000000000000000000000083526004830161328d565b03925af180156111e3576119205750f35b61192990612b87565b6101f65780f35b505050fd5b6040517f23b872dd000000000000000000000000000000000000000000000000000000006020820190815233602483015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd38816166044830152606482019490945294959194909282916119c18160848101610a77565b519082855af16119cf61308e565b816119f5575b50159081156119eb575b506112a25783926118aa565b90503b15386119df565b8051801592508215611a0a575b5050386119d5565b611a1d9250602080918301019101612de6565b3880611a02565b50346101f65760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f657611a5c612a8b565b73ffffffffffffffffffffffffffffffffffffffff90817f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c379541633036113f757807fe330cfee289e568e8774e923acd6e2a00f84be86aa1d02356835d33dbb9397025516337f3210edd3f0fc490ffc59a4adae6f48dbda2d8e89afe5b37a0145a54762f3ecf98380a380f35b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60c081360112610d6157611b1c612a8b565b67ffffffffffffffff91602435916044358481116106d557611b42903690600401612b56565b909460643581811161133757611b5c9036906004016129b5565b9360843583811161133357611b759036906004016129b5565b91909360a43590811161132f57611b909036906004016129b5565b9890957fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c54611305578b9a611bc6913691612cb6565b908a73ffffffffffffffffffffffffffffffffffffffff9373eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee85821614600014611e41575050905b827f0000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd31690813b15611e3d578b91611c68916040519485809481937ff87ef8000000000000000000000000000000000000000000000000000000000083526004830161328d565b03925af19081156111c0578a91611e29575b5050807f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f781694604051917f935b13f6000000000000000000000000000000000000000000000000000000008352602092836004820152838180611ce1602482018d8b612dfe565b03818b5afa9081156107bc5784908d92611e09575b5060249192604051928380927f70a0823100000000000000000000000000000000000000000000000000000000825230600483015286165afa9384156107bc578c94611dd4575b50508287611d4a92613751565b853b15611333578997611d9e978995611dae611dbd946040519d8e9c8d9b8c9a7f26ef699d000000000000000000000000000000000000000000000000000000008c52608060048d015260848c0191612dfe565b91868a84030160248b0152612dfe565b92868403016044870152612dfe565b90606483015203925af180156111e3576119205750f35b819c50809294503d8311611e02575b611ded8183612be6565b810103126106835798518a9990918087611d3d565b503d611de3565b60249250611e2390823d84116106c3576106b58183612be6565b91611cf6565b611e3290612b87565b6111df578838611c7a565b8b80fd5b6040517f23b872dd000000000000000000000000000000000000000000000000000000006020820190815233602483015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd388161660448301526064820194909452939c919390928291611ecd8160848101610a77565b519082855af1611edb61308e565b81611f01575b5015908115611ef7575b506112a2578a99611c02565b90503b1538611eeb565b8051801592508215611f16575b505038611ee1565b611f299250602080918301019101612de6565b3880611f0e565b50346101f657611f4e611f4236612acf565b96919492963691612c61565b60208151910120946040517f5f6970c300000000000000000000000000000000000000000000000000000000815285600482015260806024820152611fcb611f9a608483018686612dfe565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8382030160448401528684612dfe565b908760648201526020818073ffffffffffffffffffffffffffffffffffffffff9403818c867f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f78165af19081156120d65789916120b8575b501561208e5761203687868487878b612ed8565b8881549182612086575b50501695861561207c577f8fe61b2d4701a29265508750790e322b2c214399abdf98472158b8908b660d41946105f59260405195869586612e3d565b5050505050505080f35b558838612040565b60046040517f500c44b4000000000000000000000000000000000000000000000000000000008152fd5b6120d0915060203d81116107b5576107a78183612be6565b38612022565b6040513d8b823e3d90fd5b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65773ffffffffffffffffffffffffffffffffffffffff7f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c379541633036113f757807fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c557fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d169338180a180f35b5060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f65767ffffffffffffffff600435818111610650576121e49036906004016129b5565b909160443581811161234d576121fe9036906004016129b5565b60649291923582811161064c576122199036906004016129b5565b9092608435908111611337576122339036906004016129b5565b93909260a4359573ffffffffffffffffffffffffffffffffffffffff80881688036106835760c435988915158a03610683577fa42f2cf999e26109eb49cb7eef3654cc55b677b2db805f3eab8722cc5dd1af0c54611305578b9a604051927f935b13f600000000000000000000000000000000000000000000000000000000845260206004850152602084806122cd602482018787612dfe565b0381847f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f78165afa938415612342576101c99d9461231e575b50612319906024359030903390871661313b565b613420565b61231991945061233b9060203d81116106c3576106b58183612be6565b9390612305565b6040513d8f823e3d90fd5b8480fd5b50346101f657612360366129e3565b9791959098939692943661237590878a612c61565b8051906020012091898c898d60405193849283927f1876eed90000000000000000000000000000000000000000000000000000000084528c60048501526024840160c0905260c484016123c9908d8d612dfe565b907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9182868203016044870152612401908b8b612dfe565b918b606487015285830301608486015261241a92612dfe565b9060a483015203817f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f7873ffffffffffffffffffffffffffffffffffffffff16935a94602095f1908115612342578d91612842575b501561208e576124858a8c8a8686868b8b8e612f74565b988c8a549a8b61283a575b505073ffffffffffffffffffffffffffffffffffffffff8a169788156125c55750509389936124f38c8a6125319e99967fdb3db9dfc9262f4fe09dbadef104f799d8181ec565e09275d80ed3355aab68d39660209d9c9a60405197889788612e6c565b0390a460405195869283927f935b13f60000000000000000000000000000000000000000000000000000000084528560048501526024840191612dfe565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f78165afa9283156125ba576101c99373ffffffffffffffffffffffffffffffffffffffff91869161259b575b501661302c565b6125b4915060203d6020116106c3576106b58183612be6565b38612594565b6040513d86823e3d90fd5b989a50985050505050505050808201926040838503126106d55782359367ffffffffffffffff851161064c57612602602091612660968601612dcb565b9173ffffffffffffffffffffffffffffffffffffffff612623838701612aae565b169660405196879283927f935b13f60000000000000000000000000000000000000000000000000000000084528560048501526024840191612dfe565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f78165afa9384156106ca578694612819575b508573ffffffffffffffffffffffffffffffffffffffff851694604051927f70a082310000000000000000000000000000000000000000000000000000000084523060048501526020846024818a5afa9384156106905783946127e5575b5061274a8473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3168094613751565b813b1561065057829161278c916040519485809481937ff87ef8000000000000000000000000000000000000000000000000000000000083526004830161328d565b03925af190816127d2575b506105fb577fdd7b1484db8d21f4fbda2407f2920037dc379dd66e18b0851aa9d6c14ef493b9926105f5926105d26105d993886105cc61308e565b6127de90979197612b87565b9538612797565b9093506020813d602011612811575b8161280160209383612be6565b8101031261065057519238612707565b3d91506127f4565b61283391945060203d6020116106c3576106b58183612be6565b92386126a9565b558c38612490565b61285b915060203d6020116107b5576107a78183612be6565b3861246e565b50346101f657807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f6577fe330cfee289e568e8774e923acd6e2a00f84be86aa1d02356835d33dbb93970273ffffffffffffffffffffffffffffffffffffffff815416330361291d578190337f23e06a2ed150be3aaebbce1156aecc5187e5e955ea717a17e165cca37ef8c37955337fa4336c0cb1e245b95ad204faed7e940d6dc999684fd8b5e1ff597a0c4efca8ab8380a25580f35b60046040517fb3a270d8000000000000000000000000000000000000000000000000000000008152fd5b905034610d6157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610d615760209073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f78168152f35b9181601f840112156106835782359167ffffffffffffffff8311610683576020838186019501011161068357565b9060c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc830112610683576004359167ffffffffffffffff906024358281116106835781612a33916004016129b5565b939093926044358181116106835783612a4e916004016129b5565b939093926064358381116106835782612a69916004016129b5565b9390939260843591821161068357612a83916004016129b5565b909160a43590565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361068357565b359073ffffffffffffffffffffffffffffffffffffffff8216820361068357565b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820112610683576004359167ffffffffffffffff6024358181116106835783612b1d916004016129b5565b939093926044358381116106835782612b38916004016129b5565b9390939260643591821161068357612b52916004016129b5565b9091565b9181601f840112156106835782359167ffffffffffffffff8311610683576020808501948460051b01011161068357565b67ffffffffffffffff8111612b9b57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff821117612b9b57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612b9b57604052565b67ffffffffffffffff8111612b9b57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b929192612c6d82612c27565b91612c7b6040519384612be6565b829481845281830111610683578281602093846000960137010152565b9080601f8301121561068357816020612cb393359101612c61565b90565b9291909267ffffffffffffffff808511612b9b578460051b604080519460208096612ce382860182612be6565b8099815201928501948186116106835780935b868510612d07575050505050505050565b843586811161068357820160a091828286031261068357855192830183811089821117612d9d57865281356004811015610683578352612d488a8301612aae565b8a84015285820135868401526060808301358981116106835786612d6d918501612c98565b90840152608092838301359389851161068357612d8e878d96879601612c98565b90820152815201940193612cf6565b602460007f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b9080601f8301121561068357816020612cb393359101612cb6565b90816020910312610683575180151581036106835790565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b959493612e5960409492612e679460608a5260608a0191612dfe565b918783036020890152612dfe565b930152565b929093612e8b612cb398969795612e9994608087526080870191612dfe565b918483036020860152612dfe565b9360408201526060818503910152612dfe565b90816020910312610683575173ffffffffffffffffffffffffffffffffffffffff811681036106835790565b94612f5c612f6e94939295612f2a604051978895602087019a7f2a41fec9a0df4e0996b975f71622c7164b0f652ea69d9dbcd6b24e81b20ab5e58c52604088015260a0606088015260c0870191612dfe565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09586868403016080870152612dfe565b9060a083015203908101835282612be6565b51902090565b97939592969096949194604051978896602088019a7febf4535caee8019297b7be3ed867db0d00b69fedcdda98c5e2c41ea6e41a98d58c5260408901526060880160e09052610100880190612fc892612dfe565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09788888303016080890152612ffd92612dfe565b9160a0860152858583030160c086015261301692612dfe565b9060e0830152039081018252612f6e9082612be6565b61308c9273ffffffffffffffffffffffffffffffffffffffff604051937fa9059cbb00000000000000000000000000000000000000000000000000000000602086015216602484015260448301526044825261308782612bca565b6130be565b565b3d156130b9573d9061309f82612c27565b916130ad6040519384612be6565b82523d6000602084013e565b606090565b600073ffffffffffffffffffffffffffffffffffffffff8192169260208151910182855af16130eb61308e565b8161310c575b5015908115613102575b506112a257565b90503b15386130fb565b8051801592508215613121575b5050386130f1565b6131349250602080918301019101612de6565b3880613119565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff928316602482015292909116604483015260648083019390935291815261308c91613087608483612be6565b73ffffffffffffffffffffffffffffffffffffffff811680156115e5577f04dba622d284ed0014ee4b9a6a68386be1a4c08a4913ae272de89199cc686163600080a27f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c05560007f9855384122b55936fbfb8ca5120e63c6537a1ac40caf6ae33502b3c5da8c87d155565b919082519283825260005b8481106132795750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b60208183018101518483018201520161323a565b602080820181835283518091526040918284019381848460051b83010196019460009485925b8584106132c557505050505050505090565b90919293949596977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc082820301845288518051600480821015613373575061336183899384936001965273ffffffffffffffffffffffffffffffffffffffff848201511684830152878101518883015261334e6060808301519060a0809186015284019061322f565b916080809201519181840391015261322f565b9a0194019401929695949391906132b3565b8a60216024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b999b9a9796939298959491909873ffffffffffffffffffffffffffffffffffffffff809a168b5260208b0160e0905260e08b01906133dc92612dfe565b9089820360408b01526133ee92612dfe565b90878203606089015261340092612dfe565b90858203608087015261341292612dfe565b9460a0840152169060c00152565b919992949698999790959760a0526080526040968751927f70a0823100000000000000000000000000000000000000000000000000000000845260009a30600486015273ffffffffffffffffffffffffffffffffffffffff916020866024818660a051165afa958615613747578d96613710575b50476135b3575b50507f0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f7816956134cd848860a051613751565b863b1561132f57918a9796959492918894928a519b8c998a9889977fb54170840000000000000000000000000000000000000000000000000000000089526004890160a0905260a489019061352192612dfe565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc94858983030160248a015261355692612dfe565b8387820301604488015260805161356c92612dfe565b9185830301606486015261357f92612dfe565b90608483015203925af19081156135aa5750613599575050565b6135a38291612b87565b6101f65750565b513d84823e3d90fd5b1561366f57817f0000000000000000000000002d5d7d31f671f86c782533cc367f14109a08271216904790823b1561366b578c928b8f938a8f89958f8f908d8f9261363195519c8d9b8c9a8b997f2e9b7470000000000000000000000000000000000000000000000000000000008b52608051943060048d0161339f565b03925af1801561366157908c9161364d575b50505b388061349b565b61365690612b87565b61132f578a38613643565b8a513d8e823e3d90fd5b8d80fd5b817f0000000000000000000000002d5d7d31f671f86c782533cc367f14109a08271216904790823b1561366b578c928b8f938a8f89958f8f908d8f926136e895519c8d9b8c9a8b997fc62c2002000000000000000000000000000000000000000000000000000000008b52608051943060048d0161339f565b03925af18015613661576136fd575b50613646565b613709909b919b612b87565b99386136f7565b9095506020813d821161373f575b8161372b60209383612be6565b8101031261373b57519438613494565b8c80fd5b3d915061371e565b8b513d8f823e3d90fd5b90604092835180917fdd62ed3e00000000000000000000000000000000000000000000000000000000825230600483015273ffffffffffffffffffffffffffffffffffffffff80941693846024840152826044602095869389165afa91821561392d576000926138fe575b5081106137cb575b5050505050565b61387d575b91600092918380938651908101927f095ea7b300000000000000000000000000000000000000000000000000000000845260248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60448201526044815261383981612bca565b51925af161384561308e565b501561385457808080806137c4565b600490517f8164f842000000000000000000000000000000000000000000000000000000008152fd5b6000808551838101907f095ea7b3000000000000000000000000000000000000000000000000000000008252856024820152826044820152604481526138c281612bca565b519082875af16138d061308e565b506137d057600484517f8164f842000000000000000000000000000000000000000000000000000000008152fd5b90918382813d8311613926575b6139158183612be6565b810103126101f657505190386137bc565b503d61390b565b86513d6000823e3d90fdfea26469706673582212201ace3ff5ec279d48c383ced261047518e1f063a085cfa5c5cfc91bb070b2e12c64736f6c63430008140033

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

0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f780000000000000000000000002d5d7d31f671f86c782533cc367f14109a0827120000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3

-----Decoded View---------------
Arg [0] : _gateway (address): 0x5029C0EFf6C34351a0CEc334542cDb22c7928f78
Arg [1] : _gasService (address): 0x2d5d7d31F671F86C782533cc367F14109a082712
Arg [2] : _multicall (address): 0x4fd39C9E151e50580779bd04B1f7eCc310079fd3

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000005029c0eff6c34351a0cec334542cdb22c7928f78
Arg [1] : 0000000000000000000000002d5d7d31f671f86c782533cc367f14109a082712
Arg [2] : 0000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd3


Block Transaction Gas Used Reward
view all blocks ##produced##

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits

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.