Contract

0x50bdE2DBF42172c85Fe358F0603eEd0b7eCbc2eF

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

-

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Parent Transaction Hash Block From To
4110442024-12-14 18:31:0915 days ago1734201069  Contract Creation0 S
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AvocadoMultisigSecondary

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion
File 1 of 28 : AvocadoMultisigSecondary.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { SSTORE2 } from "solmate/src/utils/SSTORE2.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";

import { IAvoRegistry } from "../interfaces/IAvoRegistry.sol";
import { IAvoSignersList } from "../interfaces/IAvoSignersList.sol";
import { IAvocadoMultisigV1Secondary } from "../interfaces/IAvocadoMultisigV1Secondary.sol";
import { IAvocado } from "../Avocado.sol";
import { IAvoConfigV1 } from "../interfaces/IAvoConfigV1.sol";
import { AvocadoMultisigBase } from "./AvocadoMultisigCore.sol";
import { AvocadoMultisigProtected, AvocadoMultisigSelfUpgradeable } from "./AvocadoMultisig.sol";

// --------------------------- DEVELOPER NOTES -----------------------------------------
// @dev IMPORTANT: all storage variables go into AvocadoMultisigVariables.sol
// -------------------------------------------------------------------------------------

// empty interface used for Natspec docs for nice layout in automatically generated docs:
//
/// @title  AvocadoMultisigSecondary v1.1.0
/// @notice Extended core logic for `AvocadoMultisig.sol`, which calls this contract via delegateCall (or directly
///         for view/pure methods).
///         This contract is set as immutable on `AvocadoMultisig.sol` and is thus only upgradeable when that contract
///         itself is newly deployed (and consecutively upgraded to by the Avocado owner).
/// @dev    All methods are restricted to be only callable via delegateCall.
interface AvocadoMultisigSecondary_V1 {}

abstract contract AvocadoMultisigSecondaryCore is AvocadoMultisigBase, IAvocadoMultisigV1Secondary {
    address private immutable _ADDRESS_THIS;

    /// @dev ensures the method can only be called by a delegate call.
    modifier onlyDelegateCall() {
        _requireDelegateCalled();
        _;
    }

    /// @dev internal method for modifier logic to reduce bytecode size of contract.
    function _requireDelegateCalled() internal view {
        if (_ADDRESS_THIS == address(this)) {
            revert AvocadoMultisig__Unauthorized();
        }
    }

    constructor() {
        _ADDRESS_THIS = address(this);
    }
}

abstract contract AvocadoMultisigUpgradeTo is AvocadoMultisigSecondaryCore {
    /// @notice upgrade the contract to a new implementation address.
    ///         - Must be a valid version at the AvoRegistry.
    ///         - Can only be self-called (authorization same as for `cast` methods).
    /// @param avoImplementation_       New contract address
    /// @param afterUpgradeHookData_    flexible bytes for custom usage in after upgrade hook logic
    //
    // Implementation must call `_afterUpgradeHook()`
    function upgradeTo(address avoImplementation_, bytes calldata afterUpgradeHookData_) public onlyDelegateCall {
        if (avoImplementation_ == _avoImpl) {
            return;
        }

        // checks that `avoImplementation_` is a valid version at registry. reverts if not.
        avoRegistry.requireValidAvoVersion(avoImplementation_);

        // store previous implementation address to pass to after upgrade hook, for version x > version y specific logic
        address fromImplementation_ = _avoImpl;

        _avoImpl = avoImplementation_;
        emit Upgraded(avoImplementation_);

        // Address.functionDelegateCall will revert if success = false
        Address.functionDelegateCall(
            avoImplementation_,
            abi.encodeCall(
                AvocadoMultisigSelfUpgradeable._afterUpgradeHook,
                (fromImplementation_, afterUpgradeHookData_)
            )
        );
    }
}

abstract contract AvocadoMultisigOccupyNonces is AvocadoMultisigSecondaryCore {
    /// @notice occupies the sequential `avoNonces_` in storage. This can be used to cancel / invalidate
    ///         a previously signed request(s) because the nonce will be "used" up.
    ///         - Can only be self-called (authorization same as for `cast` methods).
    /// @param  avoNonces_ sequential ascending ordered nonces to be occupied in storage.
    ///         E.g. if current AvoNonce is 77 and txs are queued with avoNonces 77, 78 and 79,
    ///         then you would submit [78, 79] here because 77 will be occupied by the tx executing
    ///         `occupyAvoNonces()` as an action itself. If executing via non-sequential nonces, you would
    ///         submit [77, 78, 79].
    ///         - Maximum array length is 5.
    ///         - gap from the current avoNonce will revert (e.g. [79, 80] if current one is 77)
    function occupyAvoNonces(uint88[] calldata avoNonces_) external onlyDelegateCall {
        uint256 avoNoncesLength_ = avoNonces_.length;
        if (avoNoncesLength_ == 0) {
            // in case to cancel just one nonce via normal sequential nonce execution itself
            return;
        }

        if (avoNoncesLength_ > 5) {
            revert AvocadoMultisig__InvalidParams();
        }

        uint256 nextAvoNonce_ = _avoNonce;

        for (uint256 i; i < avoNoncesLength_; ) {
            if (avoNonces_[i] == nextAvoNonce_) {
                // nonce to occupy is valid -> must match the current avoNonce
                emit AvoNonceOccupied(nextAvoNonce_);

                nextAvoNonce_++;
            } else if (avoNonces_[i] > nextAvoNonce_) {
                // input nonce is not smaller or equal current nonce -> invalid sorted ascending input params
                revert AvocadoMultisig__InvalidParams();
            }
            // else while nonce to occupy is < current nonce, skip ahead

            unchecked {
                ++i;
            }
        }

        _avoNonce = uint80(nextAvoNonce_);
    }

    /// @notice occupies the `nonSequentialNonces_` in storage. This can be used to cancel / invalidate
    ///         previously signed request(s) because the nonce will be "used" up.
    ///         - Can only be self-called (authorization same as for `cast` methods).
    /// @param  nonSequentialNonces_ the non-sequential nonces to occupy
    function occupyNonSequentialNonces(bytes32[] calldata nonSequentialNonces_) external onlyDelegateCall {
        uint256 nonSequentialNoncesLength_ = nonSequentialNonces_.length;

        for (uint256 i; i < nonSequentialNoncesLength_; ) {
            nonSequentialNonces[nonSequentialNonces_[i]] = 1;

            emit NonSequentialNonceOccupied(nonSequentialNonces_[i]);

            unchecked {
                ++i;
            }
        }
    }
}

abstract contract AvocadoMultisigEIP1271 is AvocadoMultisigSecondaryCore {
    /// @dev length of a normal expected ECDSA signature
    uint256 private constant _SIGNATURE_LENGTH = 65;
    /// @dev signature must be 65 bytes or otherwise at least 90 bytes to be either a multiple
    /// of 85 bytes + prefix or a decodable `SignatureParams` struct array.
    uint256 private constant _MIN_SIGNATURE_LENGTH = 90;
    /// @dev prefix to signal decoding with multiple of 85 bytes is "0xDEC0DE6520" (appending 000000 to get to bytes8)
    bytes8 private constant _PREFIX_SIGNAL = bytes8(uint64(0xdec0de6520000000));
    /// @dev prefix length to cut of is 5 bytes (DE_C0_DE_65_20)
    uint256 private constant _PREFIX_SIGNAL_LENGTH = 5;

    /// @notice decodes `signature` for EIP1271 into `signaturesParams_`
    function decodeEIP1271Signature(
        bytes calldata signature,
        address owner_
    ) external pure returns (SignatureParams[] memory signaturesParams_) {
        // decode signaturesParams_ from bytes signature
        uint256 signatureLength_ = signature.length;

        if (signatureLength_ == _SIGNATURE_LENGTH) {
            // signature must be from owner for a Multisig with requiredSigners = 1, signers=[owner]
            signaturesParams_ = new SignatureParams[](1);
            signaturesParams_[0] = SignatureParams({ signature: signature, signer: owner_ });
        } else if (signatureLength_ < _MIN_SIGNATURE_LENGTH) {
            revert AvocadoMultisig__InvalidEIP1271Signature();
        } else if (bytes8(signature[0:_PREFIX_SIGNAL_LENGTH]) == _PREFIX_SIGNAL) {
            // if signature is prefixed with _PREFIX_SIGNAL ("0xDEC0DE6520") ->
            // signature after the prefix should be divisible by 85
            // (65 bytes signature and 20 bytes signer address) each
            uint256 signaturesCount_;
            unchecked {
                // -_PREFIX_SIGNAL_LENGTH to not count prefix
                signaturesCount_ = (signatureLength_ - _PREFIX_SIGNAL_LENGTH) / 85;
            }
            signaturesParams_ = new SignatureParams[](signaturesCount_);

            for (uint256 i; i < signaturesCount_; ) {
                // used operations can not overflow / underflow
                unchecked {
                    // +_PREFIX_SIGNAL_LENGTH to start after prefix
                    uint256 signerOffset_ = (i * 85) + _SIGNATURE_LENGTH + _PREFIX_SIGNAL_LENGTH;

                    bytes memory signerBytes_ = signature[signerOffset_:signerOffset_ + 20];
                    address signer_;
                    // cast bytes to address in the easiest way via assembly
                    assembly {
                        signer_ := shr(96, mload(add(signerBytes_, 0x20)))
                    }

                    signaturesParams_[i] = SignatureParams({
                        signature: signature[(signerOffset_ - _SIGNATURE_LENGTH):signerOffset_],
                        signer: signer_
                    });

                    ++i;
                }
            }
        } else {
            // multiple signatures are present that should form `SignatureParams[]` through abi.decode
            // @dev this will fail and revert if invalid typed data is passed in
            signaturesParams_ = abi.decode(signature, (SignatureParams[]));
        }

        return signaturesParams_;
    }

    /// @notice Marks a bytes32 `message_` (signature digest) as signed, making it verifiable by EIP-1271 `isValidSignature()`.
    ///         - Can only be self-called (authorization same as for `cast` methods).
    /// @param message_ data hash to be allow-listed as signed
    /// @dev input `message_` is hashed with `domainSeparatorV4()` according to EIP712 typed data (`EIP1271_TYPE_HASH`)
    function signMessage(bytes32 message_) external onlyDelegateCall {
        // hashing with domain separator mitigates any potential replaying on other networks or other Avocados of the same owner
        message_ = ECDSA.toTypedDataHash(
            _domainSeparatorV4(
                DOMAIN_SEPARATOR_SALT_HASHED // includes block.chainid
            ),
            keccak256(abi.encode(EIP1271_TYPE_HASH, message_))
        );

        _signedMessages[message_] = 1;

        emit SignedMessage(message_);
    }

    /// @notice Removes a previously `signMessage()` signed bytes32 `message_` (signature digest).
    ///         - Can only be self-called (authorization same as for `cast` methods).
    /// @param message_ data hash to be removed from allow-listed signatures
    function removeSignedMessage(bytes32 message_) external onlyDelegateCall {
        _signedMessages[message_] = 0;

        emit RemoveSignedMessage(message_);
    }
}

abstract contract AvocadoMultisigPayAuthorizedFee is AvocadoMultisigSecondaryCore {
    /// @notice              pays the fee for `castAuthorized()` calls via the AvoRegistry (or fallback)
    /// @param gasUsedFrom_  `gasleft()` snapshot at gas measurement starting point
    /// @param maxFee_       maximum acceptable fee to be paid, revert if fee is bigger than this value
    function payAuthorizedFee(uint256 gasUsedFrom_, uint256 maxFee_) external payable onlyDelegateCall {
        // @dev part below costs ~24k gas for if `feeAmount_` and `maxFee_` is set
        uint256 feeAmount_;
        address payable feeCollector_;
        {
            uint256 gasUsed_;
            unchecked {
                // gas can not underflow
                // gasUsed already includes everything at this point except for paying fee logic
                gasUsed_ = gasUsedFrom_ - gasleft();
            }

            // Using a low-level function call to prevent reverts (making sure the contract is truly non-custodial).
            // also limit gas, so that registry can not cause out of gas.
            (bool success_, bytes memory result_) = address(avoRegistry).staticcall{ gas: 15000 }(
                abi.encodeWithSignature("calcFee(uint256)", gasUsed_)
            );

            // checks to ensure decoding does not fail, breaking non-custodial feature
            uint256 addressValue;
            assembly {
                addressValue := mload(add(result_, 0x40))
            }
            if (success_ && result_.length > 63 && addressValue <= type(uint160).max) {
                // result bytes length < 64 or a too long address value would fail the abi.decode and cause revert
                (feeAmount_, feeCollector_) = abi.decode(result_, (uint256, address));
                if (feeAmount_ > AUTHORIZED_MAX_FEE) {
                    // make sure AvoRegistry fee is capped
                    feeAmount_ = AUTHORIZED_MAX_FEE;
                }
            } else {
                // registry calcFee failed. Use local backup minimum fee
                feeCollector_ = AUTHORIZED_FEE_COLLECTOR;
                feeAmount_ = AUTHORIZED_MIN_FEE;
            }
        }

        // pay fee, if any
        if (feeAmount_ > 0) {
            if (maxFee_ > 0 && feeAmount_ > maxFee_) {
                revert AvocadoMultisig__MaxFee(feeAmount_, maxFee_);
            }

            // sending fee based on OZ Address.sendValue, but modified to properly act based on actual error case
            // (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/utils/Address.sol#L60)
            if (address(this).balance < feeAmount_) {
                revert AvocadoMultisig__InsufficientBalance(feeAmount_);
            }

            // Setting gas to very low 1000 because 2_300 gas is added automatically for a .call with a value amount.
            // This should be enough for any normal transfer to an EOA or an Avocado Multisig.
            (bool success_, ) = feeCollector_.call{ value: feeAmount_, gas: 1000 }("");

            if (success_) {
                emit FeePaid(feeAmount_);
            } else {
                // do not revert, as an error on the feeCollector_ side should not be the "fault" of the Avo contract.
                // Letting this case pass ensures that the contract is truly non-custodial (not blockable by feeCollector)
                emit FeePayFailed(feeAmount_);
            }
        } else {
            emit FeePaid(feeAmount_);
        }
    }
}

abstract contract AvocadoMultisigSigners is AvocadoMultisigSecondaryCore {
    /// @notice adds `addSigners_` to allowed signers and sets required signers count to `requiredSigners_`
    /// Note the `addSigners_` to be added must:
    ///     - NOT be duplicates (already present in current allowed signers)
    ///     - NOT be the zero address
    ///     - be sorted ascending
    function addSigners(address[] calldata addSigners_, uint8 requiredSigners_) external onlyDelegateCall {
        uint256 addSignersLength_ = addSigners_.length;

        // check array length and make sure signers can not be zero address
        // (only check for first elem needed, rest is checked through sort)
        if (addSignersLength_ == 0 || addSigners_[0] == address(0)) {
            revert AvocadoMultisig__InvalidParams();
        }

        address[] memory currentSigners_ = _getSigners();
        uint256 currentSignersLength_ = currentSigners_.length;

        uint256 newSignersLength_ = currentSignersLength_ + addSignersLength_;
        if (newSignersLength_ > MAX_SIGNERS_COUNT) {
            revert AvocadoMultisig__InvalidParams();
        }
        address[] memory newSigners_ = new address[](newSignersLength_);

        uint256 currentSignersPos_ = 0; // index of position of loop in currentSigners_ array
        uint256 addedCount_ = 0; // keep track of number of added signers of current signers array
        for (uint256 i; i < newSignersLength_; ) {
            unchecked {
                currentSignersPos_ = i - addedCount_;
            }

            if (
                addedCount_ == addSignersLength_ ||
                (currentSignersPos_ < currentSignersLength_ &&
                    currentSigners_[currentSignersPos_] < addSigners_[addedCount_])
            ) {
                // if already added all signers or if current signer is <  next signer, keep the current one
                newSigners_[i] = currentSigners_[currentSignersPos_];
            } else {
                //  add signer
                newSigners_[i] = addSigners_[addedCount_];

                emit SignerAdded(addSigners_[addedCount_]);

                unchecked {
                    ++addedCount_;
                }
            }

            if (i > 0 && newSigners_[i] <= newSigners_[i - 1]) {
                // make sure input signers are ordered ascending and no duplicate signers are added
                revert AvocadoMultisig__InvalidParams();
            }

            unchecked {
                ++i;
            }
        }

        // update values in storage
        _setSigners(newSigners_, requiredSigners_); // updates `signersCount`, checks and sets `requiredSigners_`

        // sync mappings at AvoSignersList -> must happen *after* storage write update
        // use call with success_ here to not block users transaction if the helper contract fails.
        // in case of failure, only emit event ListSyncFailed() so off-chain tracking is informed to react.
        (bool success_, ) = address(avoSignersList).call(
            abi.encodeCall(IAvoSignersList.syncAddAvoSignerMappings, (address(this), addSigners_))
        );
        if (!success_) {
            emit ListSyncFailed();
        }
    }

    /// @notice removes `removeSigners_` from allowed signers and sets required signers count to `requiredSigners_`
    /// Note the `removeSigners_` to be removed must:
    ///     - NOT be the owner
    ///     - be sorted ascending
    ///     - be present in current allowed signers
    function removeSigners(address[] calldata removeSigners_, uint8 requiredSigners_) external onlyDelegateCall {
        uint256 removeSignersLength_ = removeSigners_.length;
        if (removeSignersLength_ == 0) {
            revert AvocadoMultisig__InvalidParams();
        }

        address[] memory currentSigners_ = _getSigners();
        uint256 currentSignersLength_ = currentSigners_.length;

        uint256 newSignersLength_ = currentSignersLength_ - removeSignersLength_;

        address owner_ = IAvocado(address(this))._owner();

        address[] memory newSigners_ = new address[](newSignersLength_);

        uint256 currentInsertPos_ = 0; // index of position of loop in `newSigners_` array
        uint256 removedCount_ = 0; // keep track of number of removed signers of current signers array
        for (uint256 i; i < currentSignersLength_; ) {
            unchecked {
                currentInsertPos_ = i - removedCount_;
            }
            if (removedCount_ == removeSignersLength_ || currentSigners_[i] != removeSigners_[removedCount_]) {
                // if already removed all signers or if current signer is not a signer to be removed, keep the current one
                if (currentInsertPos_ < newSignersLength_) {
                    // make sure index to insert is within bounds of newSigners_ array
                    newSigners_[currentInsertPos_] = currentSigners_[i];
                } else {
                    // a signer has been passed in that was not found and thus we would be inserting at a position
                    // in newSigners_ array that overflows its length
                    revert AvocadoMultisig__InvalidParams();
                }
            } else {
                // remove signer, i.e. do not insert the current signer in the newSigners_ array

                // make sure signer to be removed is not the owner
                if (removeSigners_[removedCount_] == owner_) {
                    revert AvocadoMultisig__InvalidParams();
                }

                emit SignerRemoved(removeSigners_[removedCount_]);

                unchecked {
                    ++removedCount_;
                }
            }

            unchecked {
                ++i;
            }
        }

        if (removedCount_ != removeSignersLength_) {
            // this case should not be possible but it is a good cheap extra check to make sure nothing goes wrong
            // and the contract does not end up in an invalid signers state
            revert AvocadoMultisig__InvalidParams();
        }

        // update values in storage
        _setSigners(newSigners_, requiredSigners_); // updates `signersCount`, checks and sets `requiredSigners_`

        // sync mappings at AvoSignersList -> must happen *after* storage write update
        // use call with success_ here to not block users transaction if the helper contract fails.
        // in case of failure, only emit event ListSyncFailed() so off-chain tracking is informed to react.
        (bool success_, ) = address(avoSignersList).call(
            abi.encodeCall(IAvoSignersList.syncRemoveAvoSignerMappings, (address(this), removeSigners_))
        );
        if (!success_) {
            emit ListSyncFailed();
        }
    }

    /// @notice sets number of required signers for a valid request to `requiredSigners_`
    function setRequiredSigners(uint8 requiredSigners_) external onlyDelegateCall {
        _setRequiredSigners(requiredSigners_);
    }

    /***********************************|
    |               INTERNAL            |
    |__________________________________*/

    /// @dev writes `signers_` to storage with SSTORE2 and updates `signersCount`. uses `requiredSigners_` for sanity checks
    function _setSigners(address[] memory signers_, uint8 requiredSigners_) internal {
        uint256 signersCount_ = signers_.length;

        if (signersCount_ > MAX_SIGNERS_COUNT || signersCount_ == 0) {
            revert AvocadoMultisig__InvalidParams();
        }

        if (signersCount_ == 1) {
            // if signersCount is 1, owner must be the only signer (checked in `removeSigners`)
            // can reset to empty "uninitialized" signer vars state, making subsequent interactions cheaper
            // and even giving a gas refund for clearing out the slot 1
            if (requiredSigners_ != 1) {
                revert AvocadoMultisig__InvalidParams();
            }
            if (_requiredSigners > 1) {
                emit RequiredSignersSet(1);
            }

            assembly {
                sstore(1, 0) // Reset slot 1 (signers related vars) to 0
            }
        } else {
            _signersCount = uint8(signersCount_);

            _signersPointer = SSTORE2.write(abi.encode(signers_));

            // required signers vs signersCount is checked in _setRequiredSigners
            _setRequiredSigners(requiredSigners_);
        }
    }

    /// @dev sets number of required signers to `requiredSigners_` and emits event RequiredSignersSet, if valid
    function _setRequiredSigners(uint8 requiredSigners_) internal {
        // check if number of actual signers is > `requiredSigners_` because otherwise
        // the multisig would end up in a broken state where no execution is possible anymore
        if (requiredSigners_ == 0 || requiredSigners_ > _getSignersCount()) {
            revert AvocadoMultisig__InvalidParams();
        }

        if (_requiredSigners != requiredSigners_) {
            _requiredSigners = requiredSigners_;

            emit RequiredSignersSet(requiredSigners_);
        }
    }
}

abstract contract AvocadoMultisigInitialize is AvocadoMultisigSecondaryCore {
    /// @notice sets the initial state of the Multisig for `owner_` as owner and first and only required signer
    function initialize() external onlyDelegateCall {
        address owner_ = IAvocado(address(this))._owner();

        // owner must be EOA
        if (Address.isContract(owner_) || owner_ == address(0)) {
            revert AvocadoMultisig__InvalidParams();
        }

        // set _transientAllowHash so refund behaviour is already active for first tx and this cost is applied to deployment
        _resetTransientStorage();

        // emit events
        emit SignerAdded(owner_);
        emit RequiredSignersSet(1);

        // add owner as signer at AvoSignersList
        address[] memory signers_ = new address[](1);
        signers_[0] = owner_;
        // use call with success_ here to not block users transaction if the helper contract fails.
        // in case of failure, only emit event ListSyncFailed() so off-chain tracking is informed to react.
        (bool success_, ) = address(avoSignersList).call(
            abi.encodeCall(IAvoSignersList.syncAddAvoSignerMappings, (address(this), signers_))
        );
        if (!success_) {
            emit ListSyncFailed();
        }
    }
}

abstract contract AvocadoMultisigRevertReason is AvocadoMultisigSecondaryCore {
    /// @notice Get the revert reason from the returnedData (supports Panic, Error & Custom Errors).
    /// @param returnedData_ revert data of the call
    /// @return reason_      revert reason
    //
    // Based on https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/libs/CallUtils.sol
    // This is needed in order to provide some human-readable revert message from a call.
    function getRevertReasonFromReturnedData(bytes memory returnedData_) public pure returns (string memory reason_) {
        if (returnedData_.length < 4) {
            // case 1: catch all
            return "_REASON_NOT_DEFINED";
        }

        bytes4 errorSelector_;
        assembly {
            errorSelector_ := mload(add(returnedData_, 0x20))
        }
        if (errorSelector_ == bytes4(0x4e487b71)) {
            // case 2: Panic(uint256), selector 0x4e487b71 (Defined since 0.8.0)
            // ref: https://docs.soliditylang.org/en/v0.8.0/control-structures.html#panic-via-assert-and-error-via-require)

            // convert last byte to hex digits -> string to decode the panic code
            assembly {
                returnedData_ := add(returnedData_, 0x04) // skip error selector
            }
            reason_ = string.concat("_TARGET_PANICKED: ", Strings.toHexString(uint256(bytes32(returnedData_))));
        } else if (errorSelector_ == bytes4(0x08c379a0)) {
            // case 3: Error(string), selector 0x08c379a0 (Defined at least since 0.7.0)
            // based on https://ethereum.stackexchange.com/a/83577
            assembly {
                returnedData_ := add(returnedData_, 0x04) // skip error selector
            }
            reason_ = string.concat("_", abi.decode(returnedData_, (string)));
        } else {
            // case 4: Custom errors (Defined since 0.8.0)

            // convert bytes4 selector to string
            reason_ = string.concat("_CUSTOM_ERROR: ", Strings.toHexString(uint256(uint32(errorSelector_))));

            // decode custom error params if there are any, they are returned raw as hex string,
            // up to a length of `REVERT_REASON_MAX_LENGTH`
            uint256 paramsLength_ = (returnedData_.length - 4);
            if (paramsLength_ * 2 > REVERT_REASON_MAX_LENGTH + 8) {
                paramsLength_ = REVERT_REASON_MAX_LENGTH + 4;
            }
            bytes memory result_ = new bytes(paramsLength_ * 2);

            for (uint256 i; i < paramsLength_; ) {
                // use unchecked as i is < 4 and division.
                unchecked {
                    result_[2 * i] = _toHexDigit(uint8(returnedData_[i + 4]) / 16);
                    result_[2 * i + 1] = _toHexDigit(uint8(returnedData_[i + 4]) % 16);
                    ++i;
                }
            }

            reason_ = string.concat(reason_, ". PARAMS_RAW: ");
            reason_ = string.concat(reason_, string(result_));
        }

        {
            // truncate reason_ string to REVERT_REASON_MAX_LENGTH for reserveGas used to ensure Cast event is emitted
            if (bytes(reason_).length > REVERT_REASON_MAX_LENGTH) {
                bytes memory reasonBytes_ = bytes(reason_);
                uint256 maxLength_ = REVERT_REASON_MAX_LENGTH + 1; // cheaper than <= in each loop
                bytes memory truncatedRevertReason_ = new bytes(maxLength_);
                for (uint256 i; i < maxLength_; ) {
                    truncatedRevertReason_[i] = reasonBytes_[i];

                    unchecked {
                        ++i;
                    }
                }
                reason_ = string(truncatedRevertReason_);
            }
        }
    }

    /// @dev used to convert bytes4 selector to string
    function _toHexDigit(uint8 d) internal pure returns (bytes1) {
        // use unchecked as the operations with d can not over / underflow
        unchecked {
            if (d < 10) {
                return bytes1(uint8(bytes1("0")) + d);
            }
            if (d < 16) {
                return bytes1(uint8(bytes1("a")) + d - 10);
            }
        }
        revert AvocadoMultisig__ToHexDigit();
    }
}

abstract contract AvocadoMultisigSimulate is AvocadoMultisigSecondaryCore, AvocadoMultisigRevertReason {
    /// @notice                     executes a SIMULATE cast process for `cast()` or `castChainAgnostic()` for gas estimations.
    /// @dev                        - set `signaturesParams_` to empty to automatically simulate with required signers length.
    ///                             - if `signaturesParams_` first element signature is not set, or if first signer is set to
    ///                               0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF, then gas usage burn is simulated
    ///                               for verify signature functionality. DO NOT set signature to non-empty for subsequent
    ///                               elements then; set all signatures to empty!
    ///                             - if `signaturesParams_` is set normally, signatures are verified as in actual execute
    ///                             - buffer amounts for mock smart contract signers signature verification must be added
    ///                               off-chain as this varies on a case per case basis.
    /// @param params_              Cast params such as id, avoNonce and actions to execute
    /// @param forwardParams_       Cast params related to validity of forwarding as instructed and signed
    /// @param signaturesParams_    SignatureParams structs array for signature and signer:
    ///                              - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA.
    ///                                For smart contract signatures it must fulfill the requirements for the relevant
    ///                                smart contract `.isValidSignature()` EIP1271 logic
    ///                              - signer: address of the signature signer.
    ///                                Must match the actual signature signer or refer to the smart contract
    ///                                that must be an allowed signer and validates signature via EIP1271
    /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` struct for all `CastChainAgnosticParams`
    ///                             struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`.
    ///                             must be set in the same order as when creating the signature.
    /// @return success_            true if all actions were executed succesfully, false otherwise.
    /// @return revertReason_       revert reason if one of the actions fails in the following format:
    ///                             The revert reason will be prefixed with the index of the action.
    ///                             e.g. if action 1 fails, then the reason will be "1_reason".
    ///                             if an action in the flashloan callback fails (or an otherwise nested action),
    ///                             it will be prefixed with with two numbers: "1_2_reason".
    ///                             e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails,
    ///                             the reason will be 1_2_reason.
    function simulateCast(
        CastParams calldata params_,
        CastForwardParams calldata forwardParams_,
        SignatureParams[] memory signaturesParams_,
        ChainAgnosticHash[] memory chainAgnosticHashes_
    ) public onlyDelegateCall returns (bool success_, string memory revertReason_) {
        _simulateValidateParams(
            params_.actions.length,
            params_.avoNonce,
            forwardParams_.validAfter,
            forwardParams_.validUntil,
            forwardParams_.value
        );

        if (chainAgnosticHashes_.length > 0) {
            // validate that input `CastParams` and `CastForwardParams` are present in `chainAgnosticHashes_`
            _simulateValidateChainAgnostic(
                _castChainAgnosticParamsHash(params_, forwardParams_, block.chainid),
                chainAgnosticHashes_
            );
        }

        bytes32 digest_ = chainAgnosticHashes_.length > 0
            ? _getSigDigestChainAgnostic(chainAgnosticHashes_)
            : _getSigDigest(params_, forwardParams_);

        // if signaturesParams is not set, we use required signers to simulate gas usage
        address[] memory signers_ = new address[](
            signaturesParams_.length > 0 ? signaturesParams_.length : _getRequiredSigners()
        );
        if (
            signaturesParams_.length == 0 ||
            signaturesParams_[0].signature.length == 0 ||
            signaturesParams_[0].signer == 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF
        ) {
            // simulate verify sig by burning gas according to the measurements as listed in comments in
            // _verifySig` in AvocadoMultisigCore.

            uint256 signersCount_ = uint256(_getSignersCount());
            // Avoado signersCount == 1 ? -> 11_000 gas. If only owner is signer then it is cheaper -> 8_500 gas.
            uint256 verifySigGas_ = signersCount_ == 1 ? 8500 : 11_000;
            // Avoado signersCount > 1 ? -> 6400  + allowedSignersCount * 160 + signersLength * 6900
            if (signers_.length > 1) {
                verifySigGas_ = 6_400 + (signersCount_ * 160) + (signers_.length * 6_900);
            }
            // is non Sequential nonce? + 2200
            if (params_.avoNonce == -1) {
                verifySigGas_ += 2_200;
            }
            if (signaturesParams_.length == 0) {
                // calldata etc. cost per signaturesParams array element is ~3.300 gas
                verifySigGas_ += (signers_.length * 3_300);
            }
            // is smart contract signer buffer amount is external contract dependent, must be added off-chain

            // waste the gas `verifySigGas_` to correctly account for gas usage in estimateGas calls
            uint256 gasLeft_ = gasleft();
            uint256 wasteGasCounter_;
            while (gasLeft_ - gasleft() < verifySigGas_) wasteGasCounter_++;
        } else {
            signers_ = _verifySigWithRevert(digest_, signaturesParams_, params_.avoNonce == -1);
        }

        {
            (success_, revertReason_) = _simulateExecuteCast(
                params_,
                _dynamicReserveGas(CAST_EVENTS_RESERVE_GAS, signers_.length, params_.metadata.length),
                params_.avoNonce == -1 ? digest_ : bytes32(0)
            );
        }

        if (success_) {
            emit CastExecuted(params_.source, msg.sender, signers_, params_.metadata);
        } else {
            emit CastFailed(params_.source, msg.sender, signers_, revertReason_, params_.metadata);
        }
    }

    /// @dev SIMULATES: executes `actions_` with respective target, calldata, operation etc.
    //
    // this is called by _callTargets() and executeOperation() in AvocadoMultisig.sol when the tx.origin is dead address.
    // making this public and calling via delegateCall reduces code duplication and simplifies simulation logic.
    function _simulateExecuteActions(
        Action[] memory actions_,
        uint256 id_,
        bool isFlashloanCallback_
    ) external onlyDelegateCall {
        // reset _transientAllowHash immediately to avert reentrancy etc. & get the gas refund (4800)
        _resetTransientStorage();

        uint256 storageSlot0Snapshot_; // avoImpl, nonce, initialized vars
        uint256 storageSlot1Snapshot_; // signers related variables
        // delegate call = ids 1 and 21
        bool isDelegateCallId_ = id_ == 1 || id_ == 21;
        if (isDelegateCallId_) {
            // store values before execution to make sure core storage vars are not modified by a delegatecall.
            // this ensures the smart wallet does not end up in a corrupted state.
            // for mappings etc. it is hard to protect against storage changes, so we must rely on the owner / signer
            // to know what is being triggered and the effects of a tx
            assembly {
                storageSlot0Snapshot_ := sload(0x0) // avoImpl, nonce & initialized vars
                storageSlot1Snapshot_ := sload(0x1) // signers related variables
            }
        }

        uint256 actionsLength_ = actions_.length;
        for (uint256 i; i < actionsLength_; ) {
            Action memory action_ = actions_[i];

            // execute action
            bool success_;
            bytes memory result_;
            uint256 actionMinGasLeft_;
            if (action_.operation == 0 && (id_ < 2 || id_ == 20 || id_ == 21)) {
                // call (operation = 0 & id = call(0 / 20) or mixed(1 / 21))

                // low-level call will return success true also if action target is not even a contract.
                // we do not explicitly check for this, default interaction is via UI which can check and handle this.
                // Also applies to delegatecall etc.
                (success_, result_) = action_.target.call{ value: action_.value }(action_.data);

                // handle action failure right after external call to better detect out of gas errors
                if (!success_) {
                    _simulateHandleActionFailure(actionMinGasLeft_, i, result_);
                }
            } else if (action_.operation == 1 && isDelegateCallId_) {
                // delegatecall (operation = 1 & id = mixed(1 / 21))
                (success_, result_) = action_.target.delegatecall(action_.data);

                // handle action failure right after external call to better detect out of gas errors
                if (!success_) {
                    _simulateHandleActionFailure(actionMinGasLeft_, i, result_);
                }

                // reset _transientAllowHash to make sure it can not be set up in any way for reentrancy
                _resetTransientStorage();

                // for delegatecall, make sure storage was not modified. After every action, to also defend reentrancy
                uint256 storageSlot0_;
                uint256 storageSlot1_;
                assembly {
                    storageSlot0_ := sload(0x0) // avoImpl, nonce & initialized vars
                    storageSlot1_ := sload(0x1) // signers related variables
                }

                if (!(storageSlot0_ == storageSlot0Snapshot_ && storageSlot1_ == storageSlot1Snapshot_)) {
                    revert(string.concat(Strings.toString(i), "_AVO__MODIFIED_STORAGE"));
                }
            } else if (action_.operation == 2 && (id_ == 20 || id_ == 21)) {
                // flashloan (operation = 2 & id = flashloan(20 / 21))
                if (isFlashloanCallback_) {
                    revert(string.concat(Strings.toString(i), "_AVO__NO_FLASHLOAN_IN_FLASHLOAN"));
                }
                // flashloan is always executed via .call, flashloan aggregator uses `msg.sender`, so .delegatecall
                // wouldn't send funds to this contract but rather to the original sender.

                bytes memory data_ = action_.data;
                assembly {
                    data_ := add(data_, 4) // Skip function selector (4 bytes)
                }
                // get actions data from calldata action_.data. Only supports InstaFlashAggregatorInterface
                (, , , data_, ) = abi.decode(data_, (address[], uint256[], uint256, bytes, bytes));

                // set allowHash to signal allowed entry into executeOperation()
                _transientAllowHash = bytes31(
                    keccak256(abi.encode(data_, block.timestamp, EXECUTE_OPERATION_SELECTOR))
                );
                // store id_ in transient storage slot
                _transientId = uint8(id_);

                // handle action failure right after external call to better detect out of gas errors
                (success_, result_) = action_.target.call{ value: action_.value }(action_.data);

                if (!success_) {
                    _simulateHandleActionFailure(actionMinGasLeft_, i, result_);
                }

                // reset _transientAllowHash to prevent reentrancy during actions execution
                _resetTransientStorage();
            } else {
                // either operation does not exist or the id was not set according to what the action wants to execute
                revert(string.concat(Strings.toString(i), "_AVO__INVALID_ID_OR_OPERATION"));
            }

            unchecked {
                ++i;
            }
        }
    }

    /***********************************|
    |              INTERNAL             |
    |__________________________________*/

    /// @dev SIMULATES: executes multiple cast actions according to CastParams `params_`, reserving `reserveGas_` in this contract.
    /// Uses a sequential nonce unless `nonSequentialNonce_` is set.
    /// @return success_ boolean flag indicating whether all actions have been executed successfully.
    /// @return revertReason_ if `success_` is false, then revert reason is returned as string here.
    function _simulateExecuteCast(
        CastParams calldata params_,
        uint256 /** reserveGas_ */,
        bytes32 nonSequentialNonce_
    ) internal returns (bool success_, string memory revertReason_) {
        // set allowHash to signal allowed entry into _callTargets with actions in current block only
        _transientAllowHash = bytes31(
            keccak256(abi.encode(params_.actions, params_.id, block.timestamp, _CALL_TARGETS_SELECTOR))
        );

        // nonce must be used *always* if signature is valid
        if (nonSequentialNonce_ == bytes32(0)) {
            // use sequential nonce, already validated in `_validateParams()`
            _avoNonce++;
        } else {
            // use non-sequential nonce, already validated in `_verifySig()`
            nonSequentialNonces[nonSequentialNonce_] = 1;
        }

        // execute _callTargets via a low-level call to create a separate execution frame
        // this is used to revert all the actions if one action fails without reverting the whole transaction
        bytes memory calldata_ = abi.encodeCall(AvocadoMultisigProtected._callTargets, (params_.actions, params_.id));
        bytes memory result_;
        // using inline assembly for delegatecall to define custom gas amount that should stay here in caller
        assembly {
            success_ := delegatecall(
                gas(), // send all remaining gas for simulate
                // load _avoImpl from slot 0 and explicitly convert to address with bit mask
                and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff),
                add(calldata_, 0x20),
                mload(calldata_),
                0,
                0
            )
            let size := returndatasize()

            result_ := mload(0x40)
            mstore(0x40, add(result_, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            mstore(result_, size)
            returndatacopy(add(result_, 0x20), 0, size)
        }

        if (!success_) {
            if (result_.length == 0) {
                revertReason_ = "AVO__REASON_NOT_DEFINED";
            } else {
                assembly {
                    result_ := add(result_, 0x04)
                }
                revertReason_ = abi.decode(result_, (string));
            }
        }

        // reset all transient variables to get the gas refund (4800)
        _resetTransientStorage();
    }

    /// @dev SIMULATES: handles failure of an action execution depending on error cause,
    /// decoding and reverting with `result_` as reason string.
    function _simulateHandleActionFailure(
        uint256 /** actionMinGasLeft_ */,
        uint256 i_,
        bytes memory result_
    ) internal pure {
        revert(string.concat(Strings.toString(i_), getRevertReasonFromReturnedData(result_)));
    }

    /// @dev                   SIMULATES: Validates input params, reverts on invalid values.
    /// @param actionsLength_  the length of the actions array to execute
    /// @param avoNonce_       the avoNonce from input CastParams
    /// @param validAfter_     timestamp after which the request is valid
    /// @param validUntil_     timestamp before which the request is valid
    /// @param value_          the msg.value expected to be sent along
    function _simulateValidateParams(
        uint256 actionsLength_,
        int256 avoNonce_,
        uint256 validAfter_,
        uint256 validUntil_,
        uint256 value_
    ) internal view {
        // make sure actions are defined and nonce is valid:
        // must be -1 to use a non-sequential nonce or otherwise it must match the avoNonce
        if (!(actionsLength_ > 0 && (avoNonce_ == -1 || uint256(avoNonce_) == _avoNonce))) {
            // revert AvocadoMultisig__InvalidParams(); // no revert on simulate
        }

        // make sure request is within valid timeframe
        if ((validAfter_ > block.timestamp) || (validUntil_ > 0 && validUntil_ < block.timestamp)) {
            // revert AvocadoMultisig__InvalidTiming(); // no revert on simulate
        }

        // make sure msg.value matches `value_` (if set)
        if (value_ > 0 && msg.value != value_) {
            // revert AvocadoMultisig__InvalidParams(); // no revert on simulate
        }
    }

    /// @dev SIMULATES: Validates input params for `castChainAgnostic`: verifies that the `curCastChainAgnosticHash_` is present in
    ///      the `castChainAgnosticHashes_` array of hashes. Reverts with `AvocadoMultisig__InvalidParams` if not.
    ///      Reverts with `AvocadoMultisig__ChainAgnosticChainMismatch` if the hash is present but valid for another chain.
    function _simulateValidateChainAgnostic(
        bytes32 curCastChainAgnosticHash_,
        ChainAgnosticHash[] memory castChainAgnosticHashes_
    ) internal view {
        uint256 length_ = castChainAgnosticHashes_.length;
        // chain agnostic must be at least 2 hashes
        if (length_ > 1) {
            for (uint256 i; i < length_; ) {
                if (
                    curCastChainAgnosticHash_ == castChainAgnosticHashes_[i].hash &&
                    block.chainid == castChainAgnosticHashes_[i].chainId
                ) {
                    // hash must be found, and must be for the current chain to be valid
                    return;
                }

                unchecked {
                    ++i;
                }
            }
        }

        // `_castChainAgnosticParamsHash()` of current input params is not present in castChainAgnosticHashes_ -> revert
        // revert AvocadoMultisig__InvalidParams(); // no revert on simulate
    }
}

contract AvocadoMultisigSecondary is
    AvocadoMultisigBase,
    AvocadoMultisigSecondaryCore,
    AvocadoMultisigUpgradeTo,
    AvocadoMultisigOccupyNonces,
    AvocadoMultisigEIP1271,
    AvocadoMultisigPayAuthorizedFee,
    AvocadoMultisigSigners,
    AvocadoMultisigInitialize,
    AvocadoMultisigRevertReason,
    AvocadoMultisigSimulate
{
    /***********************************|
    |    CONSTRUCTOR / INITIALIZERS     |
    |__________________________________*/

    /// @notice                        constructor sets multiple immutable values for contracts and payFee fallback logic.
    /// @param avoRegistry_            address of the avoRegistry (proxy) contract
    /// @param avoForwarder_           address of the avoForwarder (proxy) contract
    ///                                to forward tx with valid signatures. must be valid version in AvoRegistry.
    /// @param avoSignersList_         address of the AvoSignersList (proxy) contract
    /// @param avoConfigV1_            AvoConfigV1 contract holding values for authorizedFee values
    constructor(
        IAvoRegistry avoRegistry_,
        address avoForwarder_,
        IAvoSignersList avoSignersList_,
        IAvoConfigV1 avoConfigV1_
    ) AvocadoMultisigBase(avoRegistry_, avoForwarder_, avoSignersList_, avoConfigV1_) {}
}

File 2 of 28 : IERC1271.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 *
 * _Available since v4.1._
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

File 3 of 28 : 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 4 of 28 : ERC1155Holder.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol)

pragma solidity ^0.8.0;

import "./ERC1155Receiver.sol";

/**
 * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
 *
 * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
 * stuck.
 *
 * @dev _Available since v3.1._
 */
contract ERC1155Holder is ERC1155Receiver {
    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address,
        address,
        uint256[] memory,
        uint256[] memory,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155BatchReceived.selector;
    }
}

File 5 of 28 : ERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../IERC1155Receiver.sol";
import "../../../utils/introspection/ERC165.sol";

/**
 * @dev _Available since v3.1._
 */
abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
    }
}

File 6 of 28 : 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 7 of 28 : ERC721Holder.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/utils/ERC721Holder.sol)

pragma solidity ^0.8.0;

import "../IERC721Receiver.sol";

/**
 * @dev Implementation of the {IERC721Receiver} interface.
 *
 * Accepts all token transfers.
 * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
 */
contract ERC721Holder is IERC721Receiver {
    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address,
        address,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC721Received.selector;
    }
}

File 8 of 28 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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
     * ====
     *
     * [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://diligence.consensys.net/posts/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.5.11/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 9 of 28 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 10 of 28 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 11 of 28 : 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 12 of 28 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

File 13 of 28 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 14 of 28 : Avocado.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

/// @title   IAvocado
/// @notice  interface to access internal vars on-chain
interface IAvocado {
    function _avoImpl() external view returns (address);

    function _data() external view returns (uint256);

    function _owner() external view returns (address);
}

/// @title      Avocado
/// @notice     Proxy for Avocados as deployed by the AvoFactory.
///             Basic Proxy with fallback to delegate and address for implementation contract at storage 0x0
//
// @dev        If this contract changes then the deployment addresses for new Avocados through factory change too!!
//             Relayers might want to pass in version as new param then to forward to the correct factory
contract Avocado {
    /// @notice flexible immutable data slot.
    /// first 20 bytes: address owner
    /// next 4 bytes: uint32 index
    /// next 1 byte: uint8 type
    /// next 9 bytes: used flexible for use-cases found in the future
    uint256 internal immutable _data;

    /// @notice address of the Avocado logic / implementation contract. IMPORTANT: SAME STORAGE SLOT AS FOR PROXY
    //
    // @dev    _avoImpl MUST ALWAYS be the first declared variable here in the proxy and in the logic contract
    //         when upgrading, the storage at memory address 0x0 is upgraded (first slot).
    //         To reduce deployment costs this variable is internal but can still be retrieved with
    //         _avoImpl(), see code and comments in fallback below
    address internal _avoImpl;

    /// @notice   sets _avoImpl & immutable _data, fetching it from msg.sender.
    //
    // @dev      those values are not input params to not influence the deterministic Create2 address!
    constructor() {
        // "\x8c\x65\x73\x89" is hardcoded bytes of function selector for transientDeployData()
        (, bytes memory deployData_) = msg.sender.staticcall(bytes("\x8c\x65\x73\x89"));

        address impl_;
        uint256 data_;
        assembly {
            // cast first 20 bytes to version address (_avoImpl)
            impl_ := mload(add(deployData_, 0x20))

            // cast bytes in position 0x40 to uint256 data; deployData_ plus 0x40 due to padding
            data_ := mload(add(deployData_, 0x40))
        }

        _data = data_;
        _avoImpl = impl_;
    }

    /// @notice Delegates the current call to `_avoImpl` unless one of the view methods is called:
    ///         `_avoImpl()` returns the address for `_avoImpl`, `_owner()` returns the first
    ///         20 bytes of `_data`, `_data()` returns `_data`.
    //
    // @dev    Mostly based on OpenZeppelin Proxy.sol
    // logic contract must not implement a function `_avoImpl()`, `_owner()` or  `_data()`
    // as they will not be callable due to collision
    fallback() external payable {
        uint256 data_ = _data;
        assembly {
            let functionSelector_ := calldataload(0)

            // 0xb2bdfa7b = function selector for _owner()
            if eq(functionSelector_, 0xb2bdfa7b00000000000000000000000000000000000000000000000000000000) {
                // store address owner at memory address 0x0, loading only last 20 bytes through the & mask
                mstore(0, and(data_, 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff))
                return(0, 0x20) // send 32 bytes of memory slot 0 as return value
            }

            // 0x68beab3f = function selector for _data()
            if eq(functionSelector_, 0x68beab3f00000000000000000000000000000000000000000000000000000000) {
                mstore(0, data_) // store uint256 _data at memory address 0x0
                return(0, 0x20) // send 32 bytes of memory slot 0 as return value
            }

            // load address avoImpl_ from storage
            let avoImpl_ := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)

            // first 4 bytes of calldata specify which function to call.
            // if those first 4 bytes == 874095c6 (function selector for _avoImpl()) then we return the _avoImpl address
            // The value is right padded to 32-bytes with 0s
            if eq(functionSelector_, 0x874095c600000000000000000000000000000000000000000000000000000000) {
                mstore(0, avoImpl_) // store address avoImpl_ at memory address 0x0
                return(0, 0x20) // send 32 bytes of memory slot 0 as return value
            }

            // @dev code below is taken from OpenZeppelin Proxy.sol _delegate function

            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), avoImpl_, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }
}

File 15 of 28 : AvocadoMultisig.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol";

import { IAvoRegistry } from "../interfaces/IAvoRegistry.sol";
import { IAvoSignersList } from "../interfaces/IAvoSignersList.sol";
import { IAvocadoMultisigV1Base } from "../interfaces/IAvocadoMultisigV1.sol";
import { IAvocadoMultisigV1Secondary, IAvocadoMultisigV1SecondaryConstants } from "../interfaces/IAvocadoMultisigV1Secondary.sol";
import { IAvocado } from "../Avocado.sol";
import { IAvoConfigV1 } from "../interfaces/IAvoConfigV1.sol";
import { AvocadoMultisigBase, AvocadoMultisigCore } from "./AvocadoMultisigCore.sol";

// --------------------------- DEVELOPER NOTES -----------------------------------------
// @dev IMPORTANT: all storage variables go into AvocadoMultisigVariables.sol
// -------------------------------------------------------------------------------------

// empty interface used for Natspec docs for nice layout in automatically generated docs:
//
/// @title  AvocadoMultisig v1.1.0
/// @notice Smart wallet enabling meta transactions through multiple EIP712 signatures (Multisig n out of m).
///
/// Supports:
/// - Executing arbitrary actions
/// - Receiving NFTs (ERC721)
/// - Receiving ERC1155 tokens
/// - ERC1271 smart contract signatures
/// - Instadapp Flashloan callbacks
/// - chain-agnostic signatures, user can sign once for execution of actions on different chains.
///
/// The `cast` method allows the AvoForwarder (relayer) to execute multiple arbitrary actions authorized by signature.
///
/// Broadcasters are expected to call the AvoForwarder contract `execute()` method, which also automatically
/// deploys an AvocadoMultisig if necessary first.
///
/// Upgradeable by calling `upgradeTo` through a `cast` / `castAuthorized` call.
///
/// The `castAuthorized` method allows the signers of the wallet to execute multiple arbitrary actions with signatures
/// without the AvoForwarder in between, to guarantee the smart wallet is truly non-custodial.
///
/// _@dev Notes:_
/// - This contract implements parts of EIP-2770 in a minimized form. E.g. domainSeparator is immutable etc.
/// - This contract does not implement ERC2771, because trusting an upgradeable "forwarder" bears a security
/// risk for this non-custodial wallet.
/// - Signature related logic is based off of OpenZeppelin EIP712Upgradeable.
/// - All signatures are validated for defaultChainId of `634` instead of `block.chainid` from opcode (EIP-1344).
/// - For replay protection, the current `block.chainid` instead is used in the EIP-712 salt.
interface AvocadoMultisig_V1 {}

/// @dev Simple contract to upgrade the implementation address stored at storage slot 0x0.
///      Mostly based on OpenZeppelin ERC1967Upgrade contract, adapted with onlySelf etc.
///      IMPORTANT: For any new implementation, the upgrade method MUST be in the implementation itself,
///      otherwise it can not be upgraded anymore!
abstract contract AvocadoMultisigSelfUpgradeable is AvocadoMultisigCore {
    /// @notice upgrade the contract to a new implementation address.
    ///         - Must be a valid version at the AvoRegistry.
    ///         - Can only be self-called (authorization same as for `cast` methods).
    /// @param avoImplementation_       New contract address
    /// @param afterUpgradeHookData_    flexible bytes for custom usage in after upgrade hook logic
    //
    // Implementation must call `_afterUpgradeHook()`
    function upgradeTo(address avoImplementation_, bytes calldata afterUpgradeHookData_) public onlySelf {
        _spell(address(avoSecondary), msg.data);
    }

    /// @notice hook called after executing an upgrade from previous `fromImplementation_`, with flexible bytes `data_`
    function _afterUpgradeHook(address fromImplementation_, bytes calldata data_) public virtual onlySelf {}
}

abstract contract AvocadoMultisigProtected is AvocadoMultisigCore {
    /***********************************|
    |             ONLY SELF             |
    |__________________________________*/

    /// @notice occupies the sequential `avoNonces_` in storage. This can be used to cancel / invalidate
    ///         a previously signed request(s) because the nonce will be "used" up.
    ///         - Can only be self-called (authorization same as for `cast` methods).
    /// @param  avoNonces_ sequential ascending ordered nonces to be occupied in storage.
    ///         E.g. if current AvoNonce is 77 and txs are queued with avoNonces 77, 78 and 79,
    ///         then you would submit [78, 79] here because 77 will be occupied by the tx executing
    ///         `occupyAvoNonces()` as an action itself. If executing via non-sequential nonces, you would
    ///         submit [77, 78, 79].
    ///         - Maximum array length is 5.
    ///         - gap from the current avoNonce will revert (e.g. [79, 80] if current one is 77)
    function occupyAvoNonces(uint88[] calldata avoNonces_) external onlySelf {
        _spell(address(avoSecondary), msg.data);
    }

    /// @notice occupies the `nonSequentialNonces_` in storage. This can be used to cancel / invalidate
    ///         previously signed request(s) because the nonce will be "used" up.
    ///         - Can only be self-called (authorization same as for `cast` methods).
    /// @param  nonSequentialNonces_ the non-sequential nonces to occupy
    function occupyNonSequentialNonces(bytes32[] calldata nonSequentialNonces_) external onlySelf {
        _spell(address(avoSecondary), msg.data);
    }

    /***********************************|
    |         FLASHLOAN CALLBACK        |
    |__________________________________*/

    /// @dev                    callback used by Instadapp Flashloan Aggregator, executes operations while owning
    ///                         the flashloaned amounts. `data_` must contain actions, one of them must pay back flashloan
    // /// @param assets_       assets_ received a flashloan for
    // /// @param amounts_      flashloaned amounts for each asset
    // /// @param premiums_     fees to pay for the flashloan
    /// @param initiator_       flashloan initiator -> must be this contract
    /// @param data_            data bytes containing the `abi.encoded()` actions that are executed like in `CastParams.actions`
    function executeOperation(
        address[] calldata /*  assets_ */,
        uint256[] calldata /*  amounts_ */,
        uint256[] calldata /*  premiums_ */,
        address initiator_,
        bytes calldata data_
    ) external returns (bool) {
        // @dev using the valid case inverted via one ! to optimize gas usage
        // data_ includes id and actions
        if (
            !(_transientAllowHash ==
                bytes31(keccak256(abi.encode(data_, block.timestamp, EXECUTE_OPERATION_SELECTOR))) &&
                initiator_ == address(this))
        ) {
            revert AvocadoMultisig__Unauthorized();
        }

        // get and reset transient id
        uint256 id_ = uint256(_transientId);
        _transientId = 0;

        if (tx.origin == 0x000000000000000000000000000000000000dEaD) {
            // tx origin 0x000000000000000000000000000000000000dEaD used for backend gas estimations -> forward to simulate
            _spell(
                address(avoSecondary),
                abi.encodeCall(avoSecondary._simulateExecuteActions, (abi.decode(data_, (Action[])), id_, true))
            );
        } else {
            // decode actions to be executed after getting the flashloan and id_ packed into the data_
            _executeActions(abi.decode(data_, (Action[])), id_, true);
        }

        return true;
    }

    /***********************************|
    |         INDIRECT INTERNAL         |
    |__________________________________*/

    /// @dev             executes a low-level .call or .delegateCall on all `actions_`.
    ///                  Can only be self-called by this contract under certain conditions, essentially internal method.
    ///                  This is called like an external call to create a separate execution frame.
    ///                  This way we can revert all the `actions_` if one fails without reverting the whole transaction.
    /// @param actions_  the actions to execute (target, data, value, operation)
    /// @param id_       id for `actions_`, see `CastParams.id`
    function _callTargets(Action[] calldata actions_, uint256 id_) external payable {
        if (tx.origin == 0x000000000000000000000000000000000000dEaD) {
            // tx origin 0x000000000000000000000000000000000000dEaD used for backend gas estimations -> forward to simulate
            _spell(address(avoSecondary), abi.encodeCall(avoSecondary._simulateExecuteActions, (actions_, id_, false)));
        } else {
            // _transientAllowHash must be set
            if (
                (_transientAllowHash !=
                    bytes31(keccak256(abi.encode(actions_, id_, block.timestamp, _CALL_TARGETS_SELECTOR))))
            ) {
                revert AvocadoMultisig__Unauthorized();
            }

            _executeActions(actions_, id_, false);
        }
    }
}

abstract contract AvocadoMultisigEIP1271 is AvocadoMultisigCore {
    /// @inheritdoc IERC1271
    /// @param signature This can be one of the following:
    ///         - empty: `hash` must be a previously signed message in storage then.
    ///         - 65 bytes: owner signature for a Multisig with only owner as signer (requiredSigners = 1, signers=[owner]).
    ///         - a multiple of 85 bytes, through grouping of 65 bytes signature + 20 bytes signer address each.
    ///           To signal decoding this way, the signature bytes must be prefixed with `0xDEC0DE6520`.
    ///         - the `abi.encode` result for `SignatureParams` struct array.
    /// @dev reverts with `AvocadoMultisig__InvalidEIP1271Signature` or `AvocadoMultisig__InvalidParams` if signature is invalid.
    /// @dev input `message_` is hashed with `domainSeparatorV4()` according to EIP712 typed data (`EIP1271_TYPE_HASH`)
    function isValidSignature(
        bytes32 hash,
        bytes calldata signature
    ) external view override returns (bytes4 magicValue) {
        // hashing with domain separator mitigates any potential replaying on other networks or other Avocados of the same owner
        hash = ECDSA.toTypedDataHash(
            _domainSeparatorV4(
                DOMAIN_SEPARATOR_SALT_HASHED // includes block.chainid
            ),
            keccak256(abi.encode(EIP1271_TYPE_HASH, hash))
        );

        // @dev function params without _ for inheritdoc
        if (signature.length == 0) {
            // must be pre-allow-listed via `signMessage` method
            if (_signedMessages[hash] != 1) {
                revert AvocadoMultisig__InvalidEIP1271Signature();
            }
        } else {
            (bool validSignature_, ) = _verifySig(
                hash,
                // decode signaturesParams_ from bytes signature
                avoSecondary.decodeEIP1271Signature(signature, IAvocado(address(this))._owner()),
                // we have no way to know nonce type, so make sure validity test covers everything.
                // setting this flag true will check that the digest is not a used non-sequential nonce.
                // unfortunately, for sequential nonces it adds unneeded verification and gas cost,
                // because the check will always pass, but there is no way around it.
                true
            );
            if (!validSignature_) {
                revert AvocadoMultisig__InvalidEIP1271Signature();
            }
        }

        return EIP1271_MAGIC_VALUE;
    }

    /// @notice Marks a bytes32 `message_` (signature digest) as signed, making it verifiable by EIP-1271 `isValidSignature()`.
    ///         - Can only be self-called (authorization same as for `cast` methods).
    /// @param message_ data hash to be allow-listed as signed
    /// @dev input `message_` is hashed with `domainSeparatorV4()` according to EIP712 typed data (`EIP1271_TYPE_HASH`)
    function signMessage(bytes32 message_) external onlySelf {
        _spell(address(avoSecondary), msg.data);
    }

    /// @notice Removes a previously `signMessage()` signed bytes32 `message_` (signature digest).
    ///         - Can only be self-called (authorization same as for `cast` methods).
    /// @param message_ data hash to be removed from allow-listed signatures
    function removeSignedMessage(bytes32 message_) external onlySelf {
        _spell(address(avoSecondary), msg.data);
    }
}

abstract contract AvocadoMultisigSigners is AvocadoMultisigCore {
    /// @notice adds `addSigners_` to allowed signers and sets required signers count to `requiredSigners_`
    /// Note the `addSigners_` to be added must:
    ///     - NOT be duplicates (already present in current allowed signers)
    ///     - NOT be the zero address
    ///     - be sorted ascending
    function addSigners(address[] calldata addSigners_, uint8 requiredSigners_) external onlySelf {
        _spell(address(avoSecondary), msg.data);
    }

    /// @notice removes `removeSigners_` from allowed signers and sets required signers count to `requiredSigners_`
    /// Note the `removeSigners_` to be removed must:
    ///     - NOT be the owner
    ///     - be sorted ascending
    ///     - be present in current allowed signers
    function removeSigners(address[] calldata removeSigners_, uint8 requiredSigners_) external onlySelf {
        _spell(address(avoSecondary), msg.data);
    }

    /// @notice sets number of required signers for a valid request to `requiredSigners_`
    function setRequiredSigners(uint8 requiredSigners_) external onlySelf {
        _spell(address(avoSecondary), msg.data);
    }
}

abstract contract AvocadoMultisigCast is AvocadoMultisigCore {
    /// @inheritdoc IAvocadoMultisigV1Base
    function getSigDigest(
        CastParams memory params_,
        CastForwardParams memory forwardParams_
    ) public view returns (bytes32) {
        return _getSigDigest(params_, forwardParams_);
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function verify(
        CastParams calldata params_,
        CastForwardParams calldata forwardParams_,
        SignatureParams[] calldata signaturesParams_
    ) external view returns (bool) {
        _validateParams(
            params_.actions.length,
            params_.avoNonce,
            forwardParams_.validAfter,
            forwardParams_.validUntil,
            forwardParams_.value
        );

        _verifySigWithRevert(_getSigDigest(params_, forwardParams_), signaturesParams_, params_.avoNonce == -1);

        return true;
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function cast(
        CastParams calldata params_,
        CastForwardParams calldata forwardParams_,
        SignatureParams[] memory signaturesParams_
    ) external payable returns (bool success_, string memory revertReason_) {
        return _cast(params_, forwardParams_, signaturesParams_, new ChainAgnosticHash[](0));
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function simulateCast(
        CastParams calldata params_,
        CastForwardParams calldata forwardParams_,
        SignatureParams[] memory signaturesParams_
    ) external payable returns (bool success_, string memory revertReason_) {
        return _simulateCast(params_, forwardParams_, signaturesParams_, new ChainAgnosticHash[](0), false);
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function estimateCast(
        CastParams calldata params_,
        CastForwardParams calldata forwardParams_,
        SignatureParams[] memory signaturesParams_
    ) external payable returns (bool success_, string memory revertReason_) {
        return _simulateCast(params_, forwardParams_, signaturesParams_, new ChainAgnosticHash[](0), true);
    }
}

abstract contract AvocadoMultisigCastChainAgnostic is AvocadoMultisigCore {
    /// @inheritdoc IAvocadoMultisigV1Base
    function castChainAgnostic(
        CastChainAgnosticParams calldata params_,
        SignatureParams[] memory signaturesParams_,
        ChainAgnosticHash[] calldata chainAgnosticHashes_
    ) external payable returns (bool success_, string memory revertReason_) {
        if (params_.chainId != block.chainid) {
            revert AvocadoMultisig__InvalidParams();
        }

        return _cast(params_.params, params_.forwardParams, signaturesParams_, chainAgnosticHashes_);
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function getChainAgnosticHashes(
        CastChainAgnosticParams[] calldata params_
    ) public pure returns (ChainAgnosticHash[] memory chainAgnosticHashes_) {
        uint256 length_ = params_.length;
        if (length_ < 2) {
            revert AvocadoMultisig__InvalidParams();
        }
        chainAgnosticHashes_ = new ChainAgnosticHash[](length_);
        for (uint256 i; i < length_; ) {
            chainAgnosticHashes_[i] = ChainAgnosticHash(
                _castChainAgnosticParamsHash(params_[i].params, params_[i].forwardParams, params_[i].chainId),
                params_[i].chainId
            );

            unchecked {
                ++i;
            }
        }
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function getSigDigestChainAgnostic(CastChainAgnosticParams[] calldata params_) public view returns (bytes32) {
        return _getSigDigestChainAgnostic(getChainAgnosticHashes(params_));
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function getSigDigestChainAgnosticFromHashes(
        ChainAgnosticHash[] calldata chainAgnosticHashes_
    ) public view returns (bytes32) {
        return _getSigDigestChainAgnostic(chainAgnosticHashes_);
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function verifyChainAgnostic(
        CastChainAgnosticParams calldata params_,
        SignatureParams[] calldata signaturesParams_,
        ChainAgnosticHash[] calldata chainAgnosticHashes_
    ) public view returns (bool) {
        if (params_.chainId != block.chainid) {
            revert AvocadoMultisig__InvalidParams();
        }

        _validateParams(
            params_.params.actions.length,
            params_.params.avoNonce,
            params_.forwardParams.validAfter,
            params_.forwardParams.validUntil,
            params_.forwardParams.value
        );

        _validateChainAgnostic(
            _castChainAgnosticParamsHash(params_.params, params_.forwardParams, block.chainid),
            chainAgnosticHashes_
        );

        _verifySigWithRevert(
            _getSigDigestChainAgnostic(chainAgnosticHashes_),
            signaturesParams_,
            params_.params.avoNonce == -1
        );

        return true;
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function simulateCastChainAgnostic(
        CastChainAgnosticParams calldata params_,
        SignatureParams[] memory signaturesParams_,
        ChainAgnosticHash[] calldata chainAgnosticHashes_
    ) external payable returns (bool success_, string memory revertReason_) {
        if (params_.chainId != block.chainid) {
            revert AvocadoMultisig__InvalidParams();
        }

        return _simulateCast(params_.params, params_.forwardParams, signaturesParams_, chainAgnosticHashes_, false);
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function estimateCastChainAgnostic(
        CastChainAgnosticParams calldata params_,
        SignatureParams[] memory signaturesParams_,
        ChainAgnosticHash[] calldata chainAgnosticHashes_
    ) external payable returns (bool success_, string memory revertReason_) {
        if (params_.chainId != block.chainid) {
            revert AvocadoMultisig__InvalidParams();
        }

        return _simulateCast(params_.params, params_.forwardParams, signaturesParams_, chainAgnosticHashes_, true);
    }
}

abstract contract AvocadoMultisigCastAuthorized is AvocadoMultisigCore {
    /// @inheritdoc IAvocadoMultisigV1Base
    function getSigDigestAuthorized(
        CastParams memory params_,
        CastAuthorizedParams memory authorizedParams_
    ) public view returns (bytes32) {
        return _getSigDigestAuthorized(params_, authorizedParams_);
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function verifyAuthorized(
        CastParams calldata params_,
        CastAuthorizedParams calldata authorizedParams_,
        SignatureParams[] calldata signaturesParams_
    ) external view returns (bool) {
        // make sure actions are defined and nonce is valid
        _validateParams(
            params_.actions.length,
            params_.avoNonce,
            authorizedParams_.validAfter,
            authorizedParams_.validUntil,
            0 // no value param in authorized interaction
        );

        _verifySigWithRevert(
            _getSigDigestAuthorized(params_, authorizedParams_),
            signaturesParams_,
            params_.avoNonce == -1
        );

        return true;
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function castAuthorized(
        CastParams calldata params_,
        CastAuthorizedParams calldata authorizedParams_,
        SignatureParams[] memory signaturesParams_
    ) external payable returns (bool success_, string memory revertReason_) {
        uint256 gasSnapshot_ = gasleft();

        // make sure actions are defined and nonce is valid
        _validateParams(
            params_.actions.length,
            params_.avoNonce,
            authorizedParams_.validAfter,
            authorizedParams_.validUntil,
            0 // no value param in authorized interaction
        );

        bytes32 digest_ = _getSigDigestAuthorized(params_, authorizedParams_);
        address[] memory signers_ = _verifySigWithRevert(digest_, signaturesParams_, params_.avoNonce == -1);

        (success_, revertReason_) = _executeCast(
            params_,
            _dynamicReserveGas(CAST_AUTHORIZED_RESERVE_GAS, signers_.length, params_.metadata.length),
            params_.avoNonce == -1 ? digest_ : bytes32(0)
        );

        // @dev on changes in the code below this point, measure the needed reserve gas via `gasleft()` anew
        // and update reserve gas constant amounts
        if (success_) {
            emit CastExecuted(params_.source, msg.sender, signers_, params_.metadata);
        } else {
            emit CastFailed(params_.source, msg.sender, signers_, revertReason_, params_.metadata);
        }

        // @dev `_payAuthorizedFee()` costs ~24.5k gas for if a fee is configured and maxFee is set
        _spell(
            address(avoSecondary),
            abi.encodeCall(avoSecondary.payAuthorizedFee, (gasSnapshot_, authorizedParams_.maxFee))
        );
        // @dev ending point for measuring reserve gas should be here. Also see comment in `AvocadoMultisigCore._executeCast()`
    }
}

contract AvocadoMultisig is
    AvocadoMultisigBase,
    AvocadoMultisigCore,
    AvocadoMultisigSelfUpgradeable,
    AvocadoMultisigProtected,
    AvocadoMultisigEIP1271,
    AvocadoMultisigSigners,
    AvocadoMultisigCast,
    AvocadoMultisigCastAuthorized,
    AvocadoMultisigCastChainAgnostic
{
    /***********************************|
    |    CONSTRUCTOR / INITIALIZERS     |
    |__________________________________*/

    /// @notice                        constructor sets multiple immutable values for contracts and payFee fallback logic.
    /// @param avoRegistry_            address of the avoRegistry (proxy) contract
    /// @param avoForwarder_           address of the avoForwarder (proxy) contract
    ///                                to forward tx with valid signatures. must be valid version in AvoRegistry.
    /// @param avoSignersList_         address of the AvoSignersList (proxy) contract
    /// @param avoConfigV1_            AvoConfigV1 contract holding values for authorizedFee values
    /// @param secondary_              AvocadoMultisigSecondary contract for extended logic
    constructor(
        IAvoRegistry avoRegistry_,
        address avoForwarder_,
        IAvoSignersList avoSignersList_,
        IAvoConfigV1 avoConfigV1_,
        IAvocadoMultisigV1Secondary secondary_
    ) AvocadoMultisigBase(avoRegistry_, avoForwarder_, avoSignersList_, avoConfigV1_) AvocadoMultisigCore(secondary_) {
        // sanity checks to ensure all immutables are configured with same values on AvoSecondary
        if (
            address(IAvocadoMultisigV1SecondaryConstants(address(secondary_)).avoRegistry()) != address(avoRegistry_) ||
            IAvocadoMultisigV1SecondaryConstants(address(secondary_)).avoForwarder() != avoForwarder_ ||
            address(IAvocadoMultisigV1SecondaryConstants(address(secondary_)).avoSignersList()) !=
            address(avoSignersList_) ||
            IAvocadoMultisigV1SecondaryConstants(address(secondary_)).AUTHORIZED_MIN_FEE() != AUTHORIZED_MIN_FEE ||
            IAvocadoMultisigV1SecondaryConstants(address(secondary_)).AUTHORIZED_MAX_FEE() != AUTHORIZED_MAX_FEE ||
            IAvocadoMultisigV1SecondaryConstants(address(secondary_)).AUTHORIZED_FEE_COLLECTOR() !=
            AUTHORIZED_FEE_COLLECTOR
        ) {
            revert AvocadoMultisig__InvalidParams();
        }
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function initialize() public initializer {
        _spell(address(avoSecondary), msg.data);
    }

    /***********************************|
    |            PUBLIC API             |
    |__________________________________*/

    receive() external payable {}

    /// @inheritdoc IAvocadoMultisigV1Base
    function domainSeparatorV4() public view returns (bytes32) {
        return
            _domainSeparatorV4(
                DOMAIN_SEPARATOR_SALT_HASHED // includes block.chainid
            );
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function domainSeparatorV4ChainAgnostic() public view returns (bytes32) {
        return
            _domainSeparatorV4(
                DOMAIN_SEPARATOR_CHAIN_AGNOSTIC_SALT_HASHED // includes default chain id (634)
            );
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function isSigner(address signer_) public view returns (bool) {
        address[] memory allowedSigners_ = _getSigners(); // includes owner

        uint256 allowedSignersLength_ = allowedSigners_.length;
        for (uint256 i; i < allowedSignersLength_; ) {
            if (allowedSigners_[i] == signer_) {
                return true;
            }

            unchecked {
                ++i;
            }
        }

        return false;
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function signers() public view returns (address[] memory signers_) {
        return _getSigners();
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function requiredSigners() public view returns (uint8) {
        return _getRequiredSigners();
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function signersCount() public view returns (uint8) {
        return _getSignersCount();
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function owner() public view returns (address) {
        return IAvocado(address(this))._owner();
    }

    /// @inheritdoc IAvocadoMultisigV1Base
    function index() public view returns (uint32) {
        return uint32(IAvocado(address(this))._data() >> 160);
    }

    /// @notice incrementing nonce for each valid tx executed (to ensure uniqueness)
    function avoNonce() public view returns (uint256) {
        return uint256(_avoNonce);
    }
}

File 16 of 28 : AvocadoMultisigCore.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import { ERC1155Holder } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";

import { InstaFlashReceiverInterface } from "../external/InstaFlashReceiverInterface.sol";
import { IAvoRegistry } from "../interfaces/IAvoRegistry.sol";
import { IAvoSignersList } from "../interfaces/IAvoSignersList.sol";
import { IAvocadoMultisigV1Base } from "../interfaces/IAvocadoMultisigV1.sol";
import { IAvocadoMultisigV1Secondary } from "../interfaces/IAvocadoMultisigV1Secondary.sol";
import { IAvoConfigV1 } from "../interfaces/IAvoConfigV1.sol";
import { IAvocado } from "../Avocado.sol";
import { AvocadoMultisigErrors } from "./AvocadoMultisigErrors.sol";
import { AvocadoMultisigEvents } from "./AvocadoMultisigEvents.sol";
import { AvocadoMultisigVariables } from "./AvocadoMultisigVariables.sol";
import { AvocadoMultisigInitializable } from "./lib/AvocadoMultisigInitializable.sol";
import { AvocadoMultisigStructs } from "./AvocadoMultisigStructs.sol";
import { AvocadoMultisigProtected } from "./AvocadoMultisig.sol";

/// @dev AvocadoMultisigBase contains all internal helper and base state needed for AvocadoMultisig main AND
///      secondary contract logic.
abstract contract AvocadoMultisigBase is
    AvocadoMultisigErrors,
    AvocadoMultisigEvents,
    AvocadoMultisigVariables,
    AvocadoMultisigStructs,
    AvocadoMultisigInitializable
{
    /***********************************|
    |    CONSTRUCTOR / INITIALIZERS     |
    |__________________________________*/

    constructor(
        IAvoRegistry avoRegistry_,
        address avoForwarder_,
        IAvoSignersList avoSignersList_,
        IAvoConfigV1 avoConfigV1_
    ) AvocadoMultisigVariables(avoRegistry_, avoForwarder_, avoSignersList_, avoConfigV1_) {
        // Ensure logic contract initializer is not abused by disabling initializing
        // see https://forum.openzeppelin.com/t/security-advisory-initialize-uups-implementation-contracts/15301
        // and https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract
        _disableInitializers();
    }

    /***********************************|
    |               INTERNAL            |
    |__________________________________*/

    /// @dev returns the dynamic reserve gas to be kept back for emitting the CastExecuted or CastFailed event
    function _dynamicReserveGas(
        uint256 fixedReserveGas_,
        uint256 signersCount_,
        uint256 metadataLength_
    ) internal pure returns (uint256 reserveGas_) {
        unchecked {
            // the gas usage for the emitting the CastExecuted/CastFailed events depends on the signers count
            // the cost per signer is PER_SIGNER_RESERVE_GAS. We calculate this dynamically to ensure
            // enough reserve gas is reserved in Multisigs with a higher signersCount.
            // same for metadata bytes length, dynamically calculated with cost per byte for emit event
            reserveGas_ =
                fixedReserveGas_ +
                (PER_SIGNER_RESERVE_GAS * signersCount_) +
                (EMIT_EVENT_COST_PER_BYTE * metadataLength_);
        }
    }

    /// @dev Returns the domain separator for the chain with id `DEFAULT_CHAIN_ID` and `salt_`
    function _domainSeparatorV4(bytes32 salt_) internal view returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    TYPE_HASH,
                    DOMAIN_SEPARATOR_NAME_HASHED,
                    DOMAIN_SEPARATOR_VERSION_HASHED,
                    DEFAULT_CHAIN_ID,
                    address(this),
                    salt_
                )
            );
    }

    /// @dev returns the EIP712 `CAST_PARAMS_TYPE_HASH` hash for `params_`.
    function _castParamsHash(CastParams memory params_) internal pure returns (bytes32) {
        // get keccak256s for actions
        uint256 actionsLength_ = params_.actions.length;
        bytes memory actionsAbiEncodePacked_;
        for (uint256 i; i < actionsLength_; ) {
            actionsAbiEncodePacked_ = abi.encodePacked(
                actionsAbiEncodePacked_,
                keccak256(
                    abi.encode(
                        ACTION_TYPE_HASH,
                        params_.actions[i].target,
                        keccak256(params_.actions[i].data),
                        params_.actions[i].value,
                        params_.actions[i].operation
                    )
                )
            );

            unchecked {
                ++i;
            }
        }

        return
            keccak256(
                abi.encode(
                    CAST_PARAMS_TYPE_HASH,
                    // actions[]
                    keccak256(actionsAbiEncodePacked_),
                    params_.id,
                    params_.avoNonce,
                    params_.salt,
                    params_.source,
                    keccak256(params_.metadata)
                )
            );
    }

    /// @dev returns the EIP712 `CAST_FORWARD_PARAMS_TYPE_HASH` hash for `forwardParams_`.
    function _castForwardParamsHash(CastForwardParams memory forwardParams_) internal pure returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    CAST_FORWARD_PARAMS_TYPE_HASH,
                    forwardParams_.gas,
                    forwardParams_.gasPrice,
                    forwardParams_.validAfter,
                    forwardParams_.validUntil,
                    forwardParams_.value
                )
            );
    }

    /// @dev returns the EIP712 `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` hash for `params_`, `forwardParams_`, `chainId_`.
    function _castChainAgnosticParamsHash(
        CastParams memory params_,
        CastForwardParams memory forwardParams_,
        uint256 chainId_
    ) internal pure returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH,
                    _castParamsHash(params_),
                    _castForwardParamsHash(forwardParams_),
                    chainId_
                )
            );
    }

    /// @dev                        gets the digest (hash) used to verify an EIP712 signature for `chainAgnosticHashes_`.
    /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` struct for all `CastChainAgnosticParams`
    ///                             struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`.
    ///                             must be set in the same order as when creating the signature.
    /// @return                     bytes32 digest e.g. for signature or non-sequential nonce
    function _getSigDigestChainAgnostic(
        ChainAgnosticHash[] memory chainAgnosticHashes_
    ) internal view returns (bytes32) {
        uint256 length_ = chainAgnosticHashes_.length;

        bytes memory hashesAbiEncodePacked_;
        bytes memory chainIdsAbiEncodePacked_;
        for (uint256 i; i < length_; ) {
            hashesAbiEncodePacked_ = abi.encodePacked(hashesAbiEncodePacked_, chainAgnosticHashes_[i].hash);
            chainIdsAbiEncodePacked_ = abi.encodePacked(chainIdsAbiEncodePacked_, chainAgnosticHashes_[i].chainId);

            unchecked {
                ++i;
            }
        }

        return
            ECDSA.toTypedDataHash(
                // domain separator without chain id as salt for chain agnofstic actions (chain id is in signed params instead)
                _domainSeparatorV4(
                    DOMAIN_SEPARATOR_CHAIN_AGNOSTIC_SALT_HASHED // includes default chain id (634)
                ),
                // structHash according to CAST_CHAIN_AGNOSTIC_TYPE_HASH
                keccak256(
                    abi.encode(
                        CAST_CHAIN_AGNOSTIC_TYPE_HASH,
                        // hash for castChainAgnostic() params[]
                        keccak256(hashesAbiEncodePacked_),
                        // Note: chain ids must be included in this hash here to guarantee input params for the ChainAgnosticHashes
                        // struct array at e.g. castChainAgnostic() are valid. Otherwise, chainIds for the non-current chain could
                        // be passed in wrongly there.
                        keccak256(chainIdsAbiEncodePacked_)
                    )
                )
            );
    }

    /// @dev                     gets the digest (hash) used to verify an EIP712 signature for `forwardParams_`
    /// @param params_           Cast params such as id, avoNonce and actions to execute
    /// @param forwardParams_    Cast params related to validity of forwarding as instructed and signed
    /// @return                  bytes32 digest e.g. for signature or non-sequential nonce
    function _getSigDigest(
        CastParams memory params_,
        CastForwardParams memory forwardParams_
    ) internal view returns (bytes32) {
        return
            ECDSA.toTypedDataHash(
                // domain separator
                _domainSeparatorV4(
                    DOMAIN_SEPARATOR_SALT_HASHED // includes block.chainid
                ),
                // structHash according to CAST_TYPE_HASH
                keccak256(abi.encode(CAST_TYPE_HASH, _castParamsHash(params_), _castForwardParamsHash(forwardParams_)))
            );
    }

    /// @dev                          Verifies a EIP712 signature, returning valid status in `isValid_` or reverting
    ///                               in case the params for the signatures / digest are wrong
    /// @param digest_                the EIP712 digest for the signature
    /// @param signaturesParams_      SignatureParams structs array for signature and signer:
    ///                               - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA.
    ///                                 For smart contract signatures it must fulfill the requirements for the relevant
    ///                                 smart contract `.isValidSignature()` EIP1271 logic
    ///                               - signer: address of the signature signer.
    ///                                 Must match the actual signature signer or refer to the smart contract
    ///                                 that must be an allowed signer and validates signature via EIP1271
    /// @param  isNonSequentialNonce_ flag to signal verify with non sequential nonce or not
    /// @return isValid_              true if the signature is valid, false otherwise
    /// @return recoveredSigners_     recovered valid signer addresses of the signatures. In case that `isValid_` is
    ///                               false, the last element in the array with a value is the invalid signer
    function _verifySig(
        bytes32 digest_,
        SignatureParams[] memory signaturesParams_,
        bool isNonSequentialNonce_
    ) internal view returns (bool isValid_, address[] memory recoveredSigners_) {
        // gas measurements:
        // cost until the for loop in verify signature is:
        // 1 signer 3374 (_getSigners() with only owner is cheaper)
        // 2 signers 6473
        // every additional allowedSigner (!) + 160 gas (additional SSTORE2 load cost)
        // For non-sequential nonce additional cold SLOAD + check cost is ~2200
        // dynamic cost for verifying any additional signer ~6900
        // So formula:
        // Avoado signersCount == 1 ? -> 11_000 gas
        // Avoado signersCount > 1 ? -> 6400  + allowedSignersCount * 160 + signersLength * 6900
        // is non Sequential nonce? + 2200
        // is smart contract signer? + buffer amount. A very basic ECDSA verify call like with e.g. MockSigner costs ~9k.
        uint256 signaturesLength_ = signaturesParams_.length;

        if (
            // enough signatures must be submitted to reach quorom of `requiredSigners`
            signaturesLength_ < _getRequiredSigners() ||
            // for non sequential nonce, if nonce is already used, the signature has already been used and is invalid
            (isNonSequentialNonce_ && nonSequentialNonces[digest_] == 1)
        ) {
            revert AvocadoMultisig__InvalidParams();
        }

        // fill recovered signers array for use in event emit
        recoveredSigners_ = new address[](signaturesLength_);

        // get current signers from storage
        address[] memory allowedSigners_ = _getSigners(); // includes owner
        uint256 allowedSignersLength_ = allowedSigners_.length;
        // track last allowed signer index for loop performance improvements
        uint256 lastAllowedSignerIndex_ = 0;

        bool isContract_ = false; // keeping this variable outside the loop so it is not re-initialized in each loop -> cheaper
        bool isAllowedSigner_ = false;
        for (uint256 i; i < signaturesLength_; ) {
            if (Address.isContract(signaturesParams_[i].signer)) {
                recoveredSigners_[i] = signaturesParams_[i].signer;
                // set flag that the signer is a contract so we don't have to check again in code below
                isContract_ = true;
            } else {
                // recover signer from signature
                recoveredSigners_[i] = ECDSA.recover(digest_, signaturesParams_[i].signature);

                if (signaturesParams_[i].signer != recoveredSigners_[i]) {
                    // signer does not match recovered signer. Either signer param is wrong or params used to
                    // build digest are not the same as for the signature
                    revert AvocadoMultisig__InvalidParams();
                }
            }

            // because signers in storage and signers from signatures input params must be ordered ascending,
            // the for loop can be optimized each new cycle to start from the position where the last signer
            // has been found.
            // this also ensures that input params signers must be ordered ascending off-chain
            // (which again is used to improve performance and simplifies ensuring unique signers)
            for (uint256 j = lastAllowedSignerIndex_; j < allowedSignersLength_; ) {
                if (allowedSigners_[j] == recoveredSigners_[i]) {
                    isAllowedSigner_ = true;
                    unchecked {
                        lastAllowedSignerIndex_ = j + 1; // set to j+1 so that next cycle starts at next array position
                    }
                    break;
                }

                // could be optimized by checking if allowedSigners_[j] > recoveredSigners_[i]
                // and immediately skipping with a `break;` if so. Because that implies that the recoveredSigners_[i]
                // can not be present in allowedSigners_ due to ascending sort.
                // But that would optimize the failing invalid case and increase cost for the default case where
                // the input data is valid -> skip.

                unchecked {
                    ++j;
                }
            }

            // validate if signer is allowed
            if (!isAllowedSigner_) {
                return (false, recoveredSigners_);
            } else {
                // reset `isAllowedSigner_` for next loop
                isAllowedSigner_ = false;
            }

            if (isContract_) {
                // validate as smart contract signature
                if (
                    IERC1271(signaturesParams_[i].signer).isValidSignature(digest_, signaturesParams_[i].signature) !=
                    EIP1271_MAGIC_VALUE
                ) {
                    // return value is not EIP1271_MAGIC_VALUE -> smart contract returned signature is invalid
                    return (false, recoveredSigners_);
                }

                // reset isContract for next loop (because defined outside of the loop to save gas)
                isContract_ = false;
            }
            // else already everything validated through recovered signer must be an allowed signer etc. in logic above

            unchecked {
                ++i;
            }
        }

        return (true, recoveredSigners_);
    }

    /// @dev                          Verifies a EIP712 signature, reverting if it is not valid.
    /// @param digest_                the EIP712 digest for the signature
    /// @param signaturesParams_      SignatureParams structs array for signature and signer:
    ///                               - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA.
    ///                                 For smart contract signatures it must fulfill the requirements for the relevant
    ///                                 smart contract `.isValidSignature()` EIP1271 logic
    ///                               - signer: address of the signature signer.
    ///                                 Must match the actual signature signer or refer to the smart contract
    ///                                 that must be an allowed signer and validates signature via EIP1271
    /// @param  isNonSequentialNonce_ flag to signal verify with non sequential nonce or not
    /// @return recoveredSigners_     recovered valid signer addresses of the signatures. In case that `isValid_` is
    ///                               false, the last element in the array with a value is the invalid signer
    function _verifySigWithRevert(
        bytes32 digest_,
        SignatureParams[] memory signaturesParams_,
        bool isNonSequentialNonce_
    ) internal view returns (address[] memory recoveredSigners_) {
        bool validSignature_;
        (validSignature_, recoveredSigners_) = _verifySig(digest_, signaturesParams_, isNonSequentialNonce_);

        // signature must be valid
        if (!validSignature_) {
            revert AvocadoMultisig__InvalidSignature();
        }
    }
}

/// @dev AvocadoMultisigCore contains all internal helper and base state needed for AvocadoMultisig.sol main logic
abstract contract AvocadoMultisigCore is
    AvocadoMultisigBase,
    ERC721Holder,
    ERC1155Holder,
    InstaFlashReceiverInterface,
    IAvocadoMultisigV1Base,
    IERC1271
{
    IAvocadoMultisigV1Secondary public immutable avoSecondary;

    constructor(IAvocadoMultisigV1Secondary secondary_) {
        if (address(secondary_) == address(0)) {
            revert AvocadoMultisig__InvalidParams();
        }

        avoSecondary = secondary_;
    }

    /// @dev ensures the method can only be called by the same contract itself.
    modifier onlySelf() {
        _requireSelfCalled();
        _;
    }

    /// @dev internal method for modifier logic to reduce bytecode size of contract.
    function _requireSelfCalled() internal view {
        if (msg.sender != address(this)) {
            revert AvocadoMultisig__Unauthorized();
        }
    }

    /// @dev method used to trigger a delegatecall with `data_` to `target_`.
    function _spell(address target_, bytes memory data_) internal returns (bytes memory response_) {
        assembly {
            let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)
            let size := returndatasize()

            response_ := mload(0x40)
            mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            mstore(response_, size)
            returndatacopy(add(response_, 0x20), 0, size)

            switch iszero(succeeded)
            case 1 {
                // throw if delegatecall failed
                returndatacopy(0x00, 0x00, size)
                revert(0x00, size)
            }
        }
    }

    /// @dev executes multiple cast actions according to CastParams `params_`, reserving `reserveGas_` in this contract.
    /// Uses a sequential nonce unless `nonSequentialNonce_` is set.
    /// @return success_ boolean flag indicating whether all actions have been executed successfully.
    /// @return revertReason_ if `success_` is false, then revert reason is returned as string here.
    function _executeCast(
        CastParams calldata params_,
        uint256 reserveGas_,
        bytes32 nonSequentialNonce_
    ) internal returns (bool success_, string memory revertReason_) {
        // set allowHash to signal allowed entry into _callTargets with actions in current block only
        _transientAllowHash = bytes31(
            keccak256(abi.encode(params_.actions, params_.id, block.timestamp, _CALL_TARGETS_SELECTOR))
        );

        // nonce must be used *always* if signature is valid
        if (nonSequentialNonce_ == bytes32(0)) {
            // use sequential nonce, already validated in `_validateParams()`
            _avoNonce++;
        } else {
            // use non-sequential nonce, already validated in `_verifySig()`
            nonSequentialNonces[nonSequentialNonce_] = 1;
        }

        // execute _callTargets via a low-level call to create a separate execution frame
        // this is used to revert all the actions if one action fails without reverting the whole transaction
        bytes memory calldata_ = abi.encodeCall(AvocadoMultisigProtected._callTargets, (params_.actions, params_.id));
        bytes memory result_;
        unchecked {
            if (gasleft() < reserveGas_ + 150) {
                // catch out of gas issues when available gas does not even cover reserveGas
                // -> immediately return with out of gas. + 150 to cover sload, sub etc.
                _resetTransientStorage();
                return (false, "AVO__OUT_OF_GAS");
            }
        }
        // using inline assembly for delegatecall to define custom gas amount that should stay here in caller
        assembly {
            success_ := delegatecall(
                // reserve some gas to make sure we can emit CastFailed event even for out of gas cases
                // and execute fee paying logic for `castAuthorized()`.
                // if gasleft() is less than the amount wanted to be sent along, sub would overflow and send all gas
                // that's why there is the explicit check a few lines up.
                sub(gas(), reserveGas_),
                // load _avoImpl from slot 0 and explicitly convert to address with bit mask
                and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff),
                add(calldata_, 0x20),
                mload(calldata_),
                0,
                0
            )
            let size := returndatasize()

            result_ := mload(0x40)
            mstore(0x40, add(result_, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            mstore(result_, size)
            returndatacopy(add(result_, 0x20), 0, size)
        }

        // @dev starting point for measuring reserve gas should be here right after actions execution.
        // on changes in code after execution (below here or below `_executeCast()` call in calling method),
        // measure the needed reserve gas via `gasleft()` anew and update `CAST_AUTHORIZED_RESERVE_GAS`
        // and `CAST_EVENTS_RESERVE_GAS` accordingly. use a method that forces maximum logic execution,
        // e.g. `castAuthorized()` with failing action.
        // gas measurement currently: ~1400 gas for logic in this method below
        if (!success_) {
            if (result_.length == 0) {
                if (gasleft() < reserveGas_ - 150) {
                    // catch out of gas errors where not the action ran out of gas but the logic around execution
                    // of the action itself. -150 to cover gas cost until here
                    revertReason_ = "AVO__OUT_OF_GAS";
                } else {
                    // @dev this case might be caused by edge-case out of gas errors that we were unable to catch,
                    // but could potentially also have other reasons
                    revertReason_ = "AVO__REASON_NOT_DEFINED";
                }
            } else {
                assembly {
                    result_ := add(result_, 0x04)
                }
                revertReason_ = abi.decode(result_, (string));
            }
        }

        // reset all transient variables to get the gas refund (4800)
        _resetTransientStorage();
    }

    /// @dev handles failure of an action execution depending on error cause,
    /// decoding and reverting with `result_` as reason string.
    function _handleActionFailure(uint256 actionMinGasLeft_, uint256 i_, bytes memory result_) internal view {
        if (gasleft() < actionMinGasLeft_) {
            // action ran out of gas. can not add action index as that again might run out of gas. keep revert minimal
            revert("AVO__OUT_OF_GAS");
        }
        revert(string.concat(Strings.toString(i_), avoSecondary.getRevertReasonFromReturnedData(result_)));
    }

    function _simulateCast(
        CastParams calldata params_,
        CastForwardParams calldata forwardParams_,
        SignatureParams[] memory signaturesParams_,
        ChainAgnosticHash[] memory chainAgnosticHashes_,
        bool estimate_
    ) internal returns (bool success_, string memory revertReason_) {
        if (
            !(msg.sender == 0x000000000000000000000000000000000000dEaD ||
                (msg.sender == avoForwarder && tx.origin == 0x000000000000000000000000000000000000dEaD))
        ) {
            // sender must be the allowed AvoForwarder and tx origin must be dead address,
            // or msg.sender must be 0x000000000000000000000000000000000000dEaD directly.
            revert AvocadoMultisig__Unauthorized();
        }

        (success_, revertReason_) = abi.decode(
            _spell(
                address(avoSecondary),
                abi.encodeCall(
                    avoSecondary.simulateCast,
                    (params_, forwardParams_, signaturesParams_, chainAgnosticHashes_)
                )
            ),
            (bool, string)
        );

        if (estimate_ && !success_) {
            // on estimate, revert to get a more accurate gas estimation result.
            revert(revertReason_);
        }
    }

    /// @dev                        executes a cast process for `cast()` or `castChainAgnostic()`
    /// @param params_              Cast params such as id, avoNonce and actions to execute
    /// @param forwardParams_       Cast params related to validity of forwarding as instructed and signed
    /// @param signaturesParams_    SignatureParams structs array for signature and signer:
    ///                              - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA.
    ///                                For smart contract signatures it must fulfill the requirements for the relevant
    ///                                smart contract `.isValidSignature()` EIP1271 logic
    ///                              - signer: address of the signature signer.
    ///                                Must match the actual signature signer or refer to the smart contract
    ///                                that must be an allowed signer and validates signature via EIP1271
    /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` struct for all `CastChainAgnosticParams`
    ///                             struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`.
    ///                             must be set in the same order as when creating the signature.
    /// @return success_            true if all actions were executed succesfully, false otherwise.
    /// @return revertReason_       revert reason if one of the actions fails in the following format:
    ///                             The revert reason will be prefixed with the index of the action.
    ///                             e.g. if action 1 fails, then the reason will be "1_reason".
    ///                             if an action in the flashloan callback fails (or an otherwise nested action),
    ///                             it will be prefixed with with two numbers: "1_2_reason".
    ///                             e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails,
    ///                             the reason will be 1_2_reason.
    function _cast(
        CastParams calldata params_,
        CastForwardParams calldata forwardParams_,
        SignatureParams[] memory signaturesParams_,
        ChainAgnosticHash[] memory chainAgnosticHashes_
    ) internal returns (bool success_, string memory revertReason_) {
        if (msg.sender != avoForwarder) {
            // sender must be the allowed AvoForwarder
            revert AvocadoMultisig__Unauthorized();
        }

        unchecked {
            // compare actual sent gas to user instructed gas, adding 500 to `gasleft()` for approx. already used gas
            if ((gasleft() + 500) < forwardParams_.gas) {
                // relayer has not sent enough gas to cover gas limit as user instructed.
                // this error should not be blamed on the user but rather on the relayer
                revert AvocadoMultisig__InsufficientGasSent();
            }
        }

        // @dev gas measurement: uses maximum 685 gas when all params must be validated
        _validateParams(
            params_.actions.length,
            params_.avoNonce,
            forwardParams_.validAfter,
            forwardParams_.validUntil,
            forwardParams_.value
        );

        bytes32 digest_;
        if (chainAgnosticHashes_.length > 0) {
            // validate that input `CastParams` and `CastForwardParams` are present in `chainAgnosticHashes_`
            _validateChainAgnostic(
                _castChainAgnosticParamsHash(params_, forwardParams_, block.chainid),
                chainAgnosticHashes_
            );

            digest_ = _getSigDigestChainAgnostic(chainAgnosticHashes_);
        } else {
            digest_ = _getSigDigest(params_, forwardParams_);
        }

        address[] memory signers_ = _verifySigWithRevert(digest_, signaturesParams_, params_.avoNonce == -1);

        (success_, revertReason_) = _executeCast(
            params_,
            _dynamicReserveGas(CAST_EVENTS_RESERVE_GAS, signers_.length, params_.metadata.length),
            params_.avoNonce == -1 ? digest_ : bytes32(0)
        );

        // @dev on changes in the code below this point, measure the needed reserve gas via `gasleft()` anew
        // and update the reserve gas constant amounts.
        // gas measurement currently: ~7500 gas for emit event with max revertReason length
        if (success_) {
            emit CastExecuted(params_.source, msg.sender, signers_, params_.metadata);
        } else {
            emit CastFailed(params_.source, msg.sender, signers_, revertReason_, params_.metadata);
        }
        // @dev ending point for measuring reserve gas should be here. Also see comment in `AvocadoMultisigCore._executeCast()`
    }

    /// @dev executes `actions_` with respective target, calldata, operation etc.
    /// IMPORTANT: Validation of `id_` and `_transientAllowHash` is expected to happen in `executeOperation()` and `_callTargets()`.
    /// catches out of gas errors (as well as possible), reverting with `AVO__OUT_OF_GAS`.
    /// reverts with action index + error code in case of failure (e.g. "1_SOME_ERROR").
    function _executeActions(Action[] memory actions_, uint256 id_, bool isFlashloanCallback_) internal {
        // reset _transientAllowHash immediately to avert reentrancy etc. & get the gas refund (4800)
        _resetTransientStorage();

        uint256 storageSlot0Snapshot_; // avoImpl, nonce, initialized vars
        uint256 storageSlot1Snapshot_; // signers related variables
        // delegate call = ids 1 and 21
        bool isDelegateCallId_ = id_ == 1 || id_ == 21;
        if (isDelegateCallId_) {
            // store values before execution to make sure core storage vars are not modified by a delegatecall.
            // this ensures the smart wallet does not end up in a corrupted state.
            // for mappings etc. it is hard to protect against storage changes, so we must rely on the owner / signer
            // to know what is being triggered and the effects of a tx
            assembly {
                storageSlot0Snapshot_ := sload(0x0) // avoImpl, nonce & initialized vars
                storageSlot1Snapshot_ := sload(0x1) // signers related variables
            }
        }

        uint256 actionsLength_ = actions_.length;
        for (uint256 i; i < actionsLength_; ) {
            Action memory action_ = actions_[i];

            // execute action
            bool success_;
            bytes memory result_;
            uint256 actionMinGasLeft_;
            if (action_.operation == 0 && (id_ < 2 || id_ == 20 || id_ == 21)) {
                // call (operation = 0 & id = call(0 / 20) or mixed(1 / 21))
                unchecked {
                    // store amount of gas that stays with caller, according to EIP150 to detect out of gas errors
                    // -> as close as possible to actual call
                    actionMinGasLeft_ = gasleft() / 64;
                }

                // low-level call will return success true also if action target is not even a contract.
                // we do not explicitly check for this, default interaction is via UI which can check and handle this.
                // Also applies to delegatecall etc.
                (success_, result_) = action_.target.call{ value: action_.value }(action_.data);

                // handle action failure right after external call to better detect out of gas errors
                if (!success_) {
                    _handleActionFailure(actionMinGasLeft_, i, result_);
                }
            } else if (action_.operation == 1 && isDelegateCallId_) {
                // delegatecall (operation = 1 & id = mixed(1 / 21))
                unchecked {
                    // store amount of gas that stays with caller, according to EIP150 to detect out of gas errors
                    // -> as close as possible to actual call
                    actionMinGasLeft_ = gasleft() / 64;
                }

                (success_, result_) = action_.target.delegatecall(action_.data);

                // handle action failure right after external call to better detect out of gas errors
                if (!success_) {
                    _handleActionFailure(actionMinGasLeft_, i, result_);
                }

                // reset _transientAllowHash to make sure it can not be set up in any way for reentrancy
                _resetTransientStorage();

                // for delegatecall, make sure storage was not modified. After every action, to also defend reentrancy
                uint256 storageSlot0_;
                uint256 storageSlot1_;
                assembly {
                    storageSlot0_ := sload(0x0) // avoImpl, nonce & initialized vars
                    storageSlot1_ := sload(0x1) // signers related variables
                }

                if (!(storageSlot0_ == storageSlot0Snapshot_ && storageSlot1_ == storageSlot1Snapshot_)) {
                    revert(string.concat(Strings.toString(i), "_AVO__MODIFIED_STORAGE"));
                }
            } else if (action_.operation == 2 && (id_ == 20 || id_ == 21)) {
                // flashloan (operation = 2 & id = flashloan(20 / 21))
                if (isFlashloanCallback_) {
                    revert(string.concat(Strings.toString(i), "_AVO__NO_FLASHLOAN_IN_FLASHLOAN"));
                }
                // flashloan is always executed via .call, flashloan aggregator uses `msg.sender`, so .delegatecall
                // wouldn't send funds to this contract but rather to the original sender.

                bytes memory data_ = action_.data;
                assembly {
                    data_ := add(data_, 4) // Skip function selector (4 bytes)
                }
                // get actions data from calldata action_.data. Only supports InstaFlashAggregatorInterface
                (, , , data_, ) = abi.decode(data_, (address[], uint256[], uint256, bytes, bytes));

                // set allowHash to signal allowed entry into executeOperation()
                _transientAllowHash = bytes31(
                    keccak256(abi.encode(data_, block.timestamp, EXECUTE_OPERATION_SELECTOR))
                );
                // store id_ in transient storage slot
                _transientId = uint8(id_);

                unchecked {
                    // store amount of gas that stays with caller, according to EIP150 to detect out of gas errors
                    // -> as close as possible to actual call
                    actionMinGasLeft_ = gasleft() / 64;
                }

                // handle action failure right after external call to better detect out of gas errors
                (success_, result_) = action_.target.call{ value: action_.value }(action_.data);

                if (!success_) {
                    _handleActionFailure(actionMinGasLeft_, i, result_);
                }

                // reset _transientAllowHash to prevent reentrancy during actions execution
                _resetTransientStorage();
            } else {
                // either operation does not exist or the id was not set according to what the action wants to execute
                revert(string.concat(Strings.toString(i), "_AVO__INVALID_ID_OR_OPERATION"));
            }

            unchecked {
                ++i;
            }
        }
    }

    /// @dev                   Validates input params, reverts on invalid values.
    /// @param actionsLength_  the length of the actions array to execute
    /// @param avoNonce_       the avoNonce from input CastParams
    /// @param validAfter_     timestamp after which the request is valid
    /// @param validUntil_     timestamp before which the request is valid
    /// @param value_          the msg.value expected to be sent along
    function _validateParams(
        uint256 actionsLength_,
        int256 avoNonce_,
        uint256 validAfter_,
        uint256 validUntil_,
        uint256 value_
    ) internal view {
        // make sure actions are defined and nonce is valid:
        // must be -1 to use a non-sequential nonce or otherwise it must match the avoNonce
        if (!(actionsLength_ > 0 && (avoNonce_ == -1 || uint256(avoNonce_) == _avoNonce))) {
            revert AvocadoMultisig__InvalidParams();
        }

        // make sure request is within valid timeframe
        if ((validAfter_ > block.timestamp) || (validUntil_ > 0 && validUntil_ < block.timestamp)) {
            revert AvocadoMultisig__InvalidTiming();
        }

        // make sure msg.value matches `value_` (if set)
        if (value_ > 0 && msg.value != value_) {
            revert AvocadoMultisig__InvalidParams();
        }
    }

    /// @dev Validates input params for `castChainAgnostic`: verifies that the `curCastChainAgnosticHash_` is present in
    ///      the `castChainAgnosticHashes_` array of hashes. Reverts with `AvocadoMultisig__InvalidParams` if not.
    ///      Reverts with `AvocadoMultisig__ChainAgnosticChainMismatch` if the hash is present but valid for another chain.
    function _validateChainAgnostic(
        bytes32 curCastChainAgnosticHash_,
        ChainAgnosticHash[] memory castChainAgnosticHashes_
    ) internal view {
        uint256 length_ = castChainAgnosticHashes_.length;
        // chain agnostic must be at least 2 hashes
        if (length_ > 1) {
            for (uint256 i; i < length_; ) {
                if (
                    curCastChainAgnosticHash_ == castChainAgnosticHashes_[i].hash &&
                    block.chainid == castChainAgnosticHashes_[i].chainId
                ) {
                    // hash must be found, and must be for the current chain to be valid
                    return;
                }

                unchecked {
                    ++i;
                }
            }
        }

        // `_castChainAgnosticParamsHash()` of current input params is not present in castChainAgnosticHashes_ -> revert
        revert AvocadoMultisig__InvalidParams();
    }

    /// @dev                      gets the digest (hash) used to verify an EIP712 signature for `authorizedParams_`
    /// @param params_            Cast params such as id, avoNonce and actions to execute
    /// @param authorizedParams_  Cast params related to execution through owner such as maxFee
    /// @return                   bytes32 digest e.g. for signature or non-sequential nonce
    function _getSigDigestAuthorized(
        CastParams memory params_,
        CastAuthorizedParams memory authorizedParams_
    ) internal view returns (bytes32) {
        return
            ECDSA.toTypedDataHash(
                // domain separator
                _domainSeparatorV4(
                    DOMAIN_SEPARATOR_SALT_HASHED // includes block.chainid
                ),
                // structHash according to CAST_AUTHORIZED_TYPE_HASH
                keccak256(
                    abi.encode(
                        CAST_AUTHORIZED_TYPE_HASH,
                        _castParamsHash(params_),
                        // CastAuthorizedParams hash
                        keccak256(
                            abi.encode(
                                CAST_AUTHORIZED_PARAMS_TYPE_HASH,
                                authorizedParams_.maxFee,
                                authorizedParams_.gasPrice,
                                authorizedParams_.validAfter,
                                authorizedParams_.validUntil
                            )
                        )
                    )
                )
            );
    }
}

File 17 of 28 : AvocadoMultisigErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

abstract contract AvocadoMultisigErrors {
    /// @notice thrown when a method is called with invalid params (e.g. a zero address as input param)
    error AvocadoMultisig__InvalidParams();

    /// @notice thrown when a signature is not valid (e.g. not signed by enough allowed signers)
    error AvocadoMultisig__InvalidSignature();

    /// @notice thrown when someone is trying to execute a in some way auth protected logic
    error AvocadoMultisig__Unauthorized();

    /// @notice thrown when forwarder/relayer does not send enough gas as the user has defined.
    ///         this error should not be blamed on the user but rather on the relayer
    error AvocadoMultisig__InsufficientGasSent();

    /// @notice thrown when a signature has expired or when a request isn't valid yet
    error AvocadoMultisig__InvalidTiming();

    /// @notice thrown when _toHexDigit() fails
    error AvocadoMultisig__ToHexDigit();

    /// @notice thrown when an EIP1271 signature is invalid
    error AvocadoMultisig__InvalidEIP1271Signature();

    /// @notice thrown when a `castAuthorized()` `fee` is bigger than the `maxFee` given through the input param
    error AvocadoMultisig__MaxFee(uint256 fee, uint256 maxFee);

    /// @notice thrown when `castAuthorized()` fee can not be covered by available contract funds
    error AvocadoMultisig__InsufficientBalance(uint256 fee);
}

File 18 of 28 : AvocadoMultisigEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

abstract contract AvocadoMultisigEvents {
    /// @notice Emitted when the implementation is upgraded to a new logic contract
    event Upgraded(address indexed newImplementation);

    /// @notice Emitted when a message is marked as allowed smart contract signature
    event SignedMessage(bytes32 indexed messageHash);

    /// @notice Emitted when a previously allowed signed message is removed
    event RemoveSignedMessage(bytes32 indexed messageHash);

    /// @notice emitted when the avoNonce in storage is increased through an authorized call to
    /// `occupyAvoNonces()`, which can be used to cancel a previously signed request
    event AvoNonceOccupied(uint256 indexed occupiedAvoNonce);

    /// @notice emitted when a non-sequential nonce is occupied in storage through an authorized call to
    /// `useNonSequentialNonces()`, which can be used to cancel a previously signed request
    event NonSequentialNonceOccupied(bytes32 indexed occupiedNonSequentialNonce);

    /// @notice Emitted when a fee is paid through use of the `castAuthorized()` method
    event FeePaid(uint256 indexed fee);

    /// @notice Emitted when paying a fee reverts at the recipient
    event FeePayFailed(uint256 indexed fee);

    /// @notice emitted when syncing to the AvoSignersList fails
    event ListSyncFailed();

    /// @notice emitted when all actions are executed successfully.
    /// caller = owner / AvoForwarder address. signers = addresses that triggered this execution
    event CastExecuted(address indexed source, address indexed caller, address[] signers, bytes metadata);

    /// @notice emitted if one of the executed actions fails. The reason will be prefixed with the index of the action.
    /// e.g. if action 1 fails, then the reason will be 1_reason
    /// if an action in the flashloan callback fails, it will be prefixed with with two numbers:
    /// e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails, the reason will be 1_2_reason.
    /// caller = owner / AvoForwarder address. signers = addresses that triggered this execution
    /// Note If the signature was invalid, the `signers` array last set element is the signer that caused the revert
    event CastFailed(address indexed source, address indexed caller, address[] signers, string reason, bytes metadata);

    /// @notice emitted when a signer is added as Multisig signer
    event SignerAdded(address indexed signer);

    /// @notice emitted when a signer is removed as Multisig signer
    event SignerRemoved(address indexed signer);

    /// @notice emitted when the required signers count is updated
    event RequiredSignersSet(uint8 indexed requiredSigners);
}

File 19 of 28 : AvocadoMultisigStructs.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

interface AvocadoMultisigStructs {
    /// @notice a combination of a bytes signature and its signer.
    struct SignatureParams {
        ///
        /// @param signature ECDSA signature of `getSigDigest()` for default flow or EIP1271 smart contract signature
        bytes signature;
        ///
        /// @param signer signer of the signature. Can be set to smart contract address that supports EIP1271
        address signer;
    }

    /// @notice an arbitrary executable action
    struct Action {
        ///
        /// @param target the target address to execute the action on
        address target;
        ///
        /// @param data the calldata to be passed to the call for each target
        bytes data;
        ///
        /// @param value the msg.value to be passed to the call for each target. set to 0 if none
        uint256 value;
        ///
        /// @param operation type of operation to execute:
        /// 0 -> .call; 1 -> .delegateCall, 2 -> flashloan (via .call)
        uint256 operation;
    }

    /// @notice common params for both `cast()` and `castAuthorized()`
    struct CastParams {
        Action[] actions;
        ///
        /// @param id             Required:
        ///                       id for actions, e.g. 0 = CALL, 1 = MIXED (call and delegatecall),
        ///                                           20 = FLASHLOAN_CALL, 21 = FLASHLOAN_MIXED
        uint256 id;
        ///
        /// @param avoNonce   Required:
        ///                       avoNonce to be used for this tx. Must equal the avoNonce value on smart
        ///                       wallet or alternatively it must be set to -1 to use a non-sequential nonce instead
        int256 avoNonce;
        ///
        /// @param salt           Optional:
        ///                       Salt to customize non-sequential nonce (if `avoNonce` is set to -1)
        bytes32 salt;
        ///
        /// @param source         Optional:
        ///                       Source / referral for this tx
        address source;
        ///
        /// @param metadata       Optional:
        ///                       metadata for any potential additional data to be tracked in the tx
        bytes metadata;
    }

    /// @notice `cast()` input params related to forwarding validity
    struct CastForwardParams {
        ///
        /// @param gas            Optional:
        ///                       As EIP-2770: user instructed minimum amount of gas that the relayer (AvoForwarder)
        ///                       must send for the execution. Sending less gas will fail the tx at the cost of the relayer.
        ///                       Also protects against potential gas griefing attacks
        ///                       See https://ronan.eth.limo/blog/ethereum-gas-dangers/
        uint256 gas;
        ///
        /// @param gasPrice       Optional:
        ///                       Not implemented / used yet
        uint256 gasPrice;
        ///
        /// @param validAfter     Optional:
        ///                       the earliest block timestamp that the request can be forwarded in,
        ///                       or 0 if the request is not time-limited to occur after a certain time.
        ///                       Protects against relayers executing a certain transaction at an earlier moment
        ///                       not intended by the user, where it might have a completely different effect.
        uint256 validAfter;
        ///
        /// @param validUntil     Optional:
        ///                       Similar to EIP-2770: the latest block timestamp (instead of block number) the request
        ///                       can be forwarded, or 0 if request should be valid forever.
        ///                       Protects against relayers executing a certain transaction at a later moment
        ///                       not intended by the user, where it might have a completely different effect.
        uint256 validUntil;
        ///
        /// @param value          Optional:
        ///                       Not implemented / used yet (`msg.value` amount the broadcaster should send along)
        uint256 value;
    }

    /// @notice `castAuthorized()` input params
    struct CastAuthorizedParams {
        ///
        /// @param maxFee         Optional:
        ///                       the maximum Avocado charge-up allowed to be paid for tx execution
        uint256 maxFee;
        ///
        /// @param gasPrice       Optional:
        ///                       Not implemented / used yet
        uint256 gasPrice;
        ///
        /// @param validAfter     Optional:
        ///                       the earliest block timestamp that the request can be forwarded in,
        ///                       or 0 if the request is not time-limited to occur after a certain time.
        ///                       Protects against relayers executing a certain transaction at an earlier moment
        ///                       not intended by the user, where it might have a completely different effect.
        uint256 validAfter;
        ///
        /// @param validUntil     Optional:
        ///                       Similar to EIP-2770: the latest block timestamp (instead of block number) the request
        ///                       can be forwarded, or 0 if request should be valid forever.
        ///                       Protects against relayers executing a certain transaction at a later moment
        ///                       not intended by the user, where it might have a completely different effect.
        uint256 validUntil;
    }

    /// @notice params for `castChainAgnostic()` to be used when casting txs on multiple chains with one signature
    struct CastChainAgnosticParams {
        ///
        /// @param params cast params containing actions to be executed etc.
        CastParams params;
        ///
        /// @param forwardParams params related to forwarding validity
        CastForwardParams forwardParams;
        ///
        /// @param chainId chainId where these actions are valid
        uint256 chainId;
    }

    /// @notice unique chain agnostic hash with chain id to be used for chain agnostic interactions
    struct ChainAgnosticHash {
        ///
        /// @param hash EIP712 type `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` hash for one specific `CastChainAgnosticParams` struct
        bytes32 hash;
        ///
        /// @param chainId chainId where this `hash` is for
        uint256 chainId;
    }
}

File 20 of 28 : AvocadoMultisigVariables.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

import { SSTORE2 } from "solmate/src/utils/SSTORE2.sol";

import { IAvoRegistry } from "../interfaces/IAvoRegistry.sol";
import { IAvoSignersList } from "../interfaces/IAvoSignersList.sol";
import { IAvoConfigV1 } from "../interfaces/IAvoConfigV1.sol";
import { IAvocado } from "../Avocado.sol";
import { AvocadoMultisigErrors } from "./AvocadoMultisigErrors.sol";
import { AvocadoMultisigEvents } from "./AvocadoMultisigEvents.sol";

abstract contract AvocadoMultisigConstants is AvocadoMultisigErrors {
    /************************************|
    |               CONSTANTS            |
    |___________________________________*/

    /// @notice overwrite chain id for EIP712 is always set to 634 for the Avocado RPC / network
    uint256 public constant DEFAULT_CHAIN_ID = 634;

    // constants for EIP712 values
    string public constant DOMAIN_SEPARATOR_NAME = "Avocado-Multisig";
    string public constant DOMAIN_SEPARATOR_VERSION = "1.1.0";
    // hashed EIP712 values
    bytes32 internal constant DOMAIN_SEPARATOR_NAME_HASHED = keccak256(bytes(DOMAIN_SEPARATOR_NAME));
    bytes32 internal constant DOMAIN_SEPARATOR_VERSION_HASHED = keccak256(bytes(DOMAIN_SEPARATOR_VERSION));

    /// @notice _TYPE_HASH is copied from OpenZeppelin EIP712 but with added salt as last param (we use it for `block.chainid`)
    bytes32 public constant TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)");

    /// @notice EIP712 typehash for `cast()` calls, including structs
    bytes32 public constant CAST_TYPE_HASH =
        keccak256(
            "Cast(CastParams params,CastForwardParams forwardParams)Action(address target,bytes data,uint256 value,uint256 operation)CastForwardParams(uint256 gas,uint256 gasPrice,uint256 validAfter,uint256 validUntil,uint256 value)CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)"
        );

    /// @notice EIP712 typehash for Action struct
    bytes32 public constant ACTION_TYPE_HASH =
        keccak256("Action(address target,bytes data,uint256 value,uint256 operation)");

    /// @notice EIP712 typehash for CastParams struct
    bytes32 public constant CAST_PARAMS_TYPE_HASH =
        keccak256(
            "CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)Action(address target,bytes data,uint256 value,uint256 operation)"
        );
    /// @notice EIP712 typehash for CastForwardParams struct
    bytes32 public constant CAST_FORWARD_PARAMS_TYPE_HASH =
        keccak256(
            "CastForwardParams(uint256 gas,uint256 gasPrice,uint256 validAfter,uint256 validUntil,uint256 value)"
        );

    /// @notice EIP712 typehash for `castAuthorized()` calls, including structs
    bytes32 public constant CAST_AUTHORIZED_TYPE_HASH =
        keccak256(
            "CastAuthorized(CastParams params,CastAuthorizedParams authorizedParams)Action(address target,bytes data,uint256 value,uint256 operation)CastAuthorizedParams(uint256 maxFee,uint256 gasPrice,uint256 validAfter,uint256 validUntil)CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)"
        );
    /// @notice EIP712 typehash for CastAuthorizedParams struct
    bytes32 public constant CAST_AUTHORIZED_PARAMS_TYPE_HASH =
        keccak256("CastAuthorizedParams(uint256 maxFee,uint256 gasPrice,uint256 validAfter,uint256 validUntil)");

    /// @notice EIP712 typehash for `castChainAgnostic()` calls, bytes32 hashes array + chainId, made up of `getSigDigest()` for
    ///         action on the respective chain.
    bytes32 public constant CAST_CHAIN_AGNOSTIC_TYPE_HASH =
        keccak256(
            "CastChainAgnostic(CastChainAgnosticParams[] params,uint256[] chainIds)Action(address target,bytes data,uint256 value,uint256 operation)CastChainAgnosticParams(CastParams params,CastForwardParams forwardParams,uint256 chainId)CastForwardParams(uint256 gas,uint256 gasPrice,uint256 validAfter,uint256 validUntil,uint256 value)CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)"
        );
    /// @notice EIP712 typehash for CastChainAgnosticParams struct
    bytes32 public constant CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH =
        keccak256(
            "CastChainAgnosticParams(CastParams params,CastForwardParams forwardParams,uint256 chainId)Action(address target,bytes data,uint256 value,uint256 operation)CastForwardParams(uint256 gas,uint256 gasPrice,uint256 validAfter,uint256 validUntil,uint256 value)CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)"
        );

    /// @notice EIP712 typehash for signed hashes used for EIP1271 (`isValidSignature()`)
    bytes32 public constant EIP1271_TYPE_HASH = keccak256("AvocadoHash(bytes32 hash)");

    /// @notice defines the max signers count for the Multisig. This is chosen deliberately very high, as there shouldn't
    /// really be a limit on signers count in practice. It is extremely unlikely that anyone runs into this very high
    /// limit but it helps to implement test coverage within this given limit
    uint256 public constant MAX_SIGNERS_COUNT = 90;

    /// @dev "magic value" according to EIP1271 https://eips.ethereum.org/EIPS/eip-1271#specification
    bytes4 internal constant EIP1271_MAGIC_VALUE = 0x1626ba7e;

    /// @dev constants for _transientAllowHash functionality: function selectors
    bytes4 internal constant _CALL_TARGETS_SELECTOR = bytes4(keccak256(bytes("_callTargets()")));
    bytes4 internal constant EXECUTE_OPERATION_SELECTOR = bytes4(keccak256(bytes("executeOpeartion()")));

    /// @dev amount of gas to keep in castAuthorized caller method as reserve for emitting event + paying fee.
    /// the dynamic part is covered with PER_SIGNER_RESERVE_GAS.
    /// use 48_500 as reserve gas for `castAuthorized()`. Usually it will cost less but 48_500 is the maximum amount
    /// with buffer (~32_000 + 1_400 base) pay fee logic etc. could cost on maximum logic execution.
    uint256 internal constant CAST_AUTHORIZED_RESERVE_GAS = 48_500;
    /// @dev amount of gas to keep in cast caller method as reserve for emitting CastFailed / CastExecuted event.
    /// ~7500 gas + ~1400 gas + buffer. the dynamic part is covered with PER_SIGNER_RESERVE_GAS and EMIT_EVENT_COST_PER_BYTE.
    uint256 internal constant CAST_EVENTS_RESERVE_GAS = 10_000;

    /// @dev emitting one byte in an event costs 8 byte see https://github.com/wolflo/evm-opcodes/blob/main/gas.md#a8-log-operations
    uint256 internal constant EMIT_EVENT_COST_PER_BYTE = 8;

    /// @dev maximum length of revert reason, longer will be truncated. necessary to reserve enugh gas for event emit
    uint256 internal constant REVERT_REASON_MAX_LENGTH = 250;

    /// @dev each additional signer costs ~358 gas to emit in the CastFailed / CastExecuted event. this amount must be
    /// factored in dynamically depending on the number of signers (PER_SIGNER_RESERVE_GAS * number of signers)
    uint256 internal constant PER_SIGNER_RESERVE_GAS = 370;

    /************************************|
    |             IMMUTABLES             |
    |___________________________________*/
    // hashed EIP712 value to reduce gas usage
    bytes32 internal immutable DOMAIN_SEPARATOR_SALT_HASHED = keccak256(abi.encodePacked(block.chainid));
    // hashed EIP712 value to reduce gas usage
    bytes32 internal immutable DOMAIN_SEPARATOR_CHAIN_AGNOSTIC_SALT_HASHED =
        keccak256(abi.encodePacked(DEFAULT_CHAIN_ID));

    /// @notice  registry holding the valid versions (addresses) for Avocado smart wallet implementation contracts
    ///          The registry is used to verify a valid version before upgrading & to pay fees for `castAuthorized()`
    IAvoRegistry public immutable avoRegistry;

    /// @notice address of the AvoForwarder (proxy) that is allowed to forward tx with valid signatures
    address public immutable avoForwarder;

    /// @notice Signers <> Avocados mapping list contract for easy on-chain tracking
    IAvoSignersList public immutable avoSignersList;

    // @dev Note that AvocadoMultisig.sol (main) also has an immutable for avoSecondary in AvocadoMultisigCore.sol

    // backup fee logic
    /// @dev minimum fee for fee charged via `castAuthorized()` to charge if `AvoRegistry.calcFee()` would fail
    uint256 public immutable AUTHORIZED_MIN_FEE;
    /// @dev global maximum for fee charged via `castAuthorized()`. If AvoRegistry returns a fee higher than this,
    /// then MAX_AUTHORIZED_FEE is charged as fee instead (capping)
    uint256 public immutable AUTHORIZED_MAX_FEE;
    /// @dev address that the fee charged via `castAuthorized()` is sent to in the fallback case
    address payable public immutable AUTHORIZED_FEE_COLLECTOR;

    /***********************************|
    |            CONSTRUCTOR            |
    |__________________________________*/

    constructor(
        IAvoRegistry avoRegistry_,
        address avoForwarder_,
        IAvoSignersList avoSignersList_,
        IAvoConfigV1 avoConfigV1_
    ) {
        if (
            address(avoRegistry_) == address(0) ||
            avoForwarder_ == address(0) ||
            address(avoSignersList_) == address(0) ||
            address(avoConfigV1_) == address(0)
        ) {
            revert AvocadoMultisig__InvalidParams();
        }

        avoRegistry = avoRegistry_;
        avoForwarder = avoForwarder_;
        avoSignersList = avoSignersList_;

        // get values from AvoConfigV1 contract
        IAvoConfigV1.AvocadoMultisigConfig memory avoConfig_ = avoConfigV1_.avocadoMultisigConfig();

        // min & max fee settings, fee collector address are required
        if (
            avoConfig_.authorizedMinFee == 0 ||
            avoConfig_.authorizedMaxFee == 0 ||
            avoConfig_.authorizedFeeCollector == address(0) ||
            avoConfig_.authorizedMinFee > avoConfig_.authorizedMaxFee
        ) {
            revert AvocadoMultisig__InvalidParams();
        }

        AUTHORIZED_MIN_FEE = avoConfig_.authorizedMinFee;
        AUTHORIZED_MAX_FEE = avoConfig_.authorizedMaxFee;
        AUTHORIZED_FEE_COLLECTOR = payable(avoConfig_.authorizedFeeCollector);
    }
}

abstract contract AvocadoMultisigVariablesSlot0 {
    /// @notice address of the smart wallet logic / implementation contract.
    //  @dev    IMPORTANT: SAME STORAGE SLOT AS FOR PROXY (`Avocado`). DO NOT MOVE THIS VARIABLE.
    //         _avoImpl MUST ALWAYS be the first declared variable here in the logic contract and in the proxy!
    //         When upgrading, the storage at memory address 0x0 is upgraded (first slot).
    //         Note immutable and constants do not take up storage slots so they can come before.
    address internal _avoImpl;

    /// @dev nonce that is incremented for every `cast` / `castAuthorized` transaction (unless it uses a non-sequential nonce)
    uint80 internal _avoNonce;

    /// @dev AvocadoMultisigInitializable.sol variables (modified from OpenZeppelin), see ./lib folder
    /// @dev Indicates that the contract has been initialized.
    uint8 internal _initialized;
    /// @dev Indicates that the contract is in the process of being initialized.
    bool internal _initializing;
}

abstract contract AvocadoMultisigVariablesSlot1 is AvocadoMultisigConstants, AvocadoMultisigEvents {
    /// @dev signers are stored with SSTORE2 to save gas, especially for storage checks at delegateCalls.
    /// getter and setter are implemented below
    address internal _signersPointer;

    /// @notice signers count required to reach quorom and be able to execute actions
    uint8 internal _requiredSigners;

    /// @notice number of signers currently listed as allowed signers
    //
    // @dev should be updated directly via `_setSigners()` in AvocadoMultisigSecondary
    uint8 internal _signersCount;

    // 10 bytes empty

    /***********************************|
    |           SIGNERS GETTERS         |
    |__________________________________*/

    /// @dev reads signers from storage with SSTORE2
    function _getSigners() internal view returns (address[] memory signers_) {
        address pointer_ = _signersPointer;
        if (pointer_ == address(0)) {
            // signers not set yet -> only owner is signer currently.
            signers_ = new address[](1);
            signers_[0] = IAvocado(address(this))._owner();
            return signers_;
        }

        return abi.decode(SSTORE2.read(pointer_), (address[]));
    }

    /// @dev reads required signers (and returns 1 if it is not set)
    function _getRequiredSigners() internal view returns (uint8 requiredSigners_) {
        requiredSigners_ = _requiredSigners;
        if (requiredSigners_ == 0) {
            requiredSigners_ = 1;
        }
    }

    /// @dev reads signers count (and returns 1 if it is not set)
    function _getSignersCount() internal view returns (uint8 signersCount_) {
        signersCount_ = _signersCount;
        if (signersCount_ == 0) {
            signersCount_ = 1;
        }
    }
}

abstract contract AvocadoMultisigVariablesSlot2 {
    /// @dev allow-listed signed messages, e.g. for Permit2 Uniswap interaction.
    /// mappings are not in sequential storage slot, thus not influenced by previous storage variables
    /// (but consider the slot number in calculating the hash of the key to store).
    mapping(bytes32 => uint256) internal _signedMessages;
}

abstract contract AvocadoMultisigVariablesSlot3 {
    /// @notice occupied non-sequential nonces (which can not be used again)
    mapping(bytes32 => uint256) public nonSequentialNonces;
}

abstract contract AvocadoMultisigSlotGaps {
    // slots 4 to 53

    // create some storage slot gaps for future expansion before the transient storage slot
    uint256[50] private __gaps;
}

abstract contract AvocadoMultisigTransient {
    // slot 54

    /// @dev transient allow hash used to signal allowing certain entry into methods such as _callTargets etc.
    bytes31 internal _transientAllowHash;
    /// @dev transient id used for passing id to flashloan callback
    uint8 internal _transientId;
}

/// @notice Defines storage variables for AvocadoMultisig
abstract contract AvocadoMultisigVariables is
    AvocadoMultisigConstants,
    AvocadoMultisigVariablesSlot0,
    AvocadoMultisigVariablesSlot1,
    AvocadoMultisigVariablesSlot2,
    AvocadoMultisigVariablesSlot3,
    AvocadoMultisigSlotGaps,
    AvocadoMultisigTransient
{
    constructor(
        IAvoRegistry avoRegistry_,
        address avoForwarder_,
        IAvoSignersList avoSignersList_,
        IAvoConfigV1 avoConfigV1_
    ) AvocadoMultisigConstants(avoRegistry_, avoForwarder_, avoSignersList_, avoConfigV1_) {}

    /// @dev resets transient storage to default value (1). 1 is better than 0 for optimizing gas refunds
    function _resetTransientStorage() internal {
        assembly {
            sstore(54, 1) // Store 1 in the transient storage 54
        }
    }
}

File 21 of 28 : AvocadoMultisigInitializable.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

import { Address } from "@openzeppelin/contracts/utils/Address.sol";

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

/// @dev contract copied from OpenZeppelin Initializable but with storage vars moved to AvocadoMultisigVariables.sol
/// from OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)
/// see https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.1/contracts/proxy/utils/Initializable.sol

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract AvocadoMultisigInitializable is AvocadoMultisigVariables {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    // uint8 private _initialized; // -> in AvocadoMultisigVariables

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    // bool private _initializing; // -> in AvocadoMultisigVariables

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

File 22 of 28 : InstaFlashReceiverInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

interface InstaFlashReceiverInterface {
    function executeOperation(
        address[] calldata assets,
        uint256[] calldata amounts,
        uint256[] calldata premiums,
        address initiator,
        bytes calldata _data
    ) external returns (bool);
}

File 23 of 28 : IAvocadoMultisigV1.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

import { AvocadoMultisigStructs } from "../AvocadoMultisig/AvocadoMultisigStructs.sol";

// @dev base interface without getters for storage variables (to avoid overloads issues)
interface IAvocadoMultisigV1Base is AvocadoMultisigStructs {
    /// @notice initializer called by AvoFactory after deployment, sets the `owner_` as the only signer
    function initialize() external;

    /// @notice returns the domainSeparator for EIP712 signature
    function domainSeparatorV4() external view returns (bytes32);

    /// @notice returns the domainSeparator for EIP712 signature for `castChainAgnostic`
    function domainSeparatorV4ChainAgnostic() external view returns (bytes32);

    /// @notice               gets the digest (hash) used to verify an EIP712 signature for `cast()`.
    ///
    ///                       This is also used as the non-sequential nonce that will be marked as used when the
    ///                       request with the matching `params_` and `forwardParams_` is executed via `cast()`.
    /// @param params_        Cast params such as id, avoNonce and actions to execute
    /// @param forwardParams_ Cast params related to validity of forwarding as instructed and signed
    /// @return               bytes32 digest to verify signature (or used as non-sequential nonce)
    function getSigDigest(
        CastParams calldata params_,
        CastForwardParams calldata forwardParams_
    ) external view returns (bytes32);

    /// @notice                   gets the digest (hash) used to verify an EIP712 signature for `castAuthorized()`.
    ///
    ///                           This is also the non-sequential nonce that will be marked as used when the request
    ///                           with the matching `params_` and `authorizedParams_` is executed via `castAuthorized()`.
    /// @param params_            Cast params such as id, avoNonce and actions to execute
    /// @param authorizedParams_  Cast params related to authorized execution such as maxFee, as signed
    /// @return                   bytes32 digest to verify signature (or used as non-sequential nonce)
    function getSigDigestAuthorized(
        CastParams calldata params_,
        CastAuthorizedParams calldata authorizedParams_
    ) external view returns (bytes32);

    /// @notice                   Verify the signatures for a `cast()' call are valid and can be executed.
    ///                           This does not guarantuee that the tx will not revert, simply that the params are valid.
    ///                           Does not revert and returns successfully if the input is valid.
    ///                           Reverts if input params, signature or avoNonce etc. are invalid.
    /// @param params_            Cast params such as id, avoNonce and actions to execute
    /// @param forwardParams_     Cast params related to validity of forwarding as instructed and signed
    /// @param signaturesParams_  SignatureParams structs array for signature and signer:
    ///                           - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA.
    ///                             For smart contract signatures it must fulfill the requirements for the relevant
    ///                             smart contract `.isValidSignature()` EIP1271 logic
    ///                           - signer: address of the signature signer.
    ///                             Must match the actual signature signer or refer to the smart contract
    ///                             that must be an allowed signer and validates signature via EIP1271
    /// @return                   returns true if everything is valid, otherwise reverts
    function verify(
        CastParams calldata params_,
        CastForwardParams calldata forwardParams_,
        SignatureParams[] calldata signaturesParams_
    ) external view returns (bool);

    /// @notice                   Verify the signatures for a `castAuthorized()' call are valid and can be executed.
    ///                           This does not guarantuee that the tx will not revert, simply that the params are valid.
    ///                           Does not revert and returns successfully if the input is valid.
    ///                           Reverts if input params, signature or avoNonce etc. are invalid.
    /// @param params_            Cast params such as id, avoNonce and actions to execute
    /// @param authorizedParams_  Cast params related to authorized execution such as maxFee, as signed
    /// @param signaturesParams_  SignatureParams structs array for signature and signer:
    ///                           - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA.
    ///                             For smart contract signatures it must fulfill the requirements for the relevant
    ///                             smart contract `.isValidSignature()` EIP1271 logic
    ///                           - signer: address of the signature signer.
    ///                             Must match the actual signature signer or refer to the smart contract
    ///                             that must be an allowed signer and validates signature via EIP1271
    /// @return                   returns true if everything is valid, otherwise reverts
    function verifyAuthorized(
        CastParams calldata params_,
        CastAuthorizedParams calldata authorizedParams_,
        SignatureParams[] calldata signaturesParams_
    ) external view returns (bool);

    /// @notice                   Executes arbitrary actions with valid signatures. Only executable by AvoForwarder.
    ///                           If one action fails, the transaction doesn't revert, instead emits the `CastFailed` event.
    ///                           In that case, all previous actions are reverted.
    ///                           On success, emits CastExecuted event.
    /// @dev                      validates EIP712 signature then executes each action via .call or .delegatecall
    /// @param params_            Cast params such as id, avoNonce and actions to execute
    /// @param forwardParams_     Cast params related to validity of forwarding as instructed and signed
    /// @param signaturesParams_  SignatureParams structs array for signature and signer:
    ///                           - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA.
    ///                             For smart contract signatures it must fulfill the requirements for the relevant
    ///                             smart contract `.isValidSignature()` EIP1271 logic
    ///                           - signer: address of the signature signer.
    ///                             Must match the actual signature signer or refer to the smart contract
    ///                             that must be an allowed signer and validates signature via EIP1271
    /// @return success           true if all actions were executed succesfully, false otherwise.
    /// @return revertReason      revert reason if one of the actions fails in the following format:
    ///                           The revert reason will be prefixed with the index of the action.
    ///                           e.g. if action 1 fails, then the reason will be "1_reason".
    ///                           if an action in the flashloan callback fails (or an otherwise nested action),
    ///                           it will be prefixed with with two numbers: "1_2_reason".
    ///                           e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails,
    ///                           the reason will be 1_2_reason.
    function cast(
        CastParams calldata params_,
        CastForwardParams calldata forwardParams_,
        SignatureParams[] calldata signaturesParams_
    ) external payable returns (bool success, string memory revertReason);

    /// @notice                   Simulates a `cast()` call with exact same params and execution logic except for:
    ///                           - any `gasleft()` use removed to remove potential problems when estimating gas.
    ///                           - reverts on param validations removed (verify validity with `verify` instead).
    ///                           - signature validation is skipped (must be manually added to gas estimations).
    /// @dev                      tx.origin must be dead address, msg.sender must be AvoForwarder.
    /// @dev                      - set `signaturesParams_` to empty to automatically simulate with required signers length.
    ///                           - if `signaturesParams_` first element signature is not set, or if first signer is set to
    ///                             0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF, then gas usage burn is simulated
    ///                             for verify signature functionality. DO NOT set signature to non-empty for subsequent
    ///                             elements then; set all signatures to empty!
    ///                           - if `signaturesParams_` is set normally, signatures are verified as in actual execute
    ///                           - buffer amounts for mock smart contract signers signature verification must be added
    ///                             off-chain as this varies on a case per case basis.
    function simulateCast(
        CastParams calldata params_,
        CastForwardParams calldata forwardParams_,
        SignatureParams[] memory signaturesParams_
    ) external payable returns (bool success_, string memory revertReason_);

    /// @notice                   Exact same as `simulateCast`, just reverts in case of `success_` = false to optimize
    ///                           for use with .estimateGas().
    function estimateCast(
        CastParams calldata params_,
        CastForwardParams calldata forwardParams_,
        SignatureParams[] memory signaturesParams_
    ) external payable returns (bool success_, string memory revertReason_);

    /// @notice                   Executes arbitrary actions through authorized transaction sent with valid signatures.
    ///                           Includes a fee in native network gas token, amount depends on registry `calcFee()`.
    ///                           If one action fails, the transaction doesn't revert, instead emits the `CastFailed` event.
    ///                           In that case, all previous actions are reverted.
    ///                           On success, emits CastExecuted event.
    /// @dev                      executes a .call or .delegateCall for every action (depending on params)
    /// @param params_            Cast params such as id, avoNonce and actions to execute
    /// @param authorizedParams_  Cast params related to authorized execution such as maxFee, as signed
    /// @param signaturesParams_  SignatureParams structs array for signature and signer:
    ///                           - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA.
    ///                             For smart contract signatures it must fulfill the requirements for the relevant
    ///                             smart contract `.isValidSignature()` EIP1271 logic
    ///                           - signer: address of the signature signer.
    ///                             Must match the actual signature signer or refer to the smart contract
    ///                             that must be an allowed signer and validates signature via EIP1271
    /// @return success           true if all actions were executed succesfully, false otherwise.
    /// @return revertReason      revert reason if one of the actions fails in the following format:
    ///                           The revert reason will be prefixed with the index of the action.
    ///                           e.g. if action 1 fails, then the reason will be "1_reason".
    ///                           if an action in the flashloan callback fails (or an otherwise nested action),
    ///                           it will be prefixed with with two numbers: "1_2_reason".
    ///                           e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails,
    ///                           the reason will be 1_2_reason.
    function castAuthorized(
        CastParams calldata params_,
        CastAuthorizedParams calldata authorizedParams_,
        SignatureParams[] calldata signaturesParams_
    ) external payable returns (bool success, string memory revertReason);

    /// @notice returns the hashes struct for each `CastChainAgnosticParams` element of `params_`. The returned array must be
    ///         passed into `castChainAgnostic()` as the param `chainAgnosticHashes_` there (order must be the same).
    ///         The returned hash for each element is the EIP712 type hash for `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH`,
    ///         as used when the signature digest is built.
    function getChainAgnosticHashes(
        CastChainAgnosticParams[] calldata params_
    ) external pure returns (ChainAgnosticHash[] memory chainAgnosticHashes_);

    /// @notice                   gets the digest (hash) used to verify an EIP712 signature for `castChainAgnostic()`,
    ///                           built from the `CastChainAgnosticParams`.
    ///
    ///                           This is also the non-sequential nonce that will be marked as used when the request
    ///                           with the matching `params_` is executed via `castChainAgnostic()`.
    /// @param params_            Cast params such as id, avoNonce and actions to execute
    /// @return                   bytes32 digest to verify signature (or used as non-sequential nonce)
    function getSigDigestChainAgnostic(CastChainAgnosticParams[] calldata params_) external view returns (bytes32);

    /// @notice                     gets the digest (hash) used to verify an EIP712 signature for `castChainAgnostic()`,
    ///                             built from the chain agnostic hashes (result of `getChainAgnosticHashes()`).
    ///
    ///                             This is also the non-sequential nonce that will be marked as used when the request
    ///                             with the matching `params_` is executed via `castChainAgnostic()`.
    /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` for all `CastChainAgnosticParams`
    ///                             struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`.
    ///                             must be set in the same order as when creating the signature.
    /// @return                     bytes32 digest to verify signature (or used as non-sequential nonce)
    function getSigDigestChainAgnosticFromHashes(
        ChainAgnosticHash[] calldata chainAgnosticHashes_
    ) external view returns (bytes32);

    /// @notice                     Executes arbitrary actions with valid signatures. Only executable by AvoForwarder.
    ///                             If one action fails, the transaction doesn't revert, instead emits the `CastFailed` event.
    ///                             In that case, all previous actions are reverted.
    ///                             On success, emits CastExecuted event.
    /// @dev                        validates EIP712 signature then executes each action via .call or .delegatecall
    /// @param params_              params containing info and intents regarding actions to be executed. Made up of
    ///                             same params as for `cast()` plus chain id.
    /// @param signaturesParams_    SignatureParams structs array for signature and signer:
    ///                             - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA.
    ///                               For smart contract signatures it must fulfill the requirements for the relevant
    ///                               smart contract `.isValidSignature()` EIP1271 logic
    ///                             - signer: address of the signature signer.
    ///                               Must match the actual signature signer or refer to the smart contract
    ///                               that must be an allowed signer and validates signature via EIP1271
    /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` for all `CastChainAgnosticParams`
    ///                             struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`.
    ///                             must be set in the same order as when creating the signature.
    /// @return success             true if all actions were executed succesfully, false otherwise.
    /// @return revertReason        revert reason if one of the actions fails in the following format:
    ///                             The revert reason will be prefixed with the index of the action.
    ///                             e.g. if action 1 fails, then the reason will be "1_reason".
    ///                             if an action in the flashloan callback fails (or an otherwise nested action),
    ///                             it will be prefixed with with two numbers: "1_2_reason".
    ///                             e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails,
    ///                             the reason will be 1_2_reason.
    function castChainAgnostic(
        CastChainAgnosticParams calldata params_,
        SignatureParams[] memory signaturesParams_,
        ChainAgnosticHash[] calldata chainAgnosticHashes_
    ) external payable returns (bool success, string memory revertReason);

    /// @notice                   Simulates a `castChainAgnostic()` call with exact same params and execution logic except for:
    ///                           - any `gasleft()` use removed to remove potential problems when estimating gas.
    ///                           - reverts on param validations removed (verify validity with `verify` instead).
    ///                           - signature validation is skipped (must be manually added to gas estimations).
    /// @dev                      tx.origin must be dead address, msg.sender must be AvoForwarder.
    /// @dev                      - set `signaturesParams_` to empty to automatically simulate with required signers length.
    ///                           - if `signaturesParams_` first element signature is not set, or if first signer is set to
    ///                             0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF, then gas usage burn is simulated
    ///                             for verify signature functionality. DO NOT set signature to non-empty for subsequent
    ///                             elements then; set all signatures to empty!
    ///                           - if `signaturesParams_` is set normally, signatures are verified as in actual execute
    ///                           - buffer amounts for mock smart contract signers signature verification must be added
    ///                             off-chain as this varies on a case per case basis.
    function simulateCastChainAgnostic(
        CastChainAgnosticParams calldata params_,
        SignatureParams[] memory signaturesParams_,
        ChainAgnosticHash[] calldata chainAgnosticHashes_
    ) external payable returns (bool success_, string memory revertReason_);

    /// @notice                   Exact same as `simulateCastChainAgnostic`, just reverts in case of `success_` = false to
    ///                           optimize for use with .estimateGas().
    function estimateCastChainAgnostic(
        CastChainAgnosticParams calldata params_,
        SignatureParams[] memory signaturesParams_,
        ChainAgnosticHash[] calldata chainAgnosticHashes_
    ) external payable returns (bool success_, string memory revertReason_);

    /// @notice                     Verify the signatures for a `castChainAgnostic()' call are valid and can be executed.
    ///                             This does not guarantuee that the tx will not revert, simply that the params are valid.
    ///                             Does not revert and returns successfully if the input is valid.
    ///                             Reverts if input params, signature or avoNonce etc. are invalid.
    /// @param params_              params containing info and intents regarding actions to be executed. Made up of
    ///                             same params as for `cast()` plus chain id.
    /// @param signaturesParams_    SignatureParams structs array for signature and signer:
    ///                             - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA.
    ///                               For smart contract signatures it must fulfill the requirements for the relevant
    ///                               smart contract `.isValidSignature()` EIP1271 logic
    ///                             - signer: address of the signature signer.
    ///                               Must match the actual signature signer or refer to the smart contract
    ///                               that must be an allowed signer and validates signature via EIP1271
    /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` for all `CastChainAgnosticParams`
    ///                             struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`.
    ///                             must be set in the same order as when creating the signature.
    /// @return                     returns true if everything is valid, otherwise reverts
    function verifyChainAgnostic(
        CastChainAgnosticParams calldata params_,
        SignatureParams[] calldata signaturesParams_,
        ChainAgnosticHash[] calldata chainAgnosticHashes_
    ) external view returns (bool);

    /// @notice checks if an address `signer_` is an allowed signer (returns true if allowed)
    function isSigner(address signer_) external view returns (bool);

    /// @notice returns allowed signers on Avocado wich can trigger actions if reaching quorum `requiredSigners`.
    ///         signers automatically include owner.
    function signers() external view returns (address[] memory signers_);

    /// @notice returns the number of required signers
    function requiredSigners() external view returns (uint8);

    /// @notice returns the number of allowed signers
    function signersCount() external view returns (uint8);

    /// @notice Avocado owner
    function owner() external view returns (address);

    /// @notice Avocado index (number of Avocado for EOA owner)
    function index() external view returns (uint32);
}

// @dev full interface with some getters for storage variables
interface IAvocadoMultisigV1 is IAvocadoMultisigV1Base {
    /// @notice Domain separator name for signatures
    function DOMAIN_SEPARATOR_NAME() external view returns (string memory);

    /// @notice Domain separator version for signatures
    function DOMAIN_SEPARATOR_VERSION() external view returns (string memory);

    /// @notice incrementing nonce for each valid tx executed (to ensure uniqueness)
    function avoNonce() external view returns (uint256);
}

File 24 of 28 : IAvocadoMultisigV1Secondary.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

import { IAvoRegistry } from "./IAvoRegistry.sol";
import { IAvoSignersList } from "./IAvoSignersList.sol";

import { AvocadoMultisigStructs } from "../AvocadoMultisig/AvocadoMultisigStructs.sol";

interface IAvocadoMultisigV1Secondary {
    /// @notice             pays the fee for `castAuthorized()` calls via the AvoRegistry (or fallback)
    /// @param gasUsedFrom_ `gasleft()` snapshot at gas measurement starting point
    /// @param maxFee_      maximum acceptable fee to be paid, revert if fee is bigger than this value
    function payAuthorizedFee(uint256 gasUsedFrom_, uint256 maxFee_) external payable;

    /// @notice decodes `signature` for EIP1271 into `signaturesParams_`
    function decodeEIP1271Signature(
        bytes calldata signature,
        address owner_
    ) external pure returns (AvocadoMultisigStructs.SignatureParams[] memory signaturesParams_);

    /// @notice Get the revert reason from the returnedData (supports Panic, Error & Custom Errors).
    /// @param returnedData_ revert data of the call
    /// @return reason_      revert reason
    function getRevertReasonFromReturnedData(bytes memory returnedData_) external pure returns (string memory reason_);

    /// @notice upgrade the contract to a new implementation address.
    ///         - Must be a valid version at the AvoRegistry.
    ///         - Can only be self-called (authorization same as for `cast` methods).
    /// @param avoImplementation_       New contract address
    /// @param afterUpgradeHookData_    flexible bytes for custom usage in after upgrade hook logic
    //
    // Implementation must call `_afterUpgradeHook()`
    function upgradeTo(address avoImplementation_, bytes calldata afterUpgradeHookData_) external;

    /// @notice                     executes a SIMULATE cast process for `cast()` or `castChainAgnostic()` for gas estimations.
    /// @param params_              Cast params such as id, avoNonce and actions to execute
    /// @param forwardParams_       Cast params related to validity of forwarding as instructed and signed
    /// @param signaturesParams_    SignatureParams structs array for signature and signer:
    ///                              - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA.
    ///                                For smart contract signatures it must fulfill the requirements for the relevant
    ///                                smart contract `.isValidSignature()` EIP1271 logic
    ///                              - signer: address of the signature signer.
    ///                                Must match the actual signature signer or refer to the smart contract
    ///                                that must be an allowed signer and validates signature via EIP1271
    /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` struct for all `CastChainAgnosticParams`
    ///                             struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`.
    ///                             must be set in the same order as when creating the signature.
    /// @return success_            true if all actions were executed succesfully, false otherwise.
    /// @return revertReason_       revert reason if one of the actions fails in the following format:
    ///                             The revert reason will be prefixed with the index of the action.
    ///                             e.g. if action 1 fails, then the reason will be "1_reason".
    ///                             if an action in the flashloan callback fails (or an otherwise nested action),
    ///                             it will be prefixed with with two numbers: "1_2_reason".
    ///                             e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails,
    ///                             the reason will be 1_2_reason.
    function simulateCast(
        AvocadoMultisigStructs.CastParams calldata params_,
        AvocadoMultisigStructs.CastForwardParams calldata forwardParams_,
        AvocadoMultisigStructs.SignatureParams[] memory signaturesParams_,
        AvocadoMultisigStructs.ChainAgnosticHash[] memory chainAgnosticHashes_
    ) external returns (bool success_, string memory revertReason_);

    /// @notice SIMULATES: executes `actions_` with respective target, calldata, operation etc.
    function _simulateExecuteActions(
        AvocadoMultisigStructs.Action[] memory actions_,
        uint256 id_,
        bool isFlashloanCallback_
    ) external;
}

interface IAvocadoMultisigV1SecondaryConstants {
    function avoRegistry() external view returns (IAvoRegistry);
    function avoForwarder() external view returns (address);
    function avoSignersList() external view returns (IAvoSignersList);
    function AUTHORIZED_MIN_FEE() external view returns (uint256);
    function AUTHORIZED_MAX_FEE() external view returns (uint256);
    function AUTHORIZED_FEE_COLLECTOR() external view returns (address);
}

File 25 of 28 : IAvoConfigV1.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

interface IAvoConfigV1 {
    struct AvocadoMultisigConfig {
        ///
        /// @param authorizedMinFee_       minimum for fee charged via `castAuthorized()` to charge if
        ///                                `AvoRegistry.calcFee()` would fail.
        uint256 authorizedMinFee;
        ///
        /// @param authorizedMaxFee_       maximum for fee charged via `castAuthorized()`. If AvoRegistry
        ///                                returns a fee higher than this, then `authorizedMaxFee_` is charged as fee instead.
        uint256 authorizedMaxFee;
        ///
        /// @param authorizedFeeCollector_ address that the fee charged via `castAuthorized()` is sent to in the fallback case.
        address authorizedFeeCollector;
    }

    struct AvoDepositManagerConfig {
        address depositToken;
    }

    struct AvoSignersListConfig {
        bool trackInStorage;
    }

    /// @notice config for AvocadoMultisig
    function avocadoMultisigConfig() external view returns (AvocadoMultisigConfig memory);

    /// @notice config for AvoDepositManager
    function avoDepositManagerConfig() external view returns (AvoDepositManagerConfig memory);

    /// @notice config for AvoSignersList
    function avoSignersListConfig() external view returns (AvoSignersListConfig memory);
}

File 26 of 28 : IAvoRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

interface IAvoFeeCollector {
    /// @notice fee config params used to determine the fee for Avocado smart wallet `castAuthorized()` calls
    struct FeeConfig {
        ///
        /// @param feeCollector address that the fee should be paid to
        address payable feeCollector;
        ///
        /// @param mode current fee mode: 0 = percentage fee (gas cost markup); 1 = static fee (better for L2)
        uint8 mode;
        ///
        /// @param fee current fee amount:
        /// - for mode percentage: fee in 1e6 percentage (1e8 = 100%, 1e6 = 1%)
        /// - for static mode: absolute amount in native gas token to charge
        ///                    (max value 30_9485_009,821345068724781055 in 1e18)
        uint88 fee;
    }

    /// @notice calculates the `feeAmount_` for an Avocado (`msg.sender`) transaction `gasUsed_` based on
    ///         fee configuration present on the contract
    /// @param  gasUsed_       amount of gas used, required if mode is percentage. not used if mode is static fee.
    /// @return feeAmount_    calculated fee amount to be paid
    /// @return feeCollector_ address to send the fee to
    function calcFee(uint256 gasUsed_) external view returns (uint256 feeAmount_, address payable feeCollector_);
}

interface IAvoRegistry is IAvoFeeCollector {
    /// @notice                      checks if an address is listed as allowed AvoForwarder version, reverts if not.
    /// @param avoForwarderVersion_  address of the AvoForwarder logic contract to check
    function requireValidAvoForwarderVersion(address avoForwarderVersion_) external view;

    /// @notice                     checks if an address is listed as allowed Avocado version, reverts if not.
    /// @param avoVersion_          address of the Avocado logic contract to check
    function requireValidAvoVersion(address avoVersion_) external view;
}

File 27 of 28 : IAvoSignersList.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;

interface IAvoSignersList {
    /// @notice adds mappings of `addSigners_` to an Avocado `avocado_`.
    ///         checks the data present at the Avocado to validate input data.
    ///
    /// If `trackInStorage` flag is set to false, then only an event will be emitted for off-chain tracking.
    /// The contract itself will not track avocados per signer on-chain!
    ///
    /// Silently ignores `addSigners_` that are already added
    ///
    /// There is expectedly no need for this method to be called by anyone other than the Avocado itself.
    function syncAddAvoSignerMappings(address avocado_, address[] calldata addSigners_) external;

    /// @notice removes mappings of `removeSigners_` from an Avocado `avocado_`.
    ///         checks the data present at the Avocado to validate input data.
    ///
    /// If `trackInStorage` flag is set to false, then only an event will be emitted for off-chain tracking.
    /// The contract itself will not track avocados per signer on-chain!
    ///
    /// Silently ignores `removeSigners_` that are already removed
    ///
    /// There is expectedly no need for this method to be called by anyone other than the Avocado itself.
    function syncRemoveAvoSignerMappings(address avocado_, address[] calldata removeSigners_) external;
}

File 28 of 28 : SSTORE2.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
library SSTORE2 {
    uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.

    /*//////////////////////////////////////////////////////////////
                               WRITE LOGIC
    //////////////////////////////////////////////////////////////*/

    function write(bytes memory data) internal returns (address pointer) {
        // Prefix the bytecode with a STOP opcode to ensure it cannot be called.
        bytes memory runtimeCode = abi.encodePacked(hex"00", data);

        bytes memory creationCode = abi.encodePacked(
            //---------------------------------------------------------------------------------------------------------------//
            // Opcode  | Opcode + Arguments  | Description  | Stack View                                                     //
            //---------------------------------------------------------------------------------------------------------------//
            // 0x60    |  0x600B             | PUSH1 11     | codeOffset                                                     //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset                                                   //
            // 0x81    |  0x81               | DUP2         | codeOffset 0 codeOffset                                        //
            // 0x38    |  0x38               | CODESIZE     | codeSize codeOffset 0 codeOffset                               //
            // 0x03    |  0x03               | SUB          | (codeSize - codeOffset) 0 codeOffset                           //
            // 0x80    |  0x80               | DUP          | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset   //
            // 0x92    |  0x92               | SWAP3        | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset)   //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
            // 0x39    |  0x39               | CODECOPY     | 0 (codeSize - codeOffset)                                      //
            // 0xf3    |  0xf3               | RETURN       |                                                                //
            //---------------------------------------------------------------------------------------------------------------//
            hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes.
            runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.
        );

        /// @solidity memory-safe-assembly
        assembly {
            // Deploy a new contract with the generated creation code.
            // We start 32 bytes into the code to avoid copying the byte length.
            pointer := create(0, add(creationCode, 32), mload(creationCode))
        }

        require(pointer != address(0), "DEPLOYMENT_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                               READ LOGIC
    //////////////////////////////////////////////////////////////*/

    function read(address pointer) internal view returns (bytes memory) {
        return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);
    }

    function read(address pointer, uint256 start) internal view returns (bytes memory) {
        start += DATA_OFFSET;

        return readBytecode(pointer, start, pointer.code.length - start);
    }

    function read(
        address pointer,
        uint256 start,
        uint256 end
    ) internal view returns (bytes memory) {
        start += DATA_OFFSET;
        end += DATA_OFFSET;

        require(pointer.code.length >= end, "OUT_OF_BOUNDS");

        return readBytecode(pointer, start, end - start);
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HELPER LOGIC
    //////////////////////////////////////////////////////////////*/

    function readBytecode(
        address pointer,
        uint256 start,
        uint256 size
    ) private view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            data := mload(0x40)

            // Update the free memory pointer to prevent overriding our data.
            // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
            // Adding 31 to size and running the result through the logic above ensures
            // the memory pointer remains word-aligned, following the Solidity convention.
            mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))

            // Store the size of the data in the first 32 byte chunk of free memory.
            mstore(data, size)

            // Copy the code into memory right after the 32 bytes we used to store the size.
            extcodecopy(pointer, add(data, 32), start, size)
        }
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "libraries": {},
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IAvoRegistry","name":"avoRegistry_","type":"address"},{"internalType":"address","name":"avoForwarder_","type":"address"},{"internalType":"contract IAvoSignersList","name":"avoSignersList_","type":"address"},{"internalType":"contract IAvoConfigV1","name":"avoConfigV1_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"AvocadoMultisig__InsufficientBalance","type":"error"},{"inputs":[],"name":"AvocadoMultisig__InsufficientGasSent","type":"error"},{"inputs":[],"name":"AvocadoMultisig__InvalidEIP1271Signature","type":"error"},{"inputs":[],"name":"AvocadoMultisig__InvalidParams","type":"error"},{"inputs":[],"name":"AvocadoMultisig__InvalidSignature","type":"error"},{"inputs":[],"name":"AvocadoMultisig__InvalidTiming","type":"error"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"maxFee","type":"uint256"}],"name":"AvocadoMultisig__MaxFee","type":"error"},{"inputs":[],"name":"AvocadoMultisig__ToHexDigit","type":"error"},{"inputs":[],"name":"AvocadoMultisig__Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"occupiedAvoNonce","type":"uint256"}],"name":"AvoNonceOccupied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"source","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address[]","name":"signers","type":"address[]"},{"indexed":false,"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"CastExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"source","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address[]","name":"signers","type":"address[]"},{"indexed":false,"internalType":"string","name":"reason","type":"string"},{"indexed":false,"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"CastFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FeePaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FeePayFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[],"name":"ListSyncFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"occupiedNonSequentialNonce","type":"bytes32"}],"name":"NonSequentialNonceOccupied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"name":"RemoveSignedMessage","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"requiredSigners","type":"uint8"}],"name":"RequiredSignersSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"name":"SignedMessage","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"signer","type":"address"}],"name":"SignerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"signer","type":"address"}],"name":"SignerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"ACTION_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"AUTHORIZED_FEE_COLLECTOR","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"AUTHORIZED_MAX_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"AUTHORIZED_MIN_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_AUTHORIZED_PARAMS_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_AUTHORIZED_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_CHAIN_AGNOSTIC_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_FORWARD_PARAMS_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_PARAMS_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_CHAIN_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR_NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP1271_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SIGNERS_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"operation","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.Action[]","name":"actions_","type":"tuple[]"},{"internalType":"uint256","name":"id_","type":"uint256"},{"internalType":"bool","name":"isFlashloanCallback_","type":"bool"}],"name":"_simulateExecuteActions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addSigners_","type":"address[]"},{"internalType":"uint8","name":"requiredSigners_","type":"uint8"}],"name":"addSigners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"avoForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"avoRegistry","outputs":[{"internalType":"contract IAvoRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"avoSignersList","outputs":[{"internalType":"contract IAvoSignersList","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"owner_","type":"address"}],"name":"decodeEIP1271Signature","outputs":[{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"signer","type":"address"}],"internalType":"struct AvocadoMultisigStructs.SignatureParams[]","name":"signaturesParams_","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"returnedData_","type":"bytes"}],"name":"getRevertReasonFromReturnedData","outputs":[{"internalType":"string","name":"reason_","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"nonSequentialNonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint88[]","name":"avoNonces_","type":"uint88[]"}],"name":"occupyAvoNonces","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"nonSequentialNonces_","type":"bytes32[]"}],"name":"occupyNonSequentialNonces","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gasUsedFrom_","type":"uint256"},{"internalType":"uint256","name":"maxFee_","type":"uint256"}],"name":"payAuthorizedFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"message_","type":"bytes32"}],"name":"removeSignedMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"removeSigners_","type":"address[]"},{"internalType":"uint8","name":"requiredSigners_","type":"uint8"}],"name":"removeSigners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"requiredSigners_","type":"uint8"}],"name":"setRequiredSigners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"message_","type":"bytes32"}],"name":"signMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"operation","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.Action[]","name":"actions","type":"tuple[]"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"int256","name":"avoNonce","type":"int256"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"source","type":"address"},{"internalType":"bytes","name":"metadata","type":"bytes"}],"internalType":"struct AvocadoMultisigStructs.CastParams","name":"params_","type":"tuple"},{"components":[{"internalType":"uint256","name":"gas","type":"uint256"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.CastForwardParams","name":"forwardParams_","type":"tuple"},{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"signer","type":"address"}],"internalType":"struct AvocadoMultisigStructs.SignatureParams[]","name":"signaturesParams_","type":"tuple[]"},{"components":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.ChainAgnosticHash[]","name":"chainAgnosticHashes_","type":"tuple[]"}],"name":"simulateCast","outputs":[{"internalType":"bool","name":"success_","type":"bool"},{"internalType":"string","name":"revertReason_","type":"string"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"avoImplementation_","type":"address"},{"internalType":"bytes","name":"afterUpgradeHookData_","type":"bytes"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"}]

466101c090815260206101a08190529081902060805261027a610200526101e0526102206040527f56de5bbd3725e9c80073ed00b9767413d9ddf7d1ec1d602a8f924f15a9c7568560a0523480156200005757600080fd5b5060405162005ff638038062005ff68339810160408190526200007a91620002f5565b8383838383838383838383836001600160a01b0384161580620000a457506001600160a01b038316155b80620000b757506001600160a01b038216155b80620000ca57506001600160a01b038116155b15620000e95760405163ec92598560e01b815260040160405180910390fd5b6001600160a01b0380851660c05283811660e0528281166101005260408051632271f8db60e01b81529051600092841691632271f8db9160048083019260609291908290030181865afa15801562000145573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200016b91906200035d565b805190915015806200017f57506020810151155b8062000196575060408101516001600160a01b0316155b80620001a6575060208101518151115b15620001c55760405163ec92598560e01b815260040160405180910390fd5b805161012052602081015161014052604001516001600160a01b03166101605250620001f99650506200020d945050505050565b5050306101805250620003d0945050505050565b600054600160f81b900460ff16156200027c5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff600160f01b90910481161015620002da576000805460ff60f01b191660ff60f01b17905560405160ff81527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6001600160a01b0381168114620002f257600080fd5b50565b600080600080608085870312156200030c57600080fd5b84516200031981620002dc565b60208601519094506200032c81620002dc565b60408601519093506200033f81620002dc565b60608601519092506200035281620002dc565b939692955090935050565b6000606082840312156200037057600080fd5b604051606081016001600160401b0381118282101715620003a157634e487b7160e01b600052604160045260246000fd5b806040525082518152602083015160208201526040830151620003c481620002dc565b60408201529392505050565b60805160a05160c05160e0516101005161012051610140516101605161018051615b6f620004876000396000612a890152600081816107420152611f4401526000818161083e01528181611ef30152611f1b0152600081816107960152611f6701526000818161026d0152818161188801528181611ca10152612532015260006105790152600081816105c0015281816119ff0152611dc501526000612de501526000818161212c0152612cc90152615b6f6000f3fe6080604052600436106102345760003560e01c806387265c9511610138578063b4907ddc116100b0578063ec5904831161007f578063ed969f0a11610064578063ed969f0a146107d8578063f7b14fac1461080c578063fe6bfed31461082c57600080fd5b8063ec59048314610784578063ec80dcc2146107b857600080fd5b8063b4907ddc146106b3578063bb1c006b146106fc578063c7fe944f14610730578063eb2856e91461076457600080fd5b80639ee3f88311610107578063a0d06193116100ec578063a0d0619314610617578063a755ecfc1461064b578063a80ebaf11461067f57600080fd5b80639ee3f883146105e2578063a08519b5146105f757600080fd5b806387265c951461051e57806394f0320e14610567578063984cee411461059b5780639cae7aa8146105ae57600080fd5b806343ea1996116101cb5780635b502b401161019a57806364d4c8191161017f57806364d4c819146104b55780636fbc15e9146104e95780638129fc1c1461050957600080fd5b80635b502b40146104615780636043848a1461049557600080fd5b806343ea1996146103c3578063447f537f146103d957806349926a661461040d5780634cf5de191461042d57600080fd5b80632b537875116102075780632b537875146103145780633a06cc8c146103415780633e0cd897146103755780633edd9187146103a357600080fd5b8063044d3cd21461023957806316914ac01461025b5780631bbce23d146102ac5780632a5d1d66146102d9575b600080fd5b34801561024557600080fd5b506102596102543660046145d3565b610860565b005b34801561026757600080fd5b5061028f7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102b857600080fd5b506102cc6102c736600461467c565b6109f7565b6040516102a39190614723565b3480156102e557600080fd5b506103066102f43660046147c0565b60036020526000908152604090205481565b6040519081526020016102a3565b34801561032057600080fd5b5061033461032f366004614926565b610d24565b6040516102a3919061495b565b34801561034d57600080fd5b506103067f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e81565b34801561038157600080fd5b50610395610390366004614a6c565b611158565b6040516102a3929190614b97565b3480156103af57600080fd5b506102596103be3660046145d3565b6114dd565b3480156103cf57600080fd5b5061030661027a81565b3480156103e557600080fd5b506103067f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2081565b34801561041957600080fd5b506102596104283660046147c0565b611575565b34801561043957600080fd5b506103067fc84a2c176321157bd55b70feb5871c4304d99c870656d2fc420998eff645e20781565b34801561046d57600080fd5b506103067f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c081565b3480156104a157600080fd5b506102596104b0366004614bc3565b6115b8565b3480156104c157600080fd5b506103067fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647281565b3480156104f557600080fd5b50610259610504366004614c17565b6119ab565b34801561051557600080fd5b50610259611b38565b34801561052a57600080fd5b506103346040518060400160405280601081526020017f41766f6361646f2d4d756c74697369670000000000000000000000000000000081525081565b34801561057357600080fd5b5061028f7f000000000000000000000000000000000000000000000000000000000000000081565b6102596105a9366004614c6c565b611dae565b3480156105ba57600080fd5b5061028f7f000000000000000000000000000000000000000000000000000000000000000081565b3480156105ee57600080fd5b50610306605a81565b34801561060357600080fd5b506102596106123660046147c0565b61211c565b34801561062357600080fd5b506103067fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b81565b34801561065757600080fd5b506103067fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4781565b34801561068b57600080fd5b506103067f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6581565b3480156106bf57600080fd5b506103346040518060400160405280600581526020017f312e312e3000000000000000000000000000000000000000000000000000000081525081565b34801561070857600080fd5b506103067fb7c77dbcd01eff35f637803daf7abe3f0e3b86b5102459be5665dc7d9a85ac5e81565b34801561073c57600080fd5b5061028f7f000000000000000000000000000000000000000000000000000000000000000081565b34801561077057600080fd5b5061025961077f366004614bc3565b61222b565b34801561079057600080fd5b506103067f000000000000000000000000000000000000000000000000000000000000000081565b3480156107c457600080fd5b506102596107d3366004614c8e565b612654565b3480156107e457600080fd5b506103067f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb81565b34801561081857600080fd5b50610259610827366004614d8b565b612668565b34801561083857600080fd5b506103067f000000000000000000000000000000000000000000000000000000000000000081565b610868612a86565b80600081900361087757505050565b60058111156108995760405163ec92598560e01b815260040160405180910390fd5b6000805474010000000000000000000000000000000000000000900469ffffffffffffffffffff16905b8281101561099e57818585838181106108de576108de614de0565b90506020020160208101906108f39190614e0f565b6affffffffffffffffffffff16036109425760405182907f5e8592bbef3f468eddd77c711ee57100f76bbb79500d8871a700fb6108929d9290600090a28161093a81614e6b565b925050610996565b8185858381811061095557610955614de0565b905060200201602081019061096a9190614e0f565b6affffffffffffffffffffff1611156109965760405163ec92598560e01b815260040160405180910390fd5b6001016108c3565b506000805469ffffffffffffffffffff90921674010000000000000000000000000000000000000000027fffff00000000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055505050565b6060827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf8101610ad05760408051600180825281830190925290816020015b604080518082019091526060815260006020820152815260200190600190039081610a36579050506040805160606020601f890181900402820181018352918101878152929450918291889088908190850183828082843760009201829052509385525050506001600160a01b0386166020909201919091528351849190610ac057610ac0614de0565b6020026020010181905250610d1b565b605a811015610b0b576040517fa8398eb900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fdec0de6520000000000000000000000000000000000000000000000000000000610b3a600560008789614e85565b610b4391614eaf565b7fffffffffffffffff0000000000000000000000000000000000000000000000001603610d0c5760557ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8201048067ffffffffffffffff811115610ba957610ba96147d9565b604051908082528060200260200182016040528015610bef57816020015b604080518082019091526060815260006020820152815260200190600190039081610bc75790505b50925060005b81811015610d0557605581026046810190600090610c1890605a01838a8c614e85565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050505060208101516040805180820190915291925060601c9080610c91857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf81018d8f614e85565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506001600160a01b0383166020909101528751889086908110610cec57610cec614de0565b6020026020010181905250836001019350505050610bf5565b5050610d1b565b610d1884860186614f26565b91505b505b9392505050565b6060600482511015610d6957505060408051808201909152601381527f5f524541534f4e5f4e4f545f444546494e454400000000000000000000000000602082015290565b60208201517fb1b7848f000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821601610df557600483019250610dcf83610dca90614f5b565b612aea565b604051602001610ddf9190614f7f565b6040516020818303038152906040529150611078565b7ff73c8660000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821601610e685760048301925082806020019051810190610e589190614ff4565b604051602001610ddf919061503d565b610e7560e082901c612aea565b604051602001610e859190615082565b6040516020818303038152906040529150600060048451610ea691906150c7565b9050610eb460fa60086150da565b610ebf8260026150ed565b1115610ed457610ed160fa60046150da565b90505b6000610ee18260026150ed565b67ffffffffffffffff811115610ef957610ef96147d9565b6040519080825280601f01601f191660200182016040528015610f23576020820181803683370190505b50905060005b8281101561102e57610f666010878360040181518110610f4b57610f4b614de0565b016020015160f81c81610f6057610f60614ef7565b04612b07565b828260020281518110610f7b57610f7b614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610fdf6010878360040181518110610fc457610fc4614de0565b016020015160f81c81610fd957610fd9614ef7565b06612b07565b828260020260010181518110610ff757610ff7614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101610f29565b50836040516020016110409190615104565b60405160208183030381529060405293508381604051602001611064929190615145565b604051602081830303815290604052935050505b60fa825111156111525781600061109160fa60016150da565b905060008167ffffffffffffffff8111156110ae576110ae6147d9565b6040519080825280601f01601f1916602001820160405280156110d8576020820181803683370190505b50905060005b8281101561114c578381815181106110f8576110f8614de0565b602001015160f81c60f81b82828151811061111557611115614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506001016110de565b50935050505b50919050565b60006060611164612a86565b61118c6111718780615174565b90508760400135876040013588606001358960800135612b67565b8251156111bd576111bd6111b76111a2886151dc565b6111b136899003890189615278565b46612bd9565b84612c4e565b6000808451116111ec576111e76111d3886151dc565b6111e236899003890189615278565b612cbf565b6111f5565b6111f584612d3f565b905060008086511161121157611209612e62565b60ff16611214565b85515b67ffffffffffffffff81111561122c5761122c6147d9565b604051908082528060200260200182016040528015611255578160200160208202803683370190505b50905085516000148061128757508560008151811061127657611276614de0565b602002602001015160000151516000145b806112c15750856000815181106112a0576112a0614de0565b6020026020010151602001516001600160a01b03166001600160a01b038016145b1561139f5760006112d0612e90565b60ff1690506000816001146112e757612af86112eb565b6121345b61ffff16905060018351111561132d57825161130990611af46150ed565b6113148360a06150ed565b611320906119006150da565b61132a91906150da565b90505b60408a01351961134657611343610898826150da565b90505b875160000361136a57825161135d90610ce46150ed565b61136790826150da565b90505b60005a905060005b825a61137e90846150c7565b1015611396578061138e81614e6b565b915050611372565b505050506113b5565b6113b282878a6040013560001914612ebe565b90505b6113ff886113e561271084518c8060a001906113d191906152e8565b600802610172929092029290920101919050565b8a60400135600019146113f9576000612f08565b84612f08565b9094509250831561146f573361141b60a08a0160808b0161534d565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b8361145360a08d018d6152e8565b604051611462939291906153d9565b60405180910390a36114d2565b3361148060a08a0160808b0161534d565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f83866114b960a08e018e6152e8565b6040516114c994939291906153ff565b60405180910390a35b505094509492505050565b6114e5612a86565b8060005b8181101561156f5760016003600086868581811061150957611509614de0565b9050602002013581526020019081526020016000208190555083838281811061153457611534614de0565b905060200201357f3749b30151183a20a5e6b93dce343f6010bde0e057441cca9548231f5727b70360405160405180910390a26001016114e9565b50505050565b61157d612a86565b6000818152600260205260408082208290555182917fc93ef95774a42ae5f9ca1e82cc762d17a6c2424041b91bf34dcf23aacc28cf1291a250565b6115c0612a86565b8160008190036115e35760405163ec92598560e01b815260040160405180910390fd5b60006115ed61317e565b805190915060006115fe84836150c7565b90506000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611640573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116649190615444565b905060008267ffffffffffffffff811115611681576116816147d9565b6040519080825280602002602001820160405280156116aa578160200160208202803683370190505b50905060008060005b868110156118595781810392508882148061171f57508b8b838181106116db576116db614de0565b90506020020160208101906116f0919061534d565b6001600160a01b031688828151811061170b5761170b614de0565b60200260200101516001600160a01b031614155b15611796578583101561177d5787818151811061173e5761173e614de0565b602002602001015184848151811061175857611758614de0565b60200260200101906001600160a01b031690816001600160a01b031681525050611851565b60405163ec92598560e01b815260040160405180910390fd5b846001600160a01b03168c8c848181106117b2576117b2614de0565b90506020020160208101906117c7919061534d565b6001600160a01b0316036117ee5760405163ec92598560e01b815260040160405180910390fd5b8b8b8381811061180057611800614de0565b9050602002016020810190611815919061534d565b6001600160a01b03167f3525e22824a8a7df2c9a6029941c824cf95b6447f1e13d5128fd3826d35afe8b60405160405180910390a28160010191505b6001016116b3565b5087811461187a5760405163ec92598560e01b815260040160405180910390fd5b611884838a61326d565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316308d8d6040516024016118c593929190615461565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa1153e7f000000000000000000000000000000000000000000000000000000001790525161192891906154bd565b6000604051808303816000865af19150503d8060008114611965576040519150601f19603f3d011682016040523d82523d6000602084013e61196a565b606091505b505090508061199d576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b505050505050505050505050565b6119b3612a86565b6000546001600160a01b03848116911614611b33576040517efe90e30000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301527f0000000000000000000000000000000000000000000000000000000000000000169062fe90e39060240160006040518083038186803b158015611a4057600080fd5b505afa158015611a54573d6000803e3d6000fd5b5050600080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b0388811691821784556040519216945092507fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b9190a2611b3084828585604051602401611ad3939291906154d9565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fe7e38cd3000000000000000000000000000000000000000000000000000000001790526133d1565b50505b505050565b611b40612a86565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba49190615444565b90506001600160a01b0381163b151580611bc557506001600160a01b038116155b15611be35760405163ec92598560e01b815260040160405180910390fd5b611bed6001603655565b6040516001600160a01b038216907f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2490600090a26040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a2604080516001808252818301909252600091602080830190803683370190505090508181600081518110611c8357611c83614de0565b6001600160a01b0392831660209182029290920101526040516000917f00000000000000000000000000000000000000000000000000000000000000001690611cd290309085906024016154fc565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f863deb110000000000000000000000000000000000000000000000000000000017905251611d3591906154bd565b6000604051808303816000865af19150503d8060008114611d72576040519150601f19603f3d011682016040523d82523d6000602084013e611d77565b606091505b5050905080611b33576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a1505050565b611db6612a86565b60008060005a850390506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316613a9884604051602401611e0391815260200190565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f75dc7d8c0000000000000000000000000000000000000000000000000000000017905251611e6691906154bd565b6000604051808303818686fa925050503d8060008114611ea2576040519150601f19603f3d011682016040523d82523d6000602084013e611ea7565b606091505b5060408101519193509150828015611ec05750603f8251115b8015611ed357506001600160a01b038111155b15611f425781806020019051810190611eec919061551e565b90965094507f0000000000000000000000000000000000000000000000000000000000000000861115611f3d577f000000000000000000000000000000000000000000000000000000000000000095505b611f89565b7f000000000000000000000000000000000000000000000000000000000000000094507f000000000000000000000000000000000000000000000000000000000000000095505b5050505060008211156120eb57600083118015611fa557508282115b15611feb576040517f360ceed500000000000000000000000000000000000000000000000000000000815260048101839052602481018490526044015b60405180910390fd5b81471015612028576040517ffae5708700000000000000000000000000000000000000000000000000000000815260048101839052602401611fe2565b6000816001600160a01b0316836103e890604051600060405180830381858888f193505050503d806000811461207a576040519150601f19603f3d011682016040523d82523d6000602084013e61207f565b606091505b5050905080156120b95760405183907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a26120e5565b60405183907fd1b3877a34a9595e45447dad16b7354ec5a8bece40075fd60d647e76397b476490600090a25b5061156f565b60405182907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a250505050565b612124612a86565b6121eb6121507f00000000000000000000000000000000000000000000000000000000000000006133f6565b604080517f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2060208201529081018490526060015b60408051601f1981840301815282825280516020918201207f19010000000000000000000000000000000000000000000000000000000000008483015260228401949094526042808401949094528151808403909401845260629092019052815191012090565b600081815260026020526040808220600190555191925082917fbef281d0d1046788f9baa37e73d57e85933f130d8ec1e223366e0b04fbf254029190a250565b612233612a86565b8180158061226e575060008484828161224e5761224e614de0565b9050602002016020810190612263919061534d565b6001600160a01b0316145b1561228c5760405163ec92598560e01b815260040160405180910390fd5b600061229661317e565b805190915060006122a784836150da565b9050605a8111156122cb5760405163ec92598560e01b815260040160405180910390fd5b60008167ffffffffffffffff8111156122e6576122e66147d9565b60405190808252806020026020018201604052801561230f578160200160208202803683370190505b50905060008060005b848110156125235781810392508782148061238d5750858310801561238d57508a8a8381811061234a5761234a614de0565b905060200201602081019061235f919061534d565b6001600160a01b031687848151811061237a5761237a614de0565b60200260200101516001600160a01b0316105b156123e3578683815181106123a4576123a4614de0565b60200260200101518482815181106123be576123be614de0565b60200260200101906001600160a01b031690816001600160a01b03168152505061249f565b8a8a838181106123f5576123f5614de0565b905060200201602081019061240a919061534d565b84828151811061241c5761241c614de0565b60200260200101906001600160a01b031690816001600160a01b0316815250508a8a8381811061244e5761244e614de0565b9050602002016020810190612463919061534d565b6001600160a01b03167f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2460405160405180910390a28160010191505b6000811180156124fd5750836124b66001836150c7565b815181106124c6576124c6614de0565b60200260200101516001600160a01b03168482815181106124e9576124e9614de0565b60200260200101516001600160a01b031611155b1561251b5760405163ec92598560e01b815260040160405180910390fd5b600101612318565b5061252e838961326d565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316308c8c60405160240161256f93929190615461565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f863deb1100000000000000000000000000000000000000000000000000000000179052516125d291906154bd565b6000604051808303816000865af19150503d806000811461260f576040519150601f19603f3d011682016040523d82523d6000602084013e612614565b606091505b5050905080612647576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b5050505050505050505050565b61265c612a86565b6126658161350b565b50565b612670612a86565b61267a6001603655565b6000806000846001148061268e5750846015145b905080156126a157600054925060015491505b855160005b81811015612a7c5760008882815181106126c2576126c2614de0565b60200260200101519050600060606000836060015160001480156126fb575060028b10806126f057508a6014145b806126fb57508a6015145b156127825783600001516001600160a01b03168460400151856020015160405161272591906154bd565b60006040518083038185875af1925050503d8060008114612762576040519150601f19603f3d011682016040523d82523d6000602084013e612767565b606091505b5090935091508261277d5761277d8186846135e2565b612a6d565b836060015160011480156127935750865b156128905783600001516001600160a01b031684602001516040516127b891906154bd565b600060405180830381855af49150503d80600081146127f3576040519150601f19603f3d011682016040523d82523d6000602084013e6127f8565b606091505b5090935091508261280e5761280e8186846135e2565b6128186001603655565b6000546001548a8214801561282c57508981145b6128895761283987613605565b604051602001612849919061554e565b60408051601f19818403018152908290527f08c379a0000000000000000000000000000000000000000000000000000000008252611fe29160040161495b565b5050612a6d565b836060015160021480156128ae57508a601414806128ae57508a6015145b15612a545789156128d2576128c285613605565b604051602001612849919061558f565b602084015160048101805190916128f191810160249081019101615654565b50604080518082018252601281527f657865637574654f7065617274696f6e28290000000000000000000000000000602091820152905191955061295e94508593504292507f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e9101615760565b60405160208183030381529060405280519060200120603660006101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff021916908360081c02179055508b6036601f6101000a81548160ff021916908360ff16021790555084600001516001600160a01b0316856040015186602001516040516129ec91906154bd565b60006040518083038185875af1925050503d8060008114612a29576040519150601f19603f3d011682016040523d82523d6000602084013e612a2e565b606091505b50909450925083612a4457612a448287856135e2565b612a4e6001603655565b50612a6d565b612a5d85613605565b60405160200161284991906157ab565b846001019450505050506126a6565b5050505050505050565b307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031603612ae8576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6060612b0182612af9846136a5565b60010161370f565b92915050565b6000600a8260ff161015612b1f575060300160f81b90565b60108260ff161015612b35575060570160f81b90565b6040517f9778fcab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085118015612ba75750836000191480612ba7575060005474010000000000000000000000000000000000000000900469ffffffffffffffffffff1684145b5042831180612bc05750600082118015612bc057504282105b50600081118015612bd15750803414155b505050505050565b60007fc84a2c176321157bd55b70feb5871c4304d99c870656d2fc420998eff645e207612c0585613952565b612c0e85613b44565b60408051602081019490945283019190915260608201526080810183905260a0016040516020818303038152906040528051906020012090509392505050565b80516001811115611b335760005b8181101561156f57828181518110612c7657612c76614de0565b60200260200101516000015184148015612cac5750828181518110612c9d57612c9d614de0565b60200260200101516020015146145b15612cb75750505050565b600101612c5c565b6000610d1d612ced7f00000000000000000000000000000000000000000000000000000000000000006133f6565b7fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b612d1786613952565b612d2086613b44565b6040805160208101949094528301919091526060820152608001612184565b8051600090606080835b83811015612ddc5782868281518110612d6457612d64614de0565b602002602001015160000151604051602001612d819291906157ec565b604051602081830303815290604052925081868281518110612da557612da5614de0565b602002602001015160200151604051602001612dc29291906157ec565b60408051601f198184030181529190529150600101612d49565b50612e59612e097f00000000000000000000000000000000000000000000000000000000000000006133f6565b8351602080860191909120845185830120604080517fb7c77dbcd01eff35f637803daf7abe3f0e3b86b5102459be5665dc7d9a85ac5e948101949094528301919091526060820152608001612184565b95945050505050565b60015474010000000000000000000000000000000000000000900460ff166000819003612e8d575060015b90565b6001547501000000000000000000000000000000000000000000900460ff166000819003612e8d5750600190565b60606000612ecd858585613bb8565b9250905080610d1b576040517f70a1717900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006060612f168580615174565b604080518082018252600e81527f5f63616c6c5461726765747328290000000000000000000000000000000000006020918201529051612f81939291808a01359142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b910161592b565b60408051808303601f190181529190528051602090910120603680547fff000000000000000000000000000000000000000000000000000000000000001660089290921c91909117905582613034576000805474010000000000000000000000000000000000000000900469ffffffffffffffffffff169060146130048361597e565b91906101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff16021790555050613047565b6000838152600360205260409020600190555b60006130538680615174565b876020013560405160240161306a939291906159a7565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb92e87fa0000000000000000000000000000000000000000000000000000000017815281516000805493945060609390928392916001600160a01b03165af493503d6040519150601f19601f6020830101168201604052808252806000602084013e508361316a57805160000361314d576040518060400160405280601781526020017f41564f5f5f524541534f4e5f4e4f545f444546494e4544000000000000000000815250925061316a565b600481019050808060200190518101906131679190614ff4565b92505b6131746001603655565b5050935093915050565b6001546060906001600160a01b03168061324b576040805160018082528183019092529060208083019080368337019050509150306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156131f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132149190615444565b8260008151811061322757613227614de0565b60200260200101906001600160a01b031690816001600160a01b0316815250505090565b61325481613f6e565b80602001905181019061326791906159cb565b91505090565b8151605a81118061327c575080155b1561329a5760405163ec92598560e01b815260040160405180910390fd5b80600103613322578160ff166001146132c65760405163ec92598560e01b815260040160405180910390fd5b6001805474010000000000000000000000000000000000000000900460ff161115613318576040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a25b6000600155505050565b600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000060ff8416021790556040516133909061337c908590602001615a00565b604051602081830303815290604052613f8f565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055611b338261350b565b6060610d1d8383604051806060016040528060278152602001615b136027913961405a565b604080518082018252601081527f41766f6361646f2d4d756c74697369670000000000000000000000000000000060209182015281518083018352600581527f312e312e300000000000000000000000000000000000000000000000000000009082015281517fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472918101919091527ff5d974efd8e94277ef83fff53580c07a80e6e3ffdb2e3fec63185a4e812655a2918101919091527f6815ba53416ba06aff1932cc76b3832272bafab9bc8e066be382e32b06ba5546606082015261027a60808201523060a082015260c0810182905260009060e0015b604051602081830303815290604052805190602001209050919050565b60ff81161580613527575061351e612e90565b60ff168160ff16115b156135455760405163ec92598560e01b815260040160405180910390fd5b60015460ff82811674010000000000000000000000000000000000000000909204161461266557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000060ff8416908102919091179091556040517f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a250565b6135eb82613605565b6135f482610d24565b604051602001612849929190615145565b60606000613612836140d2565b600101905060008167ffffffffffffffff811115613632576136326147d9565b6040519080825280601f01601f19166020018201604052801561365c576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461366657509392505050565b600080608083901c156136bd5760809290921c916010015b604083901c156136d25760409290921c916008015b602083901c156136e75760209290921c916004015b601083901c156136fc5760109290921c916002015b600883901c15612b015760010192915050565b6060600061371e8360026150ed565b6137299060026150da565b67ffffffffffffffff811115613741576137416147d9565b6040519080825280601f01601f19166020018201604052801561376b576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000816000815181106137a2576137a2614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f78000000000000000000000000000000000000000000000000000000000000008160018151811061380557613805614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006138418460026150ed565b61384c9060016150da565b90505b60018111156138e9577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061388d5761388d614de0565b1a60f81b8282815181106138a3576138a3614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c936138e281615a13565b905061384f565b508315610d1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401611fe2565b8051516000906060825b82811015613a9157817f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f658660000151838151811061399c5761399c614de0565b602002602001015160000151876000015184815181106139be576139be614de0565b60200260200101516020015180519060200120886000015185815181106139e7576139e7614de0565b60200260200101516040015189600001518681518110613a0957613a09614de0565b602002602001015160600151604051602001613a509594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120604051602001613a779291906157ec565b60408051601f19818403018152919052915060010161395c565b507fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc47818051906020012085602001518660400151876060015188608001518960a0015180519060200120604051602001613b25979695949392919096875260208701959095526040860193909352606085019190915260808401526001600160a01b031660a083015260c082015260e00190565b6040516020818303038152906040528051906020012092505050919050565b60007f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e826000015183602001518460400151856060015186608001516040516020016134ee96959493929190958652602086019490945260408501929092526060840152608083015260a082015260c00190565b8151600090606090613bc8612e62565b60ff16811080613bee5750838015613bee57506000868152600360205260409020546001145b15613c0c5760405163ec92598560e01b815260040160405180910390fd5b8067ffffffffffffffff811115613c2557613c256147d9565b604051908082528060200260200182016040528015613c4e578160200160208202803683370190505b5091506000613c5b61317e565b805190915060008080805b86811015613f5a57613c9e8b8281518110613c8357613c83614de0565b6020026020010151602001516001600160a01b03163b151590565b15613cfc578a8181518110613cb557613cb5614de0565b602002602001015160200151888281518110613cd357613cd3614de0565b60200260200101906001600160a01b031690816001600160a01b03168152505060019250613dbd565b613d238c8c8381518110613d1257613d12614de0565b6020026020010151600001516141b4565b888281518110613d3557613d35614de0565b60200260200101906001600160a01b031690816001600160a01b031681525050878181518110613d6757613d67614de0565b60200260200101516001600160a01b03168b8281518110613d8a57613d8a614de0565b6020026020010151602001516001600160a01b031614613dbd5760405163ec92598560e01b815260040160405180910390fd5b835b85811015613e2957888281518110613dd957613dd9614de0565b60200260200101516001600160a01b0316878281518110613dfc57613dfc614de0565b60200260200101516001600160a01b031603613e215760019250806001019450613e29565b600101613dbf565b5081613e3f576000985050505050505050613f66565b600091508215613f52578a517f1626ba7e00000000000000000000000000000000000000000000000000000000908c9083908110613e7f57613e7f614de0565b6020026020010151602001516001600160a01b0316631626ba7e8e8e8581518110613eac57613eac614de0565b6020026020010151600001516040518363ffffffff1660e01b8152600401613ed5929190615a2a565b602060405180830381865afa158015613ef2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f169190615a43565b7fffffffff000000000000000000000000000000000000000000000000000000001614613f4d576000985050505050505050613f66565b600092505b600101613c66565b50600197505050505050505b935093915050565b6060612b01826001613f8a816001600160a01b0384163b6150c7565b6141d0565b60008082604051602001613fa39190615a85565b6040516020818303038152906040529050600081604051602001613fc79190615a9e565b60405160208183030381529060405290508051602082016000f092506001600160a01b038316614053576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4445504c4f594d454e545f4641494c45440000000000000000000000000000006044820152606401611fe2565b5050919050565b6060600080856001600160a01b03168560405161407791906154bd565b600060405180830381855af49150503d80600081146140b2576040519150601f19603f3d011682016040523d82523d6000602084013e6140b7565b606091505b50915091506140c8868383876141f3565b9695505050505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061411b577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310614147576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061416557662386f26fc10000830492506010015b6305f5e100831061417d576305f5e100830492506008015b612710831061419157612710830492506004015b606483106141a3576064830492506002015b600a8310612b015760010192915050565b60008060006141c3858561428e565b91509150610d1b816142d3565b60408051603f8301601f19168101909152818152818360208301863c9392505050565b6060831561427c578251600003614275576001600160a01b0385163b614275576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611fe2565b5081614286565b6142868383614486565b949350505050565b60008082516041036142c45760208301516040840151606085015160001a6142b8878285856144ca565b945094505050506142cc565b506000905060025b9250929050565b60008160048111156142e7576142e7615ae3565b036142ef5750565b600181600481111561430357614303615ae3565b0361436a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611fe2565b600281600481111561437e5761437e615ae3565b036143e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611fe2565b60038160048111156143f9576143f9615ae3565b03612665576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401611fe2565b8151156144965781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fe2919061495b565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156145015750600090506003614585565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614555573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661457e57600060019250925050614585565b9150600090505b94509492505050565b60008083601f8401126145a057600080fd5b50813567ffffffffffffffff8111156145b857600080fd5b6020830191508360208260051b85010111156142cc57600080fd5b600080602083850312156145e657600080fd5b823567ffffffffffffffff8111156145fd57600080fd5b6146098582860161458e565b90969095509350505050565b60008083601f84011261462757600080fd5b50813567ffffffffffffffff81111561463f57600080fd5b6020830191508360208285010111156142cc57600080fd5b6001600160a01b038116811461266557600080fd5b803561467781614657565b919050565b60008060006040848603121561469157600080fd5b833567ffffffffffffffff8111156146a857600080fd5b6146b486828701614615565b90945092505060208401356146c881614657565b809150509250925092565b60005b838110156146ee5781810151838201526020016146d6565b50506000910152565b6000815180845261470f8160208601602086016146d3565b601f01601f19169290920160200192915050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b838110156147b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08984030185528151805187855261478c888601826146f7565b918901516001600160a01b0316948901949094529487019492509086019060010161474a565b509098975050505050505050565b6000602082840312156147d257600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561482b5761482b6147d9565b60405290565b6040516080810167ffffffffffffffff8111828210171561482b5761482b6147d9565b60405160c0810167ffffffffffffffff8111828210171561482b5761482b6147d9565b604051601f8201601f1916810167ffffffffffffffff811182821017156148a0576148a06147d9565b604052919050565b600067ffffffffffffffff8211156148c2576148c26147d9565b50601f01601f191660200190565b600082601f8301126148e157600080fd5b81356148f46148ef826148a8565b614877565b81815284602083860101111561490957600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561493857600080fd5b813567ffffffffffffffff81111561494f57600080fd5b614286848285016148d0565b602081526000610d1d60208301846146f7565b600067ffffffffffffffff821115614988576149886147d9565b5060051b60200190565b600082601f8301126149a357600080fd5b813560206149b36148ef8361496e565b82815260059290921b840181019181810190868411156149d257600080fd5b8286015b84811015614a6157803567ffffffffffffffff808211156149f75760008081fd5b8189019150604080601f19848d03011215614a125760008081fd5b614a1a614808565b8784013583811115614a2c5760008081fd5b614a3a8d8a838801016148d0565b8252509281013592614a4b84614657565b80880193909352505083529183019183016149d6565b509695505050505050565b600080600080848603610100811215614a8457600080fd5b853567ffffffffffffffff80821115614a9c57600080fd5b9087019060c0828a031215614ab057600080fd5b90955060209060a0601f1984011215614ac857600080fd5b818801955060c0880135925080831115614ae157600080fd5b614aed89848a01614992565b945060e0880135925080831115614b0357600080fd5b50818701915087601f830112614b1857600080fd5b8135614b266148ef8261496e565b81815260069190911b8301820190828101908a831115614b4557600080fd5b938301935b82851015614b88576040858c031215614b635760008081fd5b614b6b614808565b853581528486013585820152825260409094019390830190614b4a565b979a9699509497505050505050565b821515815260406020820152600061428660408301846146f7565b803560ff8116811461467757600080fd5b600080600060408486031215614bd857600080fd5b833567ffffffffffffffff811115614bef57600080fd5b614bfb8682870161458e565b9094509250614c0e905060208501614bb2565b90509250925092565b600080600060408486031215614c2c57600080fd5b8335614c3781614657565b9250602084013567ffffffffffffffff811115614c5357600080fd5b614c5f86828701614615565b9497909650939450505050565b60008060408385031215614c7f57600080fd5b50508035926020909101359150565b600060208284031215614ca057600080fd5b610d1d82614bb2565b600082601f830112614cba57600080fd5b81356020614cca6148ef8361496e565b82815260059290921b84018101918181019086841115614ce957600080fd5b8286015b84811015614a6157803567ffffffffffffffff80821115614d0e5760008081fd5b8189019150608080601f19848d03011215614d295760008081fd5b614d31614831565b87840135614d3e81614657565b815260408481013584811115614d545760008081fd5b614d628e8b838901016148d0565b838b01525060608581013591830191909152919093013590830152508352918301918301614ced565b600080600060608486031215614da057600080fd5b833567ffffffffffffffff811115614db757600080fd5b614dc386828701614ca9565b93505060208401359150604084013580151581146146c857600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215614e2157600080fd5b81356affffffffffffffffffffff81168114610d1d57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006000198203614e7e57614e7e614e3c565b5060010190565b60008085851115614e9557600080fd5b83861115614ea257600080fd5b5050820193919092039150565b7fffffffffffffffff0000000000000000000000000000000000000000000000008135818116916008851015614eef5780818660080360031b1b83161692505b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060208284031215614f3857600080fd5b813567ffffffffffffffff811115614f4f57600080fd5b61428684828501614992565b805160208083015191908110156111525760001960209190910360031b1b16919050565b7f5f5441524745545f50414e49434b45443a200000000000000000000000000000815260008251614fb78160128501602087016146d3565b9190910160120192915050565b6000614fd26148ef846148a8565b9050828152838383011115614fe657600080fd5b610d1d8360208301846146d3565b60006020828403121561500657600080fd5b815167ffffffffffffffff81111561501d57600080fd5b8201601f8101841361502e57600080fd5b61428684825160208401614fc4565b7f5f000000000000000000000000000000000000000000000000000000000000008152600082516150758160018501602087016146d3565b9190910160010192915050565b7f5f435553544f4d5f4552524f523a2000000000000000000000000000000000008152600082516150ba81600f8501602087016146d3565b91909101600f0192915050565b81810381811115612b0157612b01614e3c565b80820180821115612b0157612b01614e3c565b8082028115828204841417612b0157612b01614e3c565b600082516151168184602087016146d3565b7f2e20504152414d535f5241573a20000000000000000000000000000000000000920191825250600e01919050565b600083516151578184602088016146d3565b83519083019061516b8183602088016146d3565b01949350505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126151a957600080fd5b83018035915067ffffffffffffffff8211156151c457600080fd5b6020019150600581901b36038213156142cc57600080fd5b600060c082360312156151ee57600080fd5b6151f6614854565b823567ffffffffffffffff8082111561520e57600080fd5b61521a36838701614ca9565b83526020850135602084015260408501356040840152606085013560608401526152466080860161466c565b608084015260a085013591508082111561525f57600080fd5b5061526c368286016148d0565b60a08301525092915050565b600060a0828403121561528a57600080fd5b60405160a0810181811067ffffffffffffffff821117156152ad576152ad6147d9565b806040525082358152602083013560208201526040830135604082015260608301356060820152608083013560808201528091505092915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261531d57600080fd5b83018035915067ffffffffffffffff82111561533857600080fd5b6020019150368190038213156142cc57600080fd5b60006020828403121561535f57600080fd5b8135610d1d81614657565b600081518084526020808501945080840160005b838110156153a35781516001600160a01b03168752958201959082019060010161537e565b509495945050505050565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6040815260006153ec604083018661536a565b82810360208401526140c88185876153ae565b606081526000615412606083018761536a565b828103602084015261542481876146f7565b905082810360408401526154398185876153ae565b979650505050505050565b60006020828403121561545657600080fd5b8151610d1d81614657565b6001600160a01b03848116825260406020808401829052908301849052600091859160608501845b878110156154b057843561549c81614657565b841682529382019390820190600101615489565b5098975050505050505050565b600082516154cf8184602087016146d3565b9190910192915050565b6001600160a01b0384168152604060208201526000612e596040830184866153ae565b6001600160a01b0383168152604060208201526000614286604083018461536a565b6000806040838503121561553157600080fd5b82519150602083015161554381614657565b809150509250929050565b600082516155608184602087016146d3565b7f5f41564f5f5f4d4f4449464945445f53544f5241474500000000000000000000920191825250601601919050565b600082516155a18184602087016146d3565b7f5f41564f5f5f4e4f5f464c4153484c4f414e5f494e5f464c4153484c4f414e00920191825250601f01919050565b600082601f8301126155e157600080fd5b815160206155f16148ef8361496e565b82815260059290921b8401810191818101908684111561561057600080fd5b8286015b84811015614a6157805161562781614657565b8352918301918301615614565b600082601f83011261564557600080fd5b610d1d83835160208501614fc4565b600080600080600060a0868803121561566c57600080fd5b855167ffffffffffffffff8082111561568457600080fd5b61569089838a016155d0565b96506020915081880151818111156156a757600080fd5b8801601f81018a136156b857600080fd5b80516156c66148ef8261496e565b81815260059190911b8201840190848101908c8311156156e557600080fd5b928501925b82841015615703578351825292850192908501906156ea565b60408c015160608d0151919a50985094505050508082111561572457600080fd5b61573089838a01615634565b9350608088015191508082111561574657600080fd5b5061575388828901615634565b9150509295509295909350565b60608152600061577360608301866146f7565b90508360208301527fffffffff0000000000000000000000000000000000000000000000000000000083166040830152949350505050565b600082516157bd8184602087016146d3565b7f5f41564f5f5f494e56414c49445f49445f4f525f4f5045524154494f4e000000920191825250601d01919050565b600083516157fe8184602088016146d3565b9190910191825250602001919050565b81835260006020808501808196508560051b810191508460005b8781101561591e57828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8188360301811261586757600080fd5b87016080813561587681614657565b6001600160a01b0316865281870135368390037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe10181126158b657600080fd5b8201878101903567ffffffffffffffff8111156158d257600080fd5b8036038213156158e157600080fd5b82898901526158f383890182846153ae565b604085810135908a015260609485013594909801939093525050509784019790840190600101615828565b5091979650505050505050565b60808152600061593f60808301878961580e565b60208301959095525060408101929092527fffffffff000000000000000000000000000000000000000000000000000000001660609091015292915050565b600069ffffffffffffffffffff80831681810361599d5761599d614e3c565b6001019392505050565b6040815260006159bb60408301858761580e565b9050826020830152949350505050565b6000602082840312156159dd57600080fd5b815167ffffffffffffffff8111156159f457600080fd5b614286848285016155d0565b602081526000610d1d602083018461536a565b600081615a2257615a22614e3c565b506000190190565b82815260406020820152600061428660408301846146f7565b600060208284031215615a5557600080fd5b81517fffffffff0000000000000000000000000000000000000000000000000000000081168114610d1d57600080fd5b60008152600082516150758160018501602087016146d3565b7f600b5981380380925939f3000000000000000000000000000000000000000000815260008251615ad681600b8501602087016146d3565b91909101600b0192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122056ffe1aede88a9ceef845f067a7a2b08750fe3af8876247bec6db406ed00b04b64736f6c63430008120033000000000000000000000000779385ec7a04242259add4990e3130846f80ea6900000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068480000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d

Deployed Bytecode

0x6080604052600436106102345760003560e01c806387265c9511610138578063b4907ddc116100b0578063ec5904831161007f578063ed969f0a11610064578063ed969f0a146107d8578063f7b14fac1461080c578063fe6bfed31461082c57600080fd5b8063ec59048314610784578063ec80dcc2146107b857600080fd5b8063b4907ddc146106b3578063bb1c006b146106fc578063c7fe944f14610730578063eb2856e91461076457600080fd5b80639ee3f88311610107578063a0d06193116100ec578063a0d0619314610617578063a755ecfc1461064b578063a80ebaf11461067f57600080fd5b80639ee3f883146105e2578063a08519b5146105f757600080fd5b806387265c951461051e57806394f0320e14610567578063984cee411461059b5780639cae7aa8146105ae57600080fd5b806343ea1996116101cb5780635b502b401161019a57806364d4c8191161017f57806364d4c819146104b55780636fbc15e9146104e95780638129fc1c1461050957600080fd5b80635b502b40146104615780636043848a1461049557600080fd5b806343ea1996146103c3578063447f537f146103d957806349926a661461040d5780634cf5de191461042d57600080fd5b80632b537875116102075780632b537875146103145780633a06cc8c146103415780633e0cd897146103755780633edd9187146103a357600080fd5b8063044d3cd21461023957806316914ac01461025b5780631bbce23d146102ac5780632a5d1d66146102d9575b600080fd5b34801561024557600080fd5b506102596102543660046145d3565b610860565b005b34801561026757600080fd5b5061028f7f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc00684881565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102b857600080fd5b506102cc6102c736600461467c565b6109f7565b6040516102a39190614723565b3480156102e557600080fd5b506103066102f43660046147c0565b60036020526000908152604090205481565b6040519081526020016102a3565b34801561032057600080fd5b5061033461032f366004614926565b610d24565b6040516102a3919061495b565b34801561034d57600080fd5b506103067f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e81565b34801561038157600080fd5b50610395610390366004614a6c565b611158565b6040516102a3929190614b97565b3480156103af57600080fd5b506102596103be3660046145d3565b6114dd565b3480156103cf57600080fd5b5061030661027a81565b3480156103e557600080fd5b506103067f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2081565b34801561041957600080fd5b506102596104283660046147c0565b611575565b34801561043957600080fd5b506103067fc84a2c176321157bd55b70feb5871c4304d99c870656d2fc420998eff645e20781565b34801561046d57600080fd5b506103067f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c081565b3480156104a157600080fd5b506102596104b0366004614bc3565b6115b8565b3480156104c157600080fd5b506103067fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647281565b3480156104f557600080fd5b50610259610504366004614c17565b6119ab565b34801561051557600080fd5b50610259611b38565b34801561052a57600080fd5b506103346040518060400160405280601081526020017f41766f6361646f2d4d756c74697369670000000000000000000000000000000081525081565b34801561057357600080fd5b5061028f7f00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a5481565b6102596105a9366004614c6c565b611dae565b3480156105ba57600080fd5b5061028f7f000000000000000000000000779385ec7a04242259add4990e3130846f80ea6981565b3480156105ee57600080fd5b50610306605a81565b34801561060357600080fd5b506102596106123660046147c0565b61211c565b34801561062357600080fd5b506103067fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b81565b34801561065757600080fd5b506103067fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4781565b34801561068b57600080fd5b506103067f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6581565b3480156106bf57600080fd5b506103346040518060400160405280600581526020017f312e312e3000000000000000000000000000000000000000000000000000000081525081565b34801561070857600080fd5b506103067fb7c77dbcd01eff35f637803daf7abe3f0e3b86b5102459be5665dc7d9a85ac5e81565b34801561073c57600080fd5b5061028f7f000000000000000000000000e8385fb3a5f15ded06eb5e20e5a81bf43115eb8e81565b34801561077057600080fd5b5061025961077f366004614bc3565b61222b565b34801561079057600080fd5b506103067f0000000000000000000000000000000000000000000000000001c6bf5263400081565b3480156107c457600080fd5b506102596107d3366004614c8e565b612654565b3480156107e457600080fd5b506103067f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb81565b34801561081857600080fd5b50610259610827366004614d8b565b612668565b34801561083857600080fd5b506103067f00000000000000000000000000000000000000000000000000b1a2bc2ec5000081565b610868612a86565b80600081900361087757505050565b60058111156108995760405163ec92598560e01b815260040160405180910390fd5b6000805474010000000000000000000000000000000000000000900469ffffffffffffffffffff16905b8281101561099e57818585838181106108de576108de614de0565b90506020020160208101906108f39190614e0f565b6affffffffffffffffffffff16036109425760405182907f5e8592bbef3f468eddd77c711ee57100f76bbb79500d8871a700fb6108929d9290600090a28161093a81614e6b565b925050610996565b8185858381811061095557610955614de0565b905060200201602081019061096a9190614e0f565b6affffffffffffffffffffff1611156109965760405163ec92598560e01b815260040160405180910390fd5b6001016108c3565b506000805469ffffffffffffffffffff90921674010000000000000000000000000000000000000000027fffff00000000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055505050565b6060827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf8101610ad05760408051600180825281830190925290816020015b604080518082019091526060815260006020820152815260200190600190039081610a36579050506040805160606020601f890181900402820181018352918101878152929450918291889088908190850183828082843760009201829052509385525050506001600160a01b0386166020909201919091528351849190610ac057610ac0614de0565b6020026020010181905250610d1b565b605a811015610b0b576040517fa8398eb900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fdec0de6520000000000000000000000000000000000000000000000000000000610b3a600560008789614e85565b610b4391614eaf565b7fffffffffffffffff0000000000000000000000000000000000000000000000001603610d0c5760557ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8201048067ffffffffffffffff811115610ba957610ba96147d9565b604051908082528060200260200182016040528015610bef57816020015b604080518082019091526060815260006020820152815260200190600190039081610bc75790505b50925060005b81811015610d0557605581026046810190600090610c1890605a01838a8c614e85565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050505060208101516040805180820190915291925060601c9080610c91857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf81018d8f614e85565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506001600160a01b0383166020909101528751889086908110610cec57610cec614de0565b6020026020010181905250836001019350505050610bf5565b5050610d1b565b610d1884860186614f26565b91505b505b9392505050565b6060600482511015610d6957505060408051808201909152601381527f5f524541534f4e5f4e4f545f444546494e454400000000000000000000000000602082015290565b60208201517fb1b7848f000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821601610df557600483019250610dcf83610dca90614f5b565b612aea565b604051602001610ddf9190614f7f565b6040516020818303038152906040529150611078565b7ff73c8660000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821601610e685760048301925082806020019051810190610e589190614ff4565b604051602001610ddf919061503d565b610e7560e082901c612aea565b604051602001610e859190615082565b6040516020818303038152906040529150600060048451610ea691906150c7565b9050610eb460fa60086150da565b610ebf8260026150ed565b1115610ed457610ed160fa60046150da565b90505b6000610ee18260026150ed565b67ffffffffffffffff811115610ef957610ef96147d9565b6040519080825280601f01601f191660200182016040528015610f23576020820181803683370190505b50905060005b8281101561102e57610f666010878360040181518110610f4b57610f4b614de0565b016020015160f81c81610f6057610f60614ef7565b04612b07565b828260020281518110610f7b57610f7b614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610fdf6010878360040181518110610fc457610fc4614de0565b016020015160f81c81610fd957610fd9614ef7565b06612b07565b828260020260010181518110610ff757610ff7614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101610f29565b50836040516020016110409190615104565b60405160208183030381529060405293508381604051602001611064929190615145565b604051602081830303815290604052935050505b60fa825111156111525781600061109160fa60016150da565b905060008167ffffffffffffffff8111156110ae576110ae6147d9565b6040519080825280601f01601f1916602001820160405280156110d8576020820181803683370190505b50905060005b8281101561114c578381815181106110f8576110f8614de0565b602001015160f81c60f81b82828151811061111557611115614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506001016110de565b50935050505b50919050565b60006060611164612a86565b61118c6111718780615174565b90508760400135876040013588606001358960800135612b67565b8251156111bd576111bd6111b76111a2886151dc565b6111b136899003890189615278565b46612bd9565b84612c4e565b6000808451116111ec576111e76111d3886151dc565b6111e236899003890189615278565b612cbf565b6111f5565b6111f584612d3f565b905060008086511161121157611209612e62565b60ff16611214565b85515b67ffffffffffffffff81111561122c5761122c6147d9565b604051908082528060200260200182016040528015611255578160200160208202803683370190505b50905085516000148061128757508560008151811061127657611276614de0565b602002602001015160000151516000145b806112c15750856000815181106112a0576112a0614de0565b6020026020010151602001516001600160a01b03166001600160a01b038016145b1561139f5760006112d0612e90565b60ff1690506000816001146112e757612af86112eb565b6121345b61ffff16905060018351111561132d57825161130990611af46150ed565b6113148360a06150ed565b611320906119006150da565b61132a91906150da565b90505b60408a01351961134657611343610898826150da565b90505b875160000361136a57825161135d90610ce46150ed565b61136790826150da565b90505b60005a905060005b825a61137e90846150c7565b1015611396578061138e81614e6b565b915050611372565b505050506113b5565b6113b282878a6040013560001914612ebe565b90505b6113ff886113e561271084518c8060a001906113d191906152e8565b600802610172929092029290920101919050565b8a60400135600019146113f9576000612f08565b84612f08565b9094509250831561146f573361141b60a08a0160808b0161534d565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b8361145360a08d018d6152e8565b604051611462939291906153d9565b60405180910390a36114d2565b3361148060a08a0160808b0161534d565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f83866114b960a08e018e6152e8565b6040516114c994939291906153ff565b60405180910390a35b505094509492505050565b6114e5612a86565b8060005b8181101561156f5760016003600086868581811061150957611509614de0565b9050602002013581526020019081526020016000208190555083838281811061153457611534614de0565b905060200201357f3749b30151183a20a5e6b93dce343f6010bde0e057441cca9548231f5727b70360405160405180910390a26001016114e9565b50505050565b61157d612a86565b6000818152600260205260408082208290555182917fc93ef95774a42ae5f9ca1e82cc762d17a6c2424041b91bf34dcf23aacc28cf1291a250565b6115c0612a86565b8160008190036115e35760405163ec92598560e01b815260040160405180910390fd5b60006115ed61317e565b805190915060006115fe84836150c7565b90506000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611640573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116649190615444565b905060008267ffffffffffffffff811115611681576116816147d9565b6040519080825280602002602001820160405280156116aa578160200160208202803683370190505b50905060008060005b868110156118595781810392508882148061171f57508b8b838181106116db576116db614de0565b90506020020160208101906116f0919061534d565b6001600160a01b031688828151811061170b5761170b614de0565b60200260200101516001600160a01b031614155b15611796578583101561177d5787818151811061173e5761173e614de0565b602002602001015184848151811061175857611758614de0565b60200260200101906001600160a01b031690816001600160a01b031681525050611851565b60405163ec92598560e01b815260040160405180910390fd5b846001600160a01b03168c8c848181106117b2576117b2614de0565b90506020020160208101906117c7919061534d565b6001600160a01b0316036117ee5760405163ec92598560e01b815260040160405180910390fd5b8b8b8381811061180057611800614de0565b9050602002016020810190611815919061534d565b6001600160a01b03167f3525e22824a8a7df2c9a6029941c824cf95b6447f1e13d5128fd3826d35afe8b60405160405180910390a28160010191505b6001016116b3565b5087811461187a5760405163ec92598560e01b815260040160405180910390fd5b611884838a61326d565b60007f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068486001600160a01b0316308d8d6040516024016118c593929190615461565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa1153e7f000000000000000000000000000000000000000000000000000000001790525161192891906154bd565b6000604051808303816000865af19150503d8060008114611965576040519150601f19603f3d011682016040523d82523d6000602084013e61196a565b606091505b505090508061199d576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b505050505050505050505050565b6119b3612a86565b6000546001600160a01b03848116911614611b33576040517efe90e30000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301527f000000000000000000000000779385ec7a04242259add4990e3130846f80ea69169062fe90e39060240160006040518083038186803b158015611a4057600080fd5b505afa158015611a54573d6000803e3d6000fd5b5050600080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b0388811691821784556040519216945092507fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b9190a2611b3084828585604051602401611ad3939291906154d9565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fe7e38cd3000000000000000000000000000000000000000000000000000000001790526133d1565b50505b505050565b611b40612a86565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba49190615444565b90506001600160a01b0381163b151580611bc557506001600160a01b038116155b15611be35760405163ec92598560e01b815260040160405180910390fd5b611bed6001603655565b6040516001600160a01b038216907f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2490600090a26040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a2604080516001808252818301909252600091602080830190803683370190505090508181600081518110611c8357611c83614de0565b6001600160a01b0392831660209182029290920101526040516000917f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068481690611cd290309085906024016154fc565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f863deb110000000000000000000000000000000000000000000000000000000017905251611d3591906154bd565b6000604051808303816000865af19150503d8060008114611d72576040519150601f19603f3d011682016040523d82523d6000602084013e611d77565b606091505b5050905080611b33576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a1505050565b611db6612a86565b60008060005a850390506000807f000000000000000000000000779385ec7a04242259add4990e3130846f80ea696001600160a01b0316613a9884604051602401611e0391815260200190565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f75dc7d8c0000000000000000000000000000000000000000000000000000000017905251611e6691906154bd565b6000604051808303818686fa925050503d8060008114611ea2576040519150601f19603f3d011682016040523d82523d6000602084013e611ea7565b606091505b5060408101519193509150828015611ec05750603f8251115b8015611ed357506001600160a01b038111155b15611f425781806020019051810190611eec919061551e565b90965094507f00000000000000000000000000000000000000000000000000b1a2bc2ec50000861115611f3d577f00000000000000000000000000000000000000000000000000b1a2bc2ec5000095505b611f89565b7f000000000000000000000000e8385fb3a5f15ded06eb5e20e5a81bf43115eb8e94507f0000000000000000000000000000000000000000000000000001c6bf5263400095505b5050505060008211156120eb57600083118015611fa557508282115b15611feb576040517f360ceed500000000000000000000000000000000000000000000000000000000815260048101839052602481018490526044015b60405180910390fd5b81471015612028576040517ffae5708700000000000000000000000000000000000000000000000000000000815260048101839052602401611fe2565b6000816001600160a01b0316836103e890604051600060405180830381858888f193505050503d806000811461207a576040519150601f19603f3d011682016040523d82523d6000602084013e61207f565b606091505b5050905080156120b95760405183907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a26120e5565b60405183907fd1b3877a34a9595e45447dad16b7354ec5a8bece40075fd60d647e76397b476490600090a25b5061156f565b60405182907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a250505050565b612124612a86565b6121eb6121507f6e452848784197f00927d379e3db9e69a5131d2269f862bfcd05a0b38f6abf7f6133f6565b604080517f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2060208201529081018490526060015b60408051601f1981840301815282825280516020918201207f19010000000000000000000000000000000000000000000000000000000000008483015260228401949094526042808401949094528151808403909401845260629092019052815191012090565b600081815260026020526040808220600190555191925082917fbef281d0d1046788f9baa37e73d57e85933f130d8ec1e223366e0b04fbf254029190a250565b612233612a86565b8180158061226e575060008484828161224e5761224e614de0565b9050602002016020810190612263919061534d565b6001600160a01b0316145b1561228c5760405163ec92598560e01b815260040160405180910390fd5b600061229661317e565b805190915060006122a784836150da565b9050605a8111156122cb5760405163ec92598560e01b815260040160405180910390fd5b60008167ffffffffffffffff8111156122e6576122e66147d9565b60405190808252806020026020018201604052801561230f578160200160208202803683370190505b50905060008060005b848110156125235781810392508782148061238d5750858310801561238d57508a8a8381811061234a5761234a614de0565b905060200201602081019061235f919061534d565b6001600160a01b031687848151811061237a5761237a614de0565b60200260200101516001600160a01b0316105b156123e3578683815181106123a4576123a4614de0565b60200260200101518482815181106123be576123be614de0565b60200260200101906001600160a01b031690816001600160a01b03168152505061249f565b8a8a838181106123f5576123f5614de0565b905060200201602081019061240a919061534d565b84828151811061241c5761241c614de0565b60200260200101906001600160a01b031690816001600160a01b0316815250508a8a8381811061244e5761244e614de0565b9050602002016020810190612463919061534d565b6001600160a01b03167f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2460405160405180910390a28160010191505b6000811180156124fd5750836124b66001836150c7565b815181106124c6576124c6614de0565b60200260200101516001600160a01b03168482815181106124e9576124e9614de0565b60200260200101516001600160a01b031611155b1561251b5760405163ec92598560e01b815260040160405180910390fd5b600101612318565b5061252e838961326d565b60007f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068486001600160a01b0316308c8c60405160240161256f93929190615461565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f863deb1100000000000000000000000000000000000000000000000000000000179052516125d291906154bd565b6000604051808303816000865af19150503d806000811461260f576040519150601f19603f3d011682016040523d82523d6000602084013e612614565b606091505b5050905080612647576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b5050505050505050505050565b61265c612a86565b6126658161350b565b50565b612670612a86565b61267a6001603655565b6000806000846001148061268e5750846015145b905080156126a157600054925060015491505b855160005b81811015612a7c5760008882815181106126c2576126c2614de0565b60200260200101519050600060606000836060015160001480156126fb575060028b10806126f057508a6014145b806126fb57508a6015145b156127825783600001516001600160a01b03168460400151856020015160405161272591906154bd565b60006040518083038185875af1925050503d8060008114612762576040519150601f19603f3d011682016040523d82523d6000602084013e612767565b606091505b5090935091508261277d5761277d8186846135e2565b612a6d565b836060015160011480156127935750865b156128905783600001516001600160a01b031684602001516040516127b891906154bd565b600060405180830381855af49150503d80600081146127f3576040519150601f19603f3d011682016040523d82523d6000602084013e6127f8565b606091505b5090935091508261280e5761280e8186846135e2565b6128186001603655565b6000546001548a8214801561282c57508981145b6128895761283987613605565b604051602001612849919061554e565b60408051601f19818403018152908290527f08c379a0000000000000000000000000000000000000000000000000000000008252611fe29160040161495b565b5050612a6d565b836060015160021480156128ae57508a601414806128ae57508a6015145b15612a545789156128d2576128c285613605565b604051602001612849919061558f565b602084015160048101805190916128f191810160249081019101615654565b50604080518082018252601281527f657865637574654f7065617274696f6e28290000000000000000000000000000602091820152905191955061295e94508593504292507f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e9101615760565b60405160208183030381529060405280519060200120603660006101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff021916908360081c02179055508b6036601f6101000a81548160ff021916908360ff16021790555084600001516001600160a01b0316856040015186602001516040516129ec91906154bd565b60006040518083038185875af1925050503d8060008114612a29576040519150601f19603f3d011682016040523d82523d6000602084013e612a2e565b606091505b50909450925083612a4457612a448287856135e2565b612a4e6001603655565b50612a6d565b612a5d85613605565b60405160200161284991906157ab565b846001019450505050506126a6565b5050505050505050565b307f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef6001600160a01b031603612ae8576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6060612b0182612af9846136a5565b60010161370f565b92915050565b6000600a8260ff161015612b1f575060300160f81b90565b60108260ff161015612b35575060570160f81b90565b6040517f9778fcab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085118015612ba75750836000191480612ba7575060005474010000000000000000000000000000000000000000900469ffffffffffffffffffff1684145b5042831180612bc05750600082118015612bc057504282105b50600081118015612bd15750803414155b505050505050565b60007fc84a2c176321157bd55b70feb5871c4304d99c870656d2fc420998eff645e207612c0585613952565b612c0e85613b44565b60408051602081019490945283019190915260608201526080810183905260a0016040516020818303038152906040528051906020012090509392505050565b80516001811115611b335760005b8181101561156f57828181518110612c7657612c76614de0565b60200260200101516000015184148015612cac5750828181518110612c9d57612c9d614de0565b60200260200101516020015146145b15612cb75750505050565b600101612c5c565b6000610d1d612ced7f6e452848784197f00927d379e3db9e69a5131d2269f862bfcd05a0b38f6abf7f6133f6565b7fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b612d1786613952565b612d2086613b44565b6040805160208101949094528301919091526060820152608001612184565b8051600090606080835b83811015612ddc5782868281518110612d6457612d64614de0565b602002602001015160000151604051602001612d819291906157ec565b604051602081830303815290604052925081868281518110612da557612da5614de0565b602002602001015160200151604051602001612dc29291906157ec565b60408051601f198184030181529190529150600101612d49565b50612e59612e097f56de5bbd3725e9c80073ed00b9767413d9ddf7d1ec1d602a8f924f15a9c756856133f6565b8351602080860191909120845185830120604080517fb7c77dbcd01eff35f637803daf7abe3f0e3b86b5102459be5665dc7d9a85ac5e948101949094528301919091526060820152608001612184565b95945050505050565b60015474010000000000000000000000000000000000000000900460ff166000819003612e8d575060015b90565b6001547501000000000000000000000000000000000000000000900460ff166000819003612e8d5750600190565b60606000612ecd858585613bb8565b9250905080610d1b576040517f70a1717900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006060612f168580615174565b604080518082018252600e81527f5f63616c6c5461726765747328290000000000000000000000000000000000006020918201529051612f81939291808a01359142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b910161592b565b60408051808303601f190181529190528051602090910120603680547fff000000000000000000000000000000000000000000000000000000000000001660089290921c91909117905582613034576000805474010000000000000000000000000000000000000000900469ffffffffffffffffffff169060146130048361597e565b91906101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff16021790555050613047565b6000838152600360205260409020600190555b60006130538680615174565b876020013560405160240161306a939291906159a7565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb92e87fa0000000000000000000000000000000000000000000000000000000017815281516000805493945060609390928392916001600160a01b03165af493503d6040519150601f19601f6020830101168201604052808252806000602084013e508361316a57805160000361314d576040518060400160405280601781526020017f41564f5f5f524541534f4e5f4e4f545f444546494e4544000000000000000000815250925061316a565b600481019050808060200190518101906131679190614ff4565b92505b6131746001603655565b5050935093915050565b6001546060906001600160a01b03168061324b576040805160018082528183019092529060208083019080368337019050509150306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156131f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132149190615444565b8260008151811061322757613227614de0565b60200260200101906001600160a01b031690816001600160a01b0316815250505090565b61325481613f6e565b80602001905181019061326791906159cb565b91505090565b8151605a81118061327c575080155b1561329a5760405163ec92598560e01b815260040160405180910390fd5b80600103613322578160ff166001146132c65760405163ec92598560e01b815260040160405180910390fd5b6001805474010000000000000000000000000000000000000000900460ff161115613318576040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a25b6000600155505050565b600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000060ff8416021790556040516133909061337c908590602001615a00565b604051602081830303815290604052613f8f565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055611b338261350b565b6060610d1d8383604051806060016040528060278152602001615b136027913961405a565b604080518082018252601081527f41766f6361646f2d4d756c74697369670000000000000000000000000000000060209182015281518083018352600581527f312e312e300000000000000000000000000000000000000000000000000000009082015281517fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472918101919091527ff5d974efd8e94277ef83fff53580c07a80e6e3ffdb2e3fec63185a4e812655a2918101919091527f6815ba53416ba06aff1932cc76b3832272bafab9bc8e066be382e32b06ba5546606082015261027a60808201523060a082015260c0810182905260009060e0015b604051602081830303815290604052805190602001209050919050565b60ff81161580613527575061351e612e90565b60ff168160ff16115b156135455760405163ec92598560e01b815260040160405180910390fd5b60015460ff82811674010000000000000000000000000000000000000000909204161461266557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000060ff8416908102919091179091556040517f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a250565b6135eb82613605565b6135f482610d24565b604051602001612849929190615145565b60606000613612836140d2565b600101905060008167ffffffffffffffff811115613632576136326147d9565b6040519080825280601f01601f19166020018201604052801561365c576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461366657509392505050565b600080608083901c156136bd5760809290921c916010015b604083901c156136d25760409290921c916008015b602083901c156136e75760209290921c916004015b601083901c156136fc5760109290921c916002015b600883901c15612b015760010192915050565b6060600061371e8360026150ed565b6137299060026150da565b67ffffffffffffffff811115613741576137416147d9565b6040519080825280601f01601f19166020018201604052801561376b576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000816000815181106137a2576137a2614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f78000000000000000000000000000000000000000000000000000000000000008160018151811061380557613805614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006138418460026150ed565b61384c9060016150da565b90505b60018111156138e9577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061388d5761388d614de0565b1a60f81b8282815181106138a3576138a3614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c936138e281615a13565b905061384f565b508315610d1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401611fe2565b8051516000906060825b82811015613a9157817f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f658660000151838151811061399c5761399c614de0565b602002602001015160000151876000015184815181106139be576139be614de0565b60200260200101516020015180519060200120886000015185815181106139e7576139e7614de0565b60200260200101516040015189600001518681518110613a0957613a09614de0565b602002602001015160600151604051602001613a509594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120604051602001613a779291906157ec565b60408051601f19818403018152919052915060010161395c565b507fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc47818051906020012085602001518660400151876060015188608001518960a0015180519060200120604051602001613b25979695949392919096875260208701959095526040860193909352606085019190915260808401526001600160a01b031660a083015260c082015260e00190565b6040516020818303038152906040528051906020012092505050919050565b60007f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e826000015183602001518460400151856060015186608001516040516020016134ee96959493929190958652602086019490945260408501929092526060840152608083015260a082015260c00190565b8151600090606090613bc8612e62565b60ff16811080613bee5750838015613bee57506000868152600360205260409020546001145b15613c0c5760405163ec92598560e01b815260040160405180910390fd5b8067ffffffffffffffff811115613c2557613c256147d9565b604051908082528060200260200182016040528015613c4e578160200160208202803683370190505b5091506000613c5b61317e565b805190915060008080805b86811015613f5a57613c9e8b8281518110613c8357613c83614de0565b6020026020010151602001516001600160a01b03163b151590565b15613cfc578a8181518110613cb557613cb5614de0565b602002602001015160200151888281518110613cd357613cd3614de0565b60200260200101906001600160a01b031690816001600160a01b03168152505060019250613dbd565b613d238c8c8381518110613d1257613d12614de0565b6020026020010151600001516141b4565b888281518110613d3557613d35614de0565b60200260200101906001600160a01b031690816001600160a01b031681525050878181518110613d6757613d67614de0565b60200260200101516001600160a01b03168b8281518110613d8a57613d8a614de0565b6020026020010151602001516001600160a01b031614613dbd5760405163ec92598560e01b815260040160405180910390fd5b835b85811015613e2957888281518110613dd957613dd9614de0565b60200260200101516001600160a01b0316878281518110613dfc57613dfc614de0565b60200260200101516001600160a01b031603613e215760019250806001019450613e29565b600101613dbf565b5081613e3f576000985050505050505050613f66565b600091508215613f52578a517f1626ba7e00000000000000000000000000000000000000000000000000000000908c9083908110613e7f57613e7f614de0565b6020026020010151602001516001600160a01b0316631626ba7e8e8e8581518110613eac57613eac614de0565b6020026020010151600001516040518363ffffffff1660e01b8152600401613ed5929190615a2a565b602060405180830381865afa158015613ef2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f169190615a43565b7fffffffff000000000000000000000000000000000000000000000000000000001614613f4d576000985050505050505050613f66565b600092505b600101613c66565b50600197505050505050505b935093915050565b6060612b01826001613f8a816001600160a01b0384163b6150c7565b6141d0565b60008082604051602001613fa39190615a85565b6040516020818303038152906040529050600081604051602001613fc79190615a9e565b60405160208183030381529060405290508051602082016000f092506001600160a01b038316614053576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4445504c4f594d454e545f4641494c45440000000000000000000000000000006044820152606401611fe2565b5050919050565b6060600080856001600160a01b03168560405161407791906154bd565b600060405180830381855af49150503d80600081146140b2576040519150601f19603f3d011682016040523d82523d6000602084013e6140b7565b606091505b50915091506140c8868383876141f3565b9695505050505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061411b577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310614147576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061416557662386f26fc10000830492506010015b6305f5e100831061417d576305f5e100830492506008015b612710831061419157612710830492506004015b606483106141a3576064830492506002015b600a8310612b015760010192915050565b60008060006141c3858561428e565b91509150610d1b816142d3565b60408051603f8301601f19168101909152818152818360208301863c9392505050565b6060831561427c578251600003614275576001600160a01b0385163b614275576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611fe2565b5081614286565b6142868383614486565b949350505050565b60008082516041036142c45760208301516040840151606085015160001a6142b8878285856144ca565b945094505050506142cc565b506000905060025b9250929050565b60008160048111156142e7576142e7615ae3565b036142ef5750565b600181600481111561430357614303615ae3565b0361436a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611fe2565b600281600481111561437e5761437e615ae3565b036143e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611fe2565b60038160048111156143f9576143f9615ae3565b03612665576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401611fe2565b8151156144965781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fe2919061495b565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156145015750600090506003614585565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614555573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661457e57600060019250925050614585565b9150600090505b94509492505050565b60008083601f8401126145a057600080fd5b50813567ffffffffffffffff8111156145b857600080fd5b6020830191508360208260051b85010111156142cc57600080fd5b600080602083850312156145e657600080fd5b823567ffffffffffffffff8111156145fd57600080fd5b6146098582860161458e565b90969095509350505050565b60008083601f84011261462757600080fd5b50813567ffffffffffffffff81111561463f57600080fd5b6020830191508360208285010111156142cc57600080fd5b6001600160a01b038116811461266557600080fd5b803561467781614657565b919050565b60008060006040848603121561469157600080fd5b833567ffffffffffffffff8111156146a857600080fd5b6146b486828701614615565b90945092505060208401356146c881614657565b809150509250925092565b60005b838110156146ee5781810151838201526020016146d6565b50506000910152565b6000815180845261470f8160208601602086016146d3565b601f01601f19169290920160200192915050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b838110156147b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08984030185528151805187855261478c888601826146f7565b918901516001600160a01b0316948901949094529487019492509086019060010161474a565b509098975050505050505050565b6000602082840312156147d257600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561482b5761482b6147d9565b60405290565b6040516080810167ffffffffffffffff8111828210171561482b5761482b6147d9565b60405160c0810167ffffffffffffffff8111828210171561482b5761482b6147d9565b604051601f8201601f1916810167ffffffffffffffff811182821017156148a0576148a06147d9565b604052919050565b600067ffffffffffffffff8211156148c2576148c26147d9565b50601f01601f191660200190565b600082601f8301126148e157600080fd5b81356148f46148ef826148a8565b614877565b81815284602083860101111561490957600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561493857600080fd5b813567ffffffffffffffff81111561494f57600080fd5b614286848285016148d0565b602081526000610d1d60208301846146f7565b600067ffffffffffffffff821115614988576149886147d9565b5060051b60200190565b600082601f8301126149a357600080fd5b813560206149b36148ef8361496e565b82815260059290921b840181019181810190868411156149d257600080fd5b8286015b84811015614a6157803567ffffffffffffffff808211156149f75760008081fd5b8189019150604080601f19848d03011215614a125760008081fd5b614a1a614808565b8784013583811115614a2c5760008081fd5b614a3a8d8a838801016148d0565b8252509281013592614a4b84614657565b80880193909352505083529183019183016149d6565b509695505050505050565b600080600080848603610100811215614a8457600080fd5b853567ffffffffffffffff80821115614a9c57600080fd5b9087019060c0828a031215614ab057600080fd5b90955060209060a0601f1984011215614ac857600080fd5b818801955060c0880135925080831115614ae157600080fd5b614aed89848a01614992565b945060e0880135925080831115614b0357600080fd5b50818701915087601f830112614b1857600080fd5b8135614b266148ef8261496e565b81815260069190911b8301820190828101908a831115614b4557600080fd5b938301935b82851015614b88576040858c031215614b635760008081fd5b614b6b614808565b853581528486013585820152825260409094019390830190614b4a565b979a9699509497505050505050565b821515815260406020820152600061428660408301846146f7565b803560ff8116811461467757600080fd5b600080600060408486031215614bd857600080fd5b833567ffffffffffffffff811115614bef57600080fd5b614bfb8682870161458e565b9094509250614c0e905060208501614bb2565b90509250925092565b600080600060408486031215614c2c57600080fd5b8335614c3781614657565b9250602084013567ffffffffffffffff811115614c5357600080fd5b614c5f86828701614615565b9497909650939450505050565b60008060408385031215614c7f57600080fd5b50508035926020909101359150565b600060208284031215614ca057600080fd5b610d1d82614bb2565b600082601f830112614cba57600080fd5b81356020614cca6148ef8361496e565b82815260059290921b84018101918181019086841115614ce957600080fd5b8286015b84811015614a6157803567ffffffffffffffff80821115614d0e5760008081fd5b8189019150608080601f19848d03011215614d295760008081fd5b614d31614831565b87840135614d3e81614657565b815260408481013584811115614d545760008081fd5b614d628e8b838901016148d0565b838b01525060608581013591830191909152919093013590830152508352918301918301614ced565b600080600060608486031215614da057600080fd5b833567ffffffffffffffff811115614db757600080fd5b614dc386828701614ca9565b93505060208401359150604084013580151581146146c857600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215614e2157600080fd5b81356affffffffffffffffffffff81168114610d1d57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006000198203614e7e57614e7e614e3c565b5060010190565b60008085851115614e9557600080fd5b83861115614ea257600080fd5b5050820193919092039150565b7fffffffffffffffff0000000000000000000000000000000000000000000000008135818116916008851015614eef5780818660080360031b1b83161692505b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060208284031215614f3857600080fd5b813567ffffffffffffffff811115614f4f57600080fd5b61428684828501614992565b805160208083015191908110156111525760001960209190910360031b1b16919050565b7f5f5441524745545f50414e49434b45443a200000000000000000000000000000815260008251614fb78160128501602087016146d3565b9190910160120192915050565b6000614fd26148ef846148a8565b9050828152838383011115614fe657600080fd5b610d1d8360208301846146d3565b60006020828403121561500657600080fd5b815167ffffffffffffffff81111561501d57600080fd5b8201601f8101841361502e57600080fd5b61428684825160208401614fc4565b7f5f000000000000000000000000000000000000000000000000000000000000008152600082516150758160018501602087016146d3565b9190910160010192915050565b7f5f435553544f4d5f4552524f523a2000000000000000000000000000000000008152600082516150ba81600f8501602087016146d3565b91909101600f0192915050565b81810381811115612b0157612b01614e3c565b80820180821115612b0157612b01614e3c565b8082028115828204841417612b0157612b01614e3c565b600082516151168184602087016146d3565b7f2e20504152414d535f5241573a20000000000000000000000000000000000000920191825250600e01919050565b600083516151578184602088016146d3565b83519083019061516b8183602088016146d3565b01949350505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126151a957600080fd5b83018035915067ffffffffffffffff8211156151c457600080fd5b6020019150600581901b36038213156142cc57600080fd5b600060c082360312156151ee57600080fd5b6151f6614854565b823567ffffffffffffffff8082111561520e57600080fd5b61521a36838701614ca9565b83526020850135602084015260408501356040840152606085013560608401526152466080860161466c565b608084015260a085013591508082111561525f57600080fd5b5061526c368286016148d0565b60a08301525092915050565b600060a0828403121561528a57600080fd5b60405160a0810181811067ffffffffffffffff821117156152ad576152ad6147d9565b806040525082358152602083013560208201526040830135604082015260608301356060820152608083013560808201528091505092915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261531d57600080fd5b83018035915067ffffffffffffffff82111561533857600080fd5b6020019150368190038213156142cc57600080fd5b60006020828403121561535f57600080fd5b8135610d1d81614657565b600081518084526020808501945080840160005b838110156153a35781516001600160a01b03168752958201959082019060010161537e565b509495945050505050565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6040815260006153ec604083018661536a565b82810360208401526140c88185876153ae565b606081526000615412606083018761536a565b828103602084015261542481876146f7565b905082810360408401526154398185876153ae565b979650505050505050565b60006020828403121561545657600080fd5b8151610d1d81614657565b6001600160a01b03848116825260406020808401829052908301849052600091859160608501845b878110156154b057843561549c81614657565b841682529382019390820190600101615489565b5098975050505050505050565b600082516154cf8184602087016146d3565b9190910192915050565b6001600160a01b0384168152604060208201526000612e596040830184866153ae565b6001600160a01b0383168152604060208201526000614286604083018461536a565b6000806040838503121561553157600080fd5b82519150602083015161554381614657565b809150509250929050565b600082516155608184602087016146d3565b7f5f41564f5f5f4d4f4449464945445f53544f5241474500000000000000000000920191825250601601919050565b600082516155a18184602087016146d3565b7f5f41564f5f5f4e4f5f464c4153484c4f414e5f494e5f464c4153484c4f414e00920191825250601f01919050565b600082601f8301126155e157600080fd5b815160206155f16148ef8361496e565b82815260059290921b8401810191818101908684111561561057600080fd5b8286015b84811015614a6157805161562781614657565b8352918301918301615614565b600082601f83011261564557600080fd5b610d1d83835160208501614fc4565b600080600080600060a0868803121561566c57600080fd5b855167ffffffffffffffff8082111561568457600080fd5b61569089838a016155d0565b96506020915081880151818111156156a757600080fd5b8801601f81018a136156b857600080fd5b80516156c66148ef8261496e565b81815260059190911b8201840190848101908c8311156156e557600080fd5b928501925b82841015615703578351825292850192908501906156ea565b60408c015160608d0151919a50985094505050508082111561572457600080fd5b61573089838a01615634565b9350608088015191508082111561574657600080fd5b5061575388828901615634565b9150509295509295909350565b60608152600061577360608301866146f7565b90508360208301527fffffffff0000000000000000000000000000000000000000000000000000000083166040830152949350505050565b600082516157bd8184602087016146d3565b7f5f41564f5f5f494e56414c49445f49445f4f525f4f5045524154494f4e000000920191825250601d01919050565b600083516157fe8184602088016146d3565b9190910191825250602001919050565b81835260006020808501808196508560051b810191508460005b8781101561591e57828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8188360301811261586757600080fd5b87016080813561587681614657565b6001600160a01b0316865281870135368390037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe10181126158b657600080fd5b8201878101903567ffffffffffffffff8111156158d257600080fd5b8036038213156158e157600080fd5b82898901526158f383890182846153ae565b604085810135908a015260609485013594909801939093525050509784019790840190600101615828565b5091979650505050505050565b60808152600061593f60808301878961580e565b60208301959095525060408101929092527fffffffff000000000000000000000000000000000000000000000000000000001660609091015292915050565b600069ffffffffffffffffffff80831681810361599d5761599d614e3c565b6001019392505050565b6040815260006159bb60408301858761580e565b9050826020830152949350505050565b6000602082840312156159dd57600080fd5b815167ffffffffffffffff8111156159f457600080fd5b614286848285016155d0565b602081526000610d1d602083018461536a565b600081615a2257615a22614e3c565b506000190190565b82815260406020820152600061428660408301846146f7565b600060208284031215615a5557600080fd5b81517fffffffff0000000000000000000000000000000000000000000000000000000081168114610d1d57600080fd5b60008152600082516150758160018501602087016146d3565b7f600b5981380380925939f3000000000000000000000000000000000000000000815260008251615ad681600b8501602087016146d3565b91909101600b0192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122056ffe1aede88a9ceef845f067a7a2b08750fe3af8876247bec6db406ed00b04b64736f6c63430008120033

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

000000000000000000000000779385ec7a04242259add4990e3130846f80ea6900000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068480000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d

-----Decoded View---------------
Arg [0] : avoRegistry_ (address): 0x779385EC7A04242259AdD4990e3130846f80eA69
Arg [1] : avoForwarder_ (address): 0x46978CD477A496028A18c02F07ab7F35EDBa5A54
Arg [2] : avoSignersList_ (address): 0xA27A71DD0348b5C69370eF9Da3743197DC006848
Arg [3] : avoConfigV1_ (address): 0x1412121E487478498b355C227c77D5ED6CF1798d

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000779385ec7a04242259add4990e3130846f80ea69
Arg [1] : 00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54
Arg [2] : 000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc006848
Arg [3] : 0000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d


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
[ Download: CSV Export  ]

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