Overview
S Balance
0 S
S Value
-More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
411059 | 18 days ago | Contract Creation | 0 S |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
AvocadoMultisig
Compiler Version
v0.8.18+commit.87f61d96
Optimization Enabled:
Yes with 10000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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); } }
// 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); }
// 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); }
// 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; } }
// 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); } }
// 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); }
// 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; } }
// 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); } } }
// 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)); } }
// 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; } }
// 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); }
// 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); } } }
// 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); } }
// 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()) } } } }
// 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 ) ) ) ) ); } }
// 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); }
// 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); }
// 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; } }
// 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 } } }
// 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; } }
// 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); }
// 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); }
// 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); }
// 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); }
// 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; }
// 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; }
// 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) } } }
{ "optimizer": { "enabled": true, "runs": 10000 }, "libraries": {}, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"},{"internalType":"contract IAvocadoMultisigV1Secondary","name":"secondary_","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":[{"internalType":"address","name":"fromImplementation_","type":"address"},{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"_afterUpgradeHook","outputs":[],"stateMutability":"nonpayable","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"}],"name":"_callTargets","outputs":[],"stateMutability":"payable","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":"avoNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"avoRegistry","outputs":[{"internalType":"contract IAvoRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"avoSecondary","outputs":[{"internalType":"contract IAvocadoMultisigV1Secondary","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"avoSignersList","outputs":[{"internalType":"contract IAvoSignersList","name":"","type":"address"}],"stateMutability":"view","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[]"}],"name":"cast","outputs":[{"internalType":"bool","name":"success_","type":"bool"},{"internalType":"string","name":"revertReason_","type":"string"}],"stateMutability":"payable","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":"maxFee","type":"uint256"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.CastAuthorizedParams","name":"authorizedParams_","type":"tuple"},{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"signer","type":"address"}],"internalType":"struct AvocadoMultisigStructs.SignatureParams[]","name":"signaturesParams_","type":"tuple[]"}],"name":"castAuthorized","outputs":[{"internalType":"bool","name":"success_","type":"bool"},{"internalType":"string","name":"revertReason_","type":"string"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"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"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.CastChainAgnosticParams","name":"params_","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":"castChainAgnostic","outputs":[{"internalType":"bool","name":"success_","type":"bool"},{"internalType":"string","name":"revertReason_","type":"string"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"domainSeparatorV4","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"domainSeparatorV4ChainAgnostic","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","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[]"}],"name":"estimateCast","outputs":[{"internalType":"bool","name":"success_","type":"bool"},{"internalType":"string","name":"revertReason_","type":"string"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"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"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.CastChainAgnosticParams","name":"params_","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":"estimateCastChainAgnostic","outputs":[{"internalType":"bool","name":"success_","type":"bool"},{"internalType":"string","name":"revertReason_","type":"string"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"address","name":"initiator_","type":"address"},{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"executeOperation","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"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"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.CastChainAgnosticParams[]","name":"params_","type":"tuple[]"}],"name":"getChainAgnosticHashes","outputs":[{"components":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.ChainAgnosticHash[]","name":"chainAgnosticHashes_","type":"tuple[]"}],"stateMutability":"pure","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"}],"name":"getSigDigest","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","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":"maxFee","type":"uint256"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.CastAuthorizedParams","name":"authorizedParams_","type":"tuple"}],"name":"getSigDigestAuthorized","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"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"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.CastChainAgnosticParams[]","name":"params_","type":"tuple[]"}],"name":"getSigDigestChainAgnostic","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.ChainAgnosticHash[]","name":"chainAgnosticHashes_","type":"tuple[]"}],"name":"getSigDigestChainAgnosticFromHashes","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"index","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"signer_","type":"address"}],"name":"isSigner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"magicValue","type":"bytes4"}],"stateMutability":"view","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":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[],"name":"requiredSigners","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","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":[],"name":"signers","outputs":[{"internalType":"address[]","name":"signers_","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"signersCount","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","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[]"}],"name":"simulateCast","outputs":[{"internalType":"bool","name":"success_","type":"bool"},{"internalType":"string","name":"revertReason_","type":"string"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"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"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.CastChainAgnosticParams","name":"params_","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":"simulateCastChainAgnostic","outputs":[{"internalType":"bool","name":"success_","type":"bool"},{"internalType":"string","name":"revertReason_","type":"string"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"avoImplementation_","type":"address"},{"internalType":"bytes","name":"afterUpgradeHookData_","type":"bytes"}],"name":"upgradeTo","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[]"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":"maxFee","type":"uint256"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.CastAuthorizedParams","name":"authorizedParams_","type":"tuple"},{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"signer","type":"address"}],"internalType":"struct AvocadoMultisigStructs.SignatureParams[]","name":"signaturesParams_","type":"tuple[]"}],"name":"verifyAuthorized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"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"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.CastChainAgnosticParams","name":"params_","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":"verifyChainAgnostic","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
466101c090815260206101a08190529081902060805261027a610200526101e0526102206040527f56de5bbd3725e9c80073ed00b9767413d9ddf7d1ec1d602a8f924f15a9c7568560a0523480156200005757600080fd5b50604051620067a9380380620067a98339810160408190526200007a916200063e565b808585858583838383838383836001600160a01b0384161580620000a557506001600160a01b038316155b80620000b857506001600160a01b038216155b80620000cb57506001600160a01b038116155b15620000ea5760405163ec92598560e01b815260040160405180910390fd5b6001600160a01b0380851660c05283811660e0528281166101005260408051632271f8db60e01b81529051600092841691632271f8db9160048083019260609291908290030181865afa15801562000146573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200016c9190620006be565b805190915015806200018057506020810151155b8062000197575060408101516001600160a01b0316155b80620001a7575060208101518151115b15620001c65760405163ec92598560e01b815260040160405180910390fd5b805161012052602081015161014052604001516001600160a01b03166101605250620001fa96505062000556945050505050565b5050506001600160a01b0382169050620002275760405163ec92598560e01b815260040160405180910390fd5b806001600160a01b0316610180816001600160a01b03168152505050846001600160a01b0316816001600160a01b0316639cae7aa86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200028c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002b2919062000731565b6001600160a01b03161415806200033f5750836001600160a01b0316816001600160a01b03166394f0320e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200030d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000333919062000731565b6001600160a01b031614155b80620003c15750826001600160a01b0316816001600160a01b03166316914ac06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200038f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003b5919062000731565b6001600160a01b031614155b8062000434575061012051816001600160a01b031663ec5904836040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200040b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000431919062000758565b14155b80620004a7575061014051816001600160a01b031663fe6bfed36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200047e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004a4919062000758565b14155b806200052c5750610160516001600160a01b0316816001600160a01b031663c7fe944f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620004fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000520919062000731565b6001600160a01b031614155b156200054b5760405163ec92598560e01b815260040160405180910390fd5b505050505062000772565b600054600160f81b900460ff1615620005c55760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff600160f01b9091048116101562000623576000805460ff60f01b191660ff60f01b17905560405160ff81527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6001600160a01b03811681146200063b57600080fd5b50565b600080600080600060a086880312156200065757600080fd5b8551620006648162000625565b6020870151909550620006778162000625565b60408701519094506200068a8162000625565b60608701519093506200069d8162000625565b6080870151909250620006b08162000625565b809150509295509295909350565b600060608284031215620006d157600080fd5b604051606081016001600160401b03811182821017156200070257634e487b7160e01b600052604160045260246000fd5b806040525082518152602083015160208201526040830151620007258162000625565b60408201529392505050565b6000602082840312156200074457600080fd5b8151620007518162000625565b9392505050565b6000602082840312156200076b57600080fd5b5051919050565b60805160a05160c05160e0516101005161012051610140516101605161018051615f486200086160003960008181610a2c01528181610def015281816111320152818161146a01528181611667015281816116fc01528181611a6801528181611eed0152818161212801528181612149015281816134bd015281816134de0152613d2801526000610ba101526000610d2701526000610c4a015260006104c201526000818161090001528181612d1201526134540152600061093401526000818161230c015261268a0152600081816110130152818161180f01528181612c8d01526130360152615f486000f3fe6080604052600436106103bc5760003560e01c806387265c95116101f2578063bb1c006b1161010d578063e8a9a4b5116100a0578063ed969f0a1161006f578063ed969f0a14610c87578063f23a6e6114610cbb578063f28dd30614610d00578063fe6bfed314610d1557600080fd5b8063e8a9a4b514610c18578063eb2856e914610724578063ec59048314610c38578063ec80dcc214610c6c57600080fd5b8063c7fe944f116100dc578063c7fe944f14610b8f578063e3a8897014610bc3578063e40956b114610be3578063e7e38cd314610bf857600080fd5b8063bb1c006b14610abd578063bb6fe03d14610af1578063bc197c8114610b11578063bda4433114610b5657600080fd5b8063a0d0619311610185578063b276203611610154578063b276203614610a1a578063b4907ddc14610a4e578063b6d40c6614610a97578063b92e87fa14610aaa57600080fd5b8063a0d061931461096b578063a755ecfc1461099f578063a80ebaf1146109d3578063aecd4fb414610a0757600080fd5b806394f0320e116101c157806394f0320e146108ee5780639cae7aa8146109225780639ee3f88314610956578063a08519b51461068957600080fd5b806387265c95146108505780638da5cb5b146108a6578063920f5c84146108bb5780639428ae4e146108db57600080fd5b8063456fd5ef116102e257806364d4c819116102755780637df73e27116102445780637df73e27146107db5780638129fc1c146107fb5780638142d401146108105780638449b2c91461082357600080fd5b806364d4c819146107645780636fbc15e9146107985780637840073a146107b357806378e890ba146107c657600080fd5b80634de992e8116102b15780634de992e8146106dd5780635b502b40146106f05780636043848a146107245780636261b1631461074457600080fd5b8063456fd5ef1461064757806346f0975a1461066757806349926a66146106895780634cf5de19146106a957600080fd5b80632986c0e51161035a5780633a06cc8c116103295780633a06cc8c146105c95780633edd9187146103fd57806343ea1996146105fd578063447f537f1461061357600080fd5b80632986c0e5146105235780632a4bf7941461054d5780632a5d1d661461056d57806333a2d584146105a857600080fd5b8063150b7a0211610396578063150b7a021461043f5780631626ba7e1461049057806316914ac0146104b05780632772aed9146104fc57600080fd5b806301ffc9a7146103c8578063044d3cd2146103fd5780630f0bc5151461041f57600080fd5b366103c357005b600080fd5b3480156103d457600080fd5b506103e86103e336600461423f565b610d49565b60405190151581526020015b60405180910390f35b34801561040957600080fd5b5061041d6104183660046142a1565b610de2565b005b34801561042b57600080fd5b506103e861043a366004614340565b610e50565b34801561044b57600080fd5b5061045f61045a366004614547565b610fdf565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016103f4565b34801561049c57600080fd5b5061045f6104ab3660046145f5565b611009565b3480156104bc57600080fd5b506104e47f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016103f4565b34801561050857600080fd5b50610511611292565b60405160ff90911681526020016103f4565b34801561052f57600080fd5b506105386112a1565b60405163ffffffff90911681526020016103f4565b34801561055957600080fd5b506103e8610568366004614665565b61130e565b34801561057957600080fd5b5061059a6105883660046146dd565b60036020526000908152604090205481565b6040519081526020016103f4565b6105bb6105b63660046147f4565b61137f565b6040516103f49291906148c3565b3480156105d557600080fd5b5061059a7f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e81565b34801561060957600080fd5b5061059a61027a81565b34801561061f57600080fd5b5061059a7f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2081565b34801561065357600080fd5b5061059a6106623660046142a1565b611438565b34801561067357600080fd5b5061067c611453565b6040516103f49190614922565b34801561069557600080fd5b5061041d6106a43660046146dd565b61145d565b3480156106b557600080fd5b5061059a7fc84a2c176321157bd55b70feb5871c4304d99c870656d2fc420998eff645e20781565b6105bb6106eb366004614947565b6114ca565b3480156106fc57600080fd5b5061059a7f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c081565b34801561073057600080fd5b5061041d61073f3660046149cd565b6116ef565b34801561075057600080fd5b506103e861075f366004614a21565b61175e565b34801561077057600080fd5b5061059a7fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647281565b3480156107a457600080fd5b5061041d61073f366004614a80565b6105bb6107c1366004614abc565b6117a3565b3480156107d257600080fd5b5061059a611808565b3480156107e757600080fd5b506103e86107f6366004614b1a565b611833565b34801561080757600080fd5b5061041d61189e565b6105bb61081e366004614abc565b611b2a565b34801561082f57600080fd5b5061084361083e3660046142a1565b611b82565b6040516103f49190614b72565b34801561085c57600080fd5b506108996040518060400160405280601081526020017f41766f6361646f2d4d756c74697369670000000000000000000000000000000081525081565b6040516103f49190614b85565b3480156108b257600080fd5b506104e4611d32565b3480156108c757600080fd5b506103e86108d6366004614b98565b611d96565b6105bb6108e9366004614abc565b611f6a565b3480156108fa57600080fd5b506104e47f000000000000000000000000000000000000000000000000000000000000000081565b34801561092e57600080fd5b506104e47f000000000000000000000000000000000000000000000000000000000000000081565b34801561096257600080fd5b5061059a605a81565b34801561097757600080fd5b5061059a7fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b81565b3480156109ab57600080fd5b5061059a7fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4781565b3480156109df57600080fd5b5061059a7f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6581565b6105bb610a153660046147f4565b611fc0565b348015610a2657600080fd5b506104e47f000000000000000000000000000000000000000000000000000000000000000081565b348015610a5a57600080fd5b506108996040518060400160405280600581526020017f312e312e3000000000000000000000000000000000000000000000000000000081525081565b6105bb610aa53660046147f4565b61206d565b61041d610ab8366004614c73565b61211a565b348015610ac957600080fd5b5061059a7fb7c77dbcd01eff35f637803daf7abe3f0e3b86b5102459be5665dc7d9a85ac5e81565b348015610afd57600080fd5b5061059a610b0c366004614e83565b612288565b348015610b1d57600080fd5b5061045f610b2c366004614f38565b7fbc197c810000000000000000000000000000000000000000000000000000000095945050505050565b348015610b6257600080fd5b5060005474010000000000000000000000000000000000000000900469ffffffffffffffffffff1661059a565b348015610b9b57600080fd5b506104e47f000000000000000000000000000000000000000000000000000000000000000081565b348015610bcf57600080fd5b5061059a610bde366004615056565b612294565b348015610bef57600080fd5b506105116122a0565b348015610c0457600080fd5b5061041d610c13366004614a80565b6122aa565b348015610c2457600080fd5b5061059a610c3336600461509c565b6122b2565b348015610c4457600080fd5b5061059a7f000000000000000000000000000000000000000000000000000000000000000081565b348015610c7857600080fd5b5061041d6106a43660046150d2565b348015610c9357600080fd5b5061059a7f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb81565b348015610cc757600080fd5b5061045f610cd63660046150ed565b7ff23a6e610000000000000000000000000000000000000000000000000000000095945050505050565b348015610d0c57600080fd5b5061059a612305565b348015610d2157600080fd5b5061059a7f000000000000000000000000000000000000000000000000000000000000000081565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e0000000000000000000000000000000000000000000000000000000001480610ddc57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b610dea612330565b610e4b7f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061236b92505050565b505050565b6000468660c0013514610e8f576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610eca610e9c8780615156565b610ea69080615194565b9050610eb28880615156565b60400135606089013560808a013560a08b01356123b7565b610f53610efb610eda8880615156565b610ee3906151fc565b610ef5368a90038a0160208b01615208565b466124ca565b8484808060200260200160405190810160405280939291908181526020016000905b82821015610f4957610f3a60408302860136819003810190615224565b81526020019060010190610f1d565b505050505061253f565b610fd2610fb18484808060200260200160405190810160405280939291908181526020016000905b82821015610fa757610f9860408302860136819003810190615224565b81526020019060010190610f7b565b50505050506125e4565b610fbb8688615256565b610fc58980615156565b6040013560001914612707565b5060019695505050505050565b7f150b7a02000000000000000000000000000000000000000000000000000000005b949350505050565b60006110d26110377f0000000000000000000000000000000000000000000000000000000000000000612759565b604080517f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2060208201529081018790526060015b60408051601f1981840301815282825280516020918201207f19010000000000000000000000000000000000000000000000000000000000008483015260228401949094526042808401949094528151808403909401845260629092019052815191012090565b9350600082900361112a57600084815260026020526040902054600114611125576040517fa8398eb900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611269565b600061122d857f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631bbce23d8787306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561119f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c39190615263565b6040518463ffffffff1660e01b81526004016111e1939291906152ab565b600060405180830381865afa1580156111fe573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611226919081019061531d565b600161286e565b50905080611267576040517fa8398eb900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b507f1626ba7e000000000000000000000000000000000000000000000000000000009392505050565b600061129c612c55565b905090565b600060a0306001600160a01b03166368beab3f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113079190615418565b901c905090565b600061133861131d8680615194565b905086604001358660400135876060013588608001356123b7565b61137361135b611347876151fc565b61135636889003880188615208565b612c83565b6113658486615256565b876040013560001914612707565b50600195945050505050565b60006060468660c00135146113c0576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61142a6113cd8780615156565b87602001878787808060200260200160405190810160405280939291908181526020016000905b828210156114205761141160408302860136819003810190615224565b815260200190600101906113f4565b5050505050612d03565b915091505b94509492505050565b600061144c6114478484611b82565b6125e4565b9392505050565b606061129c612f3d565b611465612330565b6114c67f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061236b92505050565b5050565b6000606060005a90506114f86114e08780615194565b905087604001358760400135886060013560006123b7565b600061151a611506886151fc565b61151536899003890189615431565b61302c565b9050600061153182878a6040013560001914612707565b905061157d8861156361bd7484518c8060a0019061154f919061544d565b600802610172929092029290920101919050565b8a604001356000191461157757600061311c565b8461311c565b909550935084156115ed573361159960a08a0160808b01614b1a565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b836115d160a08d018d61544d565b6040516115e0939291906154b2565b60405180910390a3611650565b336115fe60a08a0160808b01614b1a565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f838761163760a08e018e61544d565b60405161164794939291906154e2565b60405180910390a35b60405160248101849052873560448201526116e3907f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b0382169063984cee41906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061236b565b50505050935093915050565b6116f7612330565b6117587f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061236b92505050565b50505050565b600061178561176d8680615194565b905086604001358660400135876060013560006123b7565b61137361135b611794876151fc565b61151536889003880188615431565b600060606117fb858585856040519080825280602002602001820160405280156117f357816020015b60408051808201909152600080825260208201528152602001906001900390816117cc5790505b50600161343a565b915091505b935093915050565b600061129c7f0000000000000000000000000000000000000000000000000000000000000000612759565b60008061183e612f3d565b805190915060005b8181101561189357846001600160a01b031683828151811061186a5761186a615527565b60200260200101516001600160a01b03160361188b57506001949350505050565b600101611846565b506000949350505050565b6000547f0100000000000000000000000000000000000000000000000000000000000000900460ff16158080156118fd575060005460017e0100000000000000000000000000000000000000000000000000000000000090910460ff16105b806119395750303b15801561193957506000547e01000000000000000000000000000000000000000000000000000000000000900460ff166001145b6119ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790558015611a6357600080547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790555b611ac47f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061236b92505050565b508015611b2757600080547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50565b600060606117fb85858585604051908082528060200260200182016040528015611b7a57816020015b6040805180820190915260008082526020820152815260200190600190039081611b535790505b50600061343a565b6060816002811015611bc0576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff811115611bd957611bd96143fa565b604051908082528060200260200182016040528015611c1e57816020015b6040805180820190915260008082526020820152815260200190600190039081611bf75790505b50915060005b81811015611d2a576040518060400160405280611cd5878785818110611c4c57611c4c615527565b9050602002810190611c5e9190615556565b611c689080615156565b611c71906151fc565b888886818110611c8357611c83615527565b9050602002810190611c959190615556565b602001803603810190611ca89190615208565b898987818110611cba57611cba615527565b9050602002810190611ccc9190615556565b60c001356124ca565b8152602001868684818110611cec57611cec615527565b9050602002810190611cfe9190615556565b60c00135815250838281518110611d1757611d17615527565b6020908102919091010152600101611c24565b505092915050565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d72573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061129c9190615263565b604080518082018252601281527f657865637574654f7065617274696f6e282900000000000000000000000000006020918201529051600091611e01918591859142917f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e910161558a565b60408051601f19818403018152919052805160209091012060365460081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009081169116148015611e5a57506001600160a01b03841630145b611e90576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b603680547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81169091557f0100000000000000000000000000000000000000000000000000000000000000900460ff163261dead03611f4257611f3c7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03811663f7b14fac611f27878901896155d7565b85600160405160240161169c9392919061560c565b50611f59565b611f59611f51848601866155d7565b826001613588565b5060019a9950505050505050505050565b600060606117fb85858585604051908082528060200260200182016040528015611fba57816020015b6040805180820190915260008082526020820152815260200190600190039081611f935790505b50612d03565b60006060468660c0013514612001576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61142a61200e8780615156565b87602001878787808060200260200160405190810160405280939291908181526020016000905b828210156120615761205260408302860136819003810190615224565b81526020019060010190612035565b5050505050600061343a565b60006060468660c00135146120ae576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61142a6120bb8780615156565b87602001878787808060200260200160405190810160405280939291908181526020016000905b8282101561210e576120ff60408302860136819003810190615224565b815260200190600101906120e2565b5050505050600161343a565b3261dead0361218e576117587f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f7b14fac868686600060405160240161169c94939291906157f8565b604080518082018252600e81527f5f63616c6c54617267657473282900000000000000000000000000000000000060209182015290516121f89185918591859142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b9101615824565b60408051601f19818403018152919052805160209091012060365460081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00908116911614612273576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e4b6122808385615877565b826000613588565b600061144c838361302c565b600061144c8383612c83565b600061129c6139b4565b610e4b612330565b600061144c8383808060200260200160405190810160405280939291908181526020016000905b82821015610fa7576122f660408302860136819003810190615224565b815260200190600101906122d9565b600061129c7f0000000000000000000000000000000000000000000000000000000000000000612759565b333014612369576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6060600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e8115600181036123ae57816000803e816000fd5b50505092915050565b6000851180156123f757508360001914806123f7575060005474010000000000000000000000000000000000000000900469ffffffffffffffffffff1684145b61242d576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b42831180612445575060008211801561244557504282105b1561247c576040517f3c83d07f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008111801561248c5750803414155b156124c3576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b60007fc84a2c176321157bd55b70feb5871c4304d99c870656d2fc420998eff645e2076124f6856139e2565b6124ff85613bd4565b60408051602081019490945283019190915260608201526080810183905260a0016040516020818303038152906040528051906020012090509392505050565b805160018111156125b25760005b818110156125b05782818151811061256757612567615527565b6020026020010151600001518414801561259d575082818151811061258e5761258e615527565b60200260200101516020015146145b156125a85750505050565b60010161254d565b505b6040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600090606080835b83811015612681578286828151811061260957612609615527565b602002602001015160000151604051602001612626929190615884565b60405160208183030381529060405292508186828151811061264a5761264a615527565b602002602001015160200151604051602001612667929190615884565b60408051601f1981840301815291905291506001016125ee565b506126fe6126ae7f0000000000000000000000000000000000000000000000000000000000000000612759565b8351602080860191909120845185830120604080517fb7c77dbcd01eff35f637803daf7abe3f0e3b86b5102459be5665dc7d9a85ac5e94810194909452830191909152606082015260800161106b565b95945050505050565b6060600061271685858561286e565b9250905080612751576040517f70a1717900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509392505050565b604080518082018252601081527f41766f6361646f2d4d756c74697369670000000000000000000000000000000060209182015281518083018352600581527f312e312e300000000000000000000000000000000000000000000000000000009082015281517fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472918101919091527ff5d974efd8e94277ef83fff53580c07a80e6e3ffdb2e3fec63185a4e812655a2918101919091527f6815ba53416ba06aff1932cc76b3832272bafab9bc8e066be382e32b06ba5546606082015261027a60808201523060a082015260c0810182905260009060e0015b604051602081830303815290604052805190602001209050919050565b815160009060609061287e612c55565b60ff168110806128a457508380156128a457506000868152600360205260409020546001145b156128db576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff8111156128f4576128f46143fa565b60405190808252806020026020018201604052801561291d578160200160208202803683370190505b509150600061292a612f3d565b805190915060008080805b86811015612c425761296d8b828151811061295257612952615527565b6020026020010151602001516001600160a01b03163b151590565b156129cb578a818151811061298457612984615527565b6020026020010151602001518882815181106129a2576129a2615527565b60200260200101906001600160a01b031690816001600160a01b03168152505060019250612aa5565b6129f28c8c83815181106129e1576129e1615527565b602002602001015160000151613c48565b888281518110612a0457612a04615527565b60200260200101906001600160a01b031690816001600160a01b031681525050878181518110612a3657612a36615527565b60200260200101516001600160a01b03168b8281518110612a5957612a59615527565b6020026020010151602001516001600160a01b031614612aa5576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b835b85811015612b1157888281518110612ac157612ac1615527565b60200260200101516001600160a01b0316878281518110612ae457612ae4615527565b60200260200101516001600160a01b031603612b095760019250806001019450612b11565b600101612aa7565b5081612b27576000985050505050505050611800565b600091508215612c3a578a517f1626ba7e00000000000000000000000000000000000000000000000000000000908c9083908110612b6757612b67615527565b6020026020010151602001516001600160a01b0316631626ba7e8e8e8581518110612b9457612b94615527565b6020026020010151600001516040518363ffffffff1660e01b8152600401612bbd9291906158a6565b602060405180830381865afa158015612bda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bfe91906158bf565b7fffffffff000000000000000000000000000000000000000000000000000000001614612c35576000985050505050505050611800565b600092505b600101612935565b5060019750505050505050935093915050565b60015474010000000000000000000000000000000000000000900460ff166000819003612c80575060015b90565b600061144c612cb17f0000000000000000000000000000000000000000000000000000000000000000612759565b7fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b612cdb866139e2565b612ce486613bd4565b604080516020810194909452830191909152606082015260800161106b565b60006060336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614612d69576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84355a6101f4011015612da8576040517fe681bf7d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612dd0612db58780615194565b905087604001358760400135886060013589608001356123b7565b825160009015612e0e57612dfe612df8612de9896151fc565b610ef5368a90038a018a615208565b8561253f565b612e07846125e4565b9050612e2c565b612e29612e1a886151fc565b61135636899003890189615208565b90505b6000612e4182878a6040013560001914612707565b9050612e5f8861156361271084518c8060a0019061154f919061544d565b90945092508315612ecf5733612e7b60a08a0160808b01614b1a565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b83612eb360a08d018d61544d565b604051612ec2939291906154b2565b60405180910390a3612f32565b33612ee060a08a0160808b01614b1a565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f8386612f1960a08e018e61544d565b604051612f2994939291906154e2565b60405180910390a35b505094509492505050565b6001546060906001600160a01b03168061300a576040805160018082528183019092529060208083019080368337019050509150306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612faf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fd39190615263565b82600081518110612fe657612fe6615527565b60200260200101906001600160a01b031690816001600160a01b0316815250505090565b61301381613c64565b8060200190518101906130269190615940565b91505090565b600061144c61305a7f0000000000000000000000000000000000000000000000000000000000000000612759565b7f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c0613084866139e2565b85516020808801516040808a015160608b015191516130e7957f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb959094939101948552602085019390935260408401919091526060830152608082015260a00190565b60408051601f19818403018152828252805160209182012090830194909452810191909152606081019190915260800161106b565b6000606061312a8580615194565b604080518082018252600e81527f5f63616c6c5461726765747328290000000000000000000000000000000000006020918201529051613195939291808a01359142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b9101615824565b60408051808303601f190181529190528051602090910120603680547fff000000000000000000000000000000000000000000000000000000000000001660089290921c91909117905582613248576000805474010000000000000000000000000000000000000000900469ffffffffffffffffffff16906014613218836159a4565b91906101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff1602179055505061325b565b6000838152600360205260409020600190555b60006132678680615194565b876020013560405160240161327e939291906159cd565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb92e87fa0000000000000000000000000000000000000000000000000000000017905290506060609686015a1015613332576132ef6001603655565b60006040518060400160405280600f81526020017f41564f5f5f4f55545f4f465f4741530000000000000000000000000000000000815250935093505050611800565b6000808351602085016001600160a01b03600054168a5a03f493503d6040519150601f19601f6020830101168201604052808252806000602084013e5083613426578051600003613409576133886096876159f1565b5a10156133cc576040518060400160405280600f81526020017f41564f5f5f4f55545f4f465f47415300000000000000000000000000000000008152509250613426565b6040518060400160405280601781526020017f41564f5f5f524541534f4e5f4e4f545f444546494e45440000000000000000008152509250613426565b600481019050808060200190518101906134239190615a04565b92505b6134306001603655565b5050935093915050565b6000606061dead3314806134825750336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148015613482575061dead32145b6134b8576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6135227f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633e0cd8978a8a8a8a60405160240161169c9493929190615aad565b8060200190518101906135359190615c10565b9092509050828015613545575081155b1561357e57806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119c19190614b85565b9550959350505050565b6135926001603655565b600080600084600114806135a65750846015145b905080156135b957600054925060015491505b855160005b818110156139aa5760008882815181106135da576135da615527565b6020026020010151905060006060600083606001516000148015613613575060028b108061360857508a6014145b8061361357508a6015145b156136ad5760405a8161362857613628615c66565b04905083600001516001600160a01b0316846040015185602001516040516136509190615c95565b60006040518083038185875af1925050503d806000811461368d576040519150601f19603f3d011682016040523d82523d6000602084013e613692565b606091505b509093509150826136a8576136a8818684613c85565b61399b565b836060015160011480156136be5750865b156137ce5760405a816136d3576136d3615c66565b04905083600001516001600160a01b031684602001516040516136f69190615c95565b600060405180830381855af49150503d8060008114613731576040519150601f19603f3d011682016040523d82523d6000602084013e613736565b606091505b5090935091508261374c5761374c818684613c85565b6137566001603655565b6000546001548a8214801561376a57508981145b6137c75761377787613db3565b6040516020016137879190615ca7565b60408051601f19818403018152908290527f08c379a00000000000000000000000000000000000000000000000000000000082526119c191600401614b85565b505061399b565b836060015160021480156137ec57508a601414806137ec57508a6015145b156139825789156138105761380085613db3565b6040516020016137879190615ce8565b6020840151600481018051909161382f91810160249081019101615d29565b50604080518082018252601281527f657865637574654f7065617274696f6e28290000000000000000000000000000602091820152905191955061389c94508593504292507f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e9101615e28565b60408051808303601f19018152918152815160209092019190912060081c7f010000000000000000000000000000000000000000000000000000000000000060ff8f1602176036555a816138f2576138f2615c66565b04915084600001516001600160a01b03168560400151866020015160405161391a9190615c95565b60006040518083038185875af1925050503d8060008114613957576040519150601f19603f3d011682016040523d82523d6000602084013e61395c565b606091505b5090945092508361397257613972828785613c85565b61397c6001603655565b5061399b565b61398b85613db3565b6040516020016137879190615e73565b846001019450505050506135be565b5050505050505050565b6001547501000000000000000000000000000000000000000000900460ff166000819003612c805750600190565b8051516000906060825b82811015613b2157817f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6586600001518381518110613a2c57613a2c615527565b60200260200101516000015187600001518481518110613a4e57613a4e615527565b6020026020010151602001518051906020012088600001518581518110613a7757613a77615527565b60200260200101516040015189600001518681518110613a9957613a99615527565b602002602001015160600151604051602001613ae09594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120604051602001613b07929190615884565b60408051601f1981840301815291905291506001016139ec565b507fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc47818051906020012085602001518660400151876060015188608001518960a0015180519060200120604051602001613bb5979695949392919096875260208701959095526040860193909352606085019190915260808401526001600160a01b031660a083015260c082015260e00190565b6040516020818303038152906040528051906020012092505050919050565b60007f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e8260000151836020015184604001518560600151866080015160405160200161285196959493929190958652602086019490945260408501929092526060840152608083015260a082015260c00190565b6000806000613c578585613e53565b9150915061275181613e98565b6060610ddc826001613c80816001600160a01b0384163b6159f1565b61404b565b825a1015613cef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f41564f5f5f4f55545f4f465f474153000000000000000000000000000000000060448201526064016119c1565b613cf882613db3565b6040517f2b5378750000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632b53787590613d5d908590600401614b85565b600060405180830381865afa158015613d7a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613da29190810190615a04565b604051602001613787929190615eb4565b60606000613dc08361406e565b600101905060008167ffffffffffffffff811115613de057613de06143fa565b6040519080825280601f01601f191660200182016040528015613e0a576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a8504945084613e1457509392505050565b6000808251604103613e895760208301516040840151606085015160001a613e7d87828585614150565b94509450505050613e91565b506000905060025b9250929050565b6000816004811115613eac57613eac615ee3565b03613eb45750565b6001816004811115613ec857613ec8615ee3565b03613f2f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016119c1565b6002816004811115613f4357613f43615ee3565b03613faa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016119c1565b6003816004811115613fbe57613fbe615ee3565b03611b27576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f756500000000000000000000000000000000000000000000000000000000000060648201526084016119c1565b60408051603f8301601f19168101909152818152818360208301863c9392505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083106140b7577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef810000000083106140e3576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061410157662386f26fc10000830492506010015b6305f5e1008310614119576305f5e100830492506008015b612710831061412d57612710830492506004015b6064831061413f576064830492506002015b600a8310610ddc5760010192915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115614187575060009050600361142f565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156141db573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166142045760006001925092505061142f565b9660009650945050505050565b7fffffffff0000000000000000000000000000000000000000000000000000000081168114611b2757600080fd5b60006020828403121561425157600080fd5b813561144c81614211565b60008083601f84011261426e57600080fd5b50813567ffffffffffffffff81111561428657600080fd5b6020830191508360208260051b8501011115613e9157600080fd5b600080602083850312156142b457600080fd5b823567ffffffffffffffff8111156142cb57600080fd5b6142d78582860161425c565b90969095509350505050565b600060e082840312156142f557600080fd5b50919050565b60008083601f84011261430d57600080fd5b50813567ffffffffffffffff81111561432557600080fd5b6020830191508360208260061b8501011115613e9157600080fd5b60008060008060006060868803121561435857600080fd5b853567ffffffffffffffff8082111561437057600080fd5b61437c89838a016142e3565b9650602088013591508082111561439257600080fd5b61439e89838a0161425c565b909650945060408801359150808211156143b757600080fd5b506143c4888289016142fb565b969995985093965092949392505050565b6001600160a01b0381168114611b2757600080fd5b80356143f5816143d5565b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561444c5761444c6143fa565b60405290565b6040516080810167ffffffffffffffff8111828210171561444c5761444c6143fa565b60405160c0810167ffffffffffffffff8111828210171561444c5761444c6143fa565b604051601f8201601f1916810167ffffffffffffffff811182821017156144c1576144c16143fa565b604052919050565b600067ffffffffffffffff8211156144e3576144e36143fa565b50601f01601f191660200190565b600082601f83011261450257600080fd5b8135614515614510826144c9565b614498565b81815284602083860101111561452a57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561455d57600080fd5b8435614568816143d5565b93506020850135614578816143d5565b925060408501359150606085013567ffffffffffffffff81111561459b57600080fd5b6145a7878288016144f1565b91505092959194509250565b60008083601f8401126145c557600080fd5b50813567ffffffffffffffff8111156145dd57600080fd5b602083019150836020828501011115613e9157600080fd5b60008060006040848603121561460a57600080fd5b83359250602084013567ffffffffffffffff81111561462857600080fd5b614634868287016145b3565b9497909650939450505050565b600060c082840312156142f557600080fd5b600060a082840312156142f557600080fd5b60008060008060e0858703121561467b57600080fd5b843567ffffffffffffffff8082111561469357600080fd5b61469f88838901614641565b95506146ae8860208901614653565b945060c08701359150808211156146c457600080fd5b506146d18782880161425c565b95989497509550505050565b6000602082840312156146ef57600080fd5b5035919050565b600067ffffffffffffffff821115614710576147106143fa565b5060051b60200190565b6000614728614510846146f6565b8381529050602080820190600585901b84018681111561474757600080fd5b845b818110156147c957803567ffffffffffffffff8082111561476a5760008081fd5b908701906040828b03121561477f5760008081fd5b614787614429565b8235828111156147975760008081fd5b6147a38c8286016144f1565b82525091850135916147b4836143d5565b80860192909252508452928201928201614749565b505050509392505050565b600082601f8301126147e557600080fd5b61144c8383356020850161471a565b6000806000806060858703121561480a57600080fd5b843567ffffffffffffffff8082111561482257600080fd5b61482e888389016142e3565b9550602087013591508082111561484457600080fd5b614850888389016147d4565b9450604087013591508082111561486657600080fd5b506146d1878288016142fb565b60005b8381101561488e578181015183820152602001614876565b50506000910152565b600081518084526148af816020860160208601614873565b601f01601f19169290920160200192915050565b82151581526040602082015260006110016040830184614897565b600081518084526020808501945080840160005b838110156149175781516001600160a01b0316875295820195908201906001016148f2565b509495945050505050565b60208152600061144c60208301846148de565b6000608082840312156142f557600080fd5b600080600060c0848603121561495c57600080fd5b833567ffffffffffffffff8082111561497457600080fd5b61498087838801614641565b945061498f8760208801614935565b935060a08601359150808211156149a557600080fd5b506149b2868287016147d4565b9150509250925092565b803560ff811681146143f557600080fd5b6000806000604084860312156149e257600080fd5b833567ffffffffffffffff8111156149f957600080fd5b614a058682870161425c565b9094509250614a189050602085016149bc565b90509250925092565b60008060008060c08587031215614a3757600080fd5b843567ffffffffffffffff80821115614a4f57600080fd5b614a5b88838901614641565b9550614a6a8860208901614935565b945060a08701359150808211156146c457600080fd5b600080600060408486031215614a9557600080fd5b8335614aa0816143d5565b9250602084013567ffffffffffffffff81111561462857600080fd5b600080600060e08486031215614ad157600080fd5b833567ffffffffffffffff80821115614ae957600080fd5b614af587838801614641565b9450614b048760208801614653565b935060c08601359150808211156149a557600080fd5b600060208284031215614b2c57600080fd5b813561144c816143d5565b600081518084526020808501945080840160005b83811015614917578151805188528301518388015260409096019590820190600101614b4b565b60208152600061144c6020830184614b37565b60208152600061144c6020830184614897565b600080600080600080600080600060a08a8c031215614bb657600080fd5b893567ffffffffffffffff80821115614bce57600080fd5b614bda8d838e0161425c565b909b50995060208c0135915080821115614bf357600080fd5b614bff8d838e0161425c565b909950975060408c0135915080821115614c1857600080fd5b614c248d838e0161425c565b909750955060608c01359150614c39826143d5565b90935060808b01359080821115614c4f57600080fd5b50614c5c8c828d016145b3565b915080935050809150509295985092959850929598565b600080600060408486031215614c8857600080fd5b833567ffffffffffffffff811115614c9f57600080fd5b614cab8682870161425c565b909790965060209590950135949350505050565b6000614ccd614510846146f6565b8381529050602080820190600585901b840186811115614cec57600080fd5b845b818110156147c957803567ffffffffffffffff80821115614d0f5760008081fd5b908701906080828b031215614d245760008081fd5b614d2c614452565b8235614d37816143d5565b81528286013582811115614d4b5760008081fd5b614d578c8286016144f1565b82880152506040838101359082015260609283013592810192909252508452928201928201614cee565b600082601f830112614d9257600080fd5b61144c83833560208501614cbf565b600060c08284031215614db357600080fd5b614dbb614475565b9050813567ffffffffffffffff80821115614dd557600080fd5b614de185838601614d81565b8352602084013560208401526040840135604084015260608401356060840152614e0d608085016143ea565b608084015260a0840135915080821115614e2657600080fd5b50614e33848285016144f1565b60a08301525092915050565b600060808284031215614e5157600080fd5b614e59614452565b90508135815260208201356020820152604082013560408201526060820135606082015292915050565b60008060a08385031215614e9657600080fd5b823567ffffffffffffffff811115614ead57600080fd5b614eb985828601614da1565b925050614ec98460208501614e3f565b90509250929050565b600082601f830112614ee357600080fd5b81356020614ef3614510836146f6565b82815260059290921b84018101918181019086841115614f1257600080fd5b8286015b84811015614f2d5780358352918301918301614f16565b509695505050505050565b600080600080600060a08688031215614f5057600080fd5b8535614f5b816143d5565b94506020860135614f6b816143d5565b9350604086013567ffffffffffffffff80821115614f8857600080fd5b614f9489838a01614ed2565b94506060880135915080821115614faa57600080fd5b614fb689838a01614ed2565b93506080880135915080821115614fcc57600080fd5b50614fd9888289016144f1565b9150509295509295909350565b600060a08284031215614ff857600080fd5b60405160a0810181811067ffffffffffffffff8211171561501b5761501b6143fa565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b60008060c0838503121561506957600080fd5b823567ffffffffffffffff81111561508057600080fd5b61508c85828601614da1565b925050614ec98460208501614fe6565b600080602083850312156150af57600080fd5b823567ffffffffffffffff8111156150c657600080fd5b6142d7858286016142fb565b6000602082840312156150e457600080fd5b61144c826149bc565b600080600080600060a0868803121561510557600080fd5b8535615110816143d5565b94506020860135615120816143d5565b93506040860135925060608601359150608086013567ffffffffffffffff81111561514a57600080fd5b614fd9888289016144f1565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261518a57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126151c957600080fd5b83018035915067ffffffffffffffff8211156151e457600080fd5b6020019150600581901b3603821315613e9157600080fd5b6000610ddc3683614da1565b600060a0828403121561521a57600080fd5b61144c8383614fe6565b60006040828403121561523657600080fd5b61523e614429565b82358152602083013560208201528091505092915050565b600061144c36848461471a565b60006020828403121561527557600080fd5b815161144c816143d5565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6040815260006152bf604083018587615280565b90506001600160a01b0383166020830152949350505050565b600082601f8301126152e957600080fd5b81516152f7614510826144c9565b81815284602083860101111561530c57600080fd5b611001826020830160208701614873565b6000602080838503121561533057600080fd5b825167ffffffffffffffff8082111561534857600080fd5b818501915085601f83011261535c57600080fd5b815161536a614510826146f6565b81815260059190911b8301840190848101908883111561538957600080fd5b8585015b8381101561540b578051858111156153a55760008081fd5b86016040818c03601f19018113156153bd5760008081fd5b6153c5614429565b89830151888111156153d75760008081fd5b6153e58e8c838701016152d8565b82525091810151916153f6836143d5565b808a019290925250835291860191860161538d565b5098975050505050505050565b60006020828403121561542a57600080fd5b5051919050565b60006080828403121561544357600080fd5b61144c8383614e3f565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261548257600080fd5b83018035915067ffffffffffffffff82111561549d57600080fd5b602001915036819003821315613e9157600080fd5b6040815260006154c560408301866148de565b82810360208401526154d8818587615280565b9695505050505050565b6060815260006154f560608301876148de565b82810360208401526155078187614897565b9050828103604084015261551c818587615280565b979650505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2183360301811261518a57600080fd5b60608152600061559e606083018688615280565b90508360208301527fffffffff000000000000000000000000000000000000000000000000000000008316604083015295945050505050565b6000602082840312156155e957600080fd5b813567ffffffffffffffff81111561560057600080fd5b61100184828501614d81565b60006060808301818452808751808352608092508286019150828160051b8701016020808b0160005b848110156156af577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808a850301865281516001600160a01b03815116855283810151888587015261568889870182614897565b60408381015190880152918a0151958a019590955295830195935090820190600101615635565b50508701899052871515604088015294506110019350505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126156ff57600080fd5b830160208101925035905067ffffffffffffffff81111561571f57600080fd5b803603821315613e9157600080fd5b81835260006020808501808196508560051b81019150846000805b888110156157ea578385038a5282357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81893603018112615787578283fd5b880160808135615796816143d5565b6001600160a01b031687526157ad828901836156ca565b828a8a01526157bf838a018284615280565b604085810135908b015260609485013594909901939093525050509885019891850191600101615749565b509298975050505050505050565b60608152600061580c60608301868861572e565b60208301949094525090151560409091015292915050565b60808152600061583860808301878961572e565b60208301959095525060408101929092527fffffffff000000000000000000000000000000000000000000000000000000001660609091015292915050565b600061144c368484614cbf565b60008351615896818460208801614873565b9190910191825250602001919050565b8281526040602082015260006110016040830184614897565b6000602082840312156158d157600080fd5b815161144c81614211565b600082601f8301126158ed57600080fd5b815160206158fd614510836146f6565b82815260059290921b8401810191818101908684111561591c57600080fd5b8286015b84811015614f2d578051615933816143d5565b8352918301918301615920565b60006020828403121561595257600080fd5b815167ffffffffffffffff81111561596957600080fd5b611001848285016158dc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600069ffffffffffffffffffff8083168181036159c3576159c3615975565b6001019392505050565b6040815260006159e160408301858761572e565b9050826020830152949350505050565b81810381811115610ddc57610ddc615975565b600060208284031215615a1657600080fd5b815167ffffffffffffffff811115615a2d57600080fd5b611001848285016152d8565b600081518084526020808501808196508360051b8101915082860160005b85811015615aa0578284038952815160408151818752615a7982880182614897565b928801516001600160a01b0316968801969096525098850198935090840190600101615a57565b5091979650505050505050565b600061010080835286357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1883603018112615ae757600080fd5b870160208101903567ffffffffffffffff811115615b0457600080fd5b8060051b3603821315615b1657600080fd5b60c083860152615b2b6101c08601828461572e565b92505050602087013561012084015260408701356101408401526060870135610160840152615b5c608088016143ea565b6001600160a01b0316610180840152615b7860a08801886156ca565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00858403016101a0860152615bae838284615280565b92505050615bea602084018780358252602081013560208301526040810135604083015260608101356060830152608081013560808301525050565b82810360c0840152615bfc8186615a39565b905082810360e084015261551c8185614b37565b60008060408385031215615c2357600080fd5b82518015158114615c3357600080fd5b602084015190925067ffffffffffffffff811115615c5057600080fd5b615c5c858286016152d8565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000825161518a818460208701614873565b60008251615cb9818460208701614873565b7f5f41564f5f5f4d4f4449464945445f53544f5241474500000000000000000000920191825250601601919050565b60008251615cfa818460208701614873565b7f5f41564f5f5f4e4f5f464c4153484c4f414e5f494e5f464c4153484c4f414e00920191825250601f01919050565b600080600080600060a08688031215615d4157600080fd5b855167ffffffffffffffff80821115615d5957600080fd5b615d6589838a016158dc565b9650602091508188015181811115615d7c57600080fd5b8801601f81018a13615d8d57600080fd5b8051615d9b614510826146f6565b81815260059190911b8201840190848101908c831115615dba57600080fd5b928501925b82841015615dd857835182529285019290850190615dbf565b60408c015160608d0151919a509850945050505080821115615df957600080fd5b615e0589838a016152d8565b93506080880151915080821115615e1b57600080fd5b50614fd9888289016152d8565b606081526000615e3b6060830186614897565b90508360208301527fffffffff0000000000000000000000000000000000000000000000000000000083166040830152949350505050565b60008251615e85818460208701614873565b7f5f41564f5f5f494e56414c49445f49445f4f525f4f5045524154494f4e000000920191825250601d01919050565b60008351615ec6818460208801614873565b835190830190615eda818360208801614873565b01949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea2646970667358221220074a82e1d408a622c861eb428960df3f50376d1b0125d8c604e38f13d30eaa1b64736f6c63430008120033000000000000000000000000779385ec7a04242259add4990e3130846f80ea6900000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068480000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef
Deployed Bytecode
0x6080604052600436106103bc5760003560e01c806387265c95116101f2578063bb1c006b1161010d578063e8a9a4b5116100a0578063ed969f0a1161006f578063ed969f0a14610c87578063f23a6e6114610cbb578063f28dd30614610d00578063fe6bfed314610d1557600080fd5b8063e8a9a4b514610c18578063eb2856e914610724578063ec59048314610c38578063ec80dcc214610c6c57600080fd5b8063c7fe944f116100dc578063c7fe944f14610b8f578063e3a8897014610bc3578063e40956b114610be3578063e7e38cd314610bf857600080fd5b8063bb1c006b14610abd578063bb6fe03d14610af1578063bc197c8114610b11578063bda4433114610b5657600080fd5b8063a0d0619311610185578063b276203611610154578063b276203614610a1a578063b4907ddc14610a4e578063b6d40c6614610a97578063b92e87fa14610aaa57600080fd5b8063a0d061931461096b578063a755ecfc1461099f578063a80ebaf1146109d3578063aecd4fb414610a0757600080fd5b806394f0320e116101c157806394f0320e146108ee5780639cae7aa8146109225780639ee3f88314610956578063a08519b51461068957600080fd5b806387265c95146108505780638da5cb5b146108a6578063920f5c84146108bb5780639428ae4e146108db57600080fd5b8063456fd5ef116102e257806364d4c819116102755780637df73e27116102445780637df73e27146107db5780638129fc1c146107fb5780638142d401146108105780638449b2c91461082357600080fd5b806364d4c819146107645780636fbc15e9146107985780637840073a146107b357806378e890ba146107c657600080fd5b80634de992e8116102b15780634de992e8146106dd5780635b502b40146106f05780636043848a146107245780636261b1631461074457600080fd5b8063456fd5ef1461064757806346f0975a1461066757806349926a66146106895780634cf5de19146106a957600080fd5b80632986c0e51161035a5780633a06cc8c116103295780633a06cc8c146105c95780633edd9187146103fd57806343ea1996146105fd578063447f537f1461061357600080fd5b80632986c0e5146105235780632a4bf7941461054d5780632a5d1d661461056d57806333a2d584146105a857600080fd5b8063150b7a0211610396578063150b7a021461043f5780631626ba7e1461049057806316914ac0146104b05780632772aed9146104fc57600080fd5b806301ffc9a7146103c8578063044d3cd2146103fd5780630f0bc5151461041f57600080fd5b366103c357005b600080fd5b3480156103d457600080fd5b506103e86103e336600461423f565b610d49565b60405190151581526020015b60405180910390f35b34801561040957600080fd5b5061041d6104183660046142a1565b610de2565b005b34801561042b57600080fd5b506103e861043a366004614340565b610e50565b34801561044b57600080fd5b5061045f61045a366004614547565b610fdf565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016103f4565b34801561049c57600080fd5b5061045f6104ab3660046145f5565b611009565b3480156104bc57600080fd5b506104e47f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc00684881565b6040516001600160a01b0390911681526020016103f4565b34801561050857600080fd5b50610511611292565b60405160ff90911681526020016103f4565b34801561052f57600080fd5b506105386112a1565b60405163ffffffff90911681526020016103f4565b34801561055957600080fd5b506103e8610568366004614665565b61130e565b34801561057957600080fd5b5061059a6105883660046146dd565b60036020526000908152604090205481565b6040519081526020016103f4565b6105bb6105b63660046147f4565b61137f565b6040516103f49291906148c3565b3480156105d557600080fd5b5061059a7f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e81565b34801561060957600080fd5b5061059a61027a81565b34801561061f57600080fd5b5061059a7f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2081565b34801561065357600080fd5b5061059a6106623660046142a1565b611438565b34801561067357600080fd5b5061067c611453565b6040516103f49190614922565b34801561069557600080fd5b5061041d6106a43660046146dd565b61145d565b3480156106b557600080fd5b5061059a7fc84a2c176321157bd55b70feb5871c4304d99c870656d2fc420998eff645e20781565b6105bb6106eb366004614947565b6114ca565b3480156106fc57600080fd5b5061059a7f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c081565b34801561073057600080fd5b5061041d61073f3660046149cd565b6116ef565b34801561075057600080fd5b506103e861075f366004614a21565b61175e565b34801561077057600080fd5b5061059a7fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647281565b3480156107a457600080fd5b5061041d61073f366004614a80565b6105bb6107c1366004614abc565b6117a3565b3480156107d257600080fd5b5061059a611808565b3480156107e757600080fd5b506103e86107f6366004614b1a565b611833565b34801561080757600080fd5b5061041d61189e565b6105bb61081e366004614abc565b611b2a565b34801561082f57600080fd5b5061084361083e3660046142a1565b611b82565b6040516103f49190614b72565b34801561085c57600080fd5b506108996040518060400160405280601081526020017f41766f6361646f2d4d756c74697369670000000000000000000000000000000081525081565b6040516103f49190614b85565b3480156108b257600080fd5b506104e4611d32565b3480156108c757600080fd5b506103e86108d6366004614b98565b611d96565b6105bb6108e9366004614abc565b611f6a565b3480156108fa57600080fd5b506104e47f00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a5481565b34801561092e57600080fd5b506104e47f000000000000000000000000779385ec7a04242259add4990e3130846f80ea6981565b34801561096257600080fd5b5061059a605a81565b34801561097757600080fd5b5061059a7fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b81565b3480156109ab57600080fd5b5061059a7fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4781565b3480156109df57600080fd5b5061059a7f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6581565b6105bb610a153660046147f4565b611fc0565b348015610a2657600080fd5b506104e47f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef81565b348015610a5a57600080fd5b506108996040518060400160405280600581526020017f312e312e3000000000000000000000000000000000000000000000000000000081525081565b6105bb610aa53660046147f4565b61206d565b61041d610ab8366004614c73565b61211a565b348015610ac957600080fd5b5061059a7fb7c77dbcd01eff35f637803daf7abe3f0e3b86b5102459be5665dc7d9a85ac5e81565b348015610afd57600080fd5b5061059a610b0c366004614e83565b612288565b348015610b1d57600080fd5b5061045f610b2c366004614f38565b7fbc197c810000000000000000000000000000000000000000000000000000000095945050505050565b348015610b6257600080fd5b5060005474010000000000000000000000000000000000000000900469ffffffffffffffffffff1661059a565b348015610b9b57600080fd5b506104e47f000000000000000000000000e8385fb3a5f15ded06eb5e20e5a81bf43115eb8e81565b348015610bcf57600080fd5b5061059a610bde366004615056565b612294565b348015610bef57600080fd5b506105116122a0565b348015610c0457600080fd5b5061041d610c13366004614a80565b6122aa565b348015610c2457600080fd5b5061059a610c3336600461509c565b6122b2565b348015610c4457600080fd5b5061059a7f0000000000000000000000000000000000000000000000000001c6bf5263400081565b348015610c7857600080fd5b5061041d6106a43660046150d2565b348015610c9357600080fd5b5061059a7f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb81565b348015610cc757600080fd5b5061045f610cd63660046150ed565b7ff23a6e610000000000000000000000000000000000000000000000000000000095945050505050565b348015610d0c57600080fd5b5061059a612305565b348015610d2157600080fd5b5061059a7f00000000000000000000000000000000000000000000000000b1a2bc2ec5000081565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e0000000000000000000000000000000000000000000000000000000001480610ddc57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b610dea612330565b610e4b7f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061236b92505050565b505050565b6000468660c0013514610e8f576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610eca610e9c8780615156565b610ea69080615194565b9050610eb28880615156565b60400135606089013560808a013560a08b01356123b7565b610f53610efb610eda8880615156565b610ee3906151fc565b610ef5368a90038a0160208b01615208565b466124ca565b8484808060200260200160405190810160405280939291908181526020016000905b82821015610f4957610f3a60408302860136819003810190615224565b81526020019060010190610f1d565b505050505061253f565b610fd2610fb18484808060200260200160405190810160405280939291908181526020016000905b82821015610fa757610f9860408302860136819003810190615224565b81526020019060010190610f7b565b50505050506125e4565b610fbb8688615256565b610fc58980615156565b6040013560001914612707565b5060019695505050505050565b7f150b7a02000000000000000000000000000000000000000000000000000000005b949350505050565b60006110d26110377f6e452848784197f00927d379e3db9e69a5131d2269f862bfcd05a0b38f6abf7f612759565b604080517f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2060208201529081018790526060015b60408051601f1981840301815282825280516020918201207f19010000000000000000000000000000000000000000000000000000000000008483015260228401949094526042808401949094528151808403909401845260629092019052815191012090565b9350600082900361112a57600084815260026020526040902054600114611125576040517fa8398eb900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611269565b600061122d857f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef6001600160a01b0316631bbce23d8787306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561119f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c39190615263565b6040518463ffffffff1660e01b81526004016111e1939291906152ab565b600060405180830381865afa1580156111fe573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611226919081019061531d565b600161286e565b50905080611267576040517fa8398eb900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b507f1626ba7e000000000000000000000000000000000000000000000000000000009392505050565b600061129c612c55565b905090565b600060a0306001600160a01b03166368beab3f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113079190615418565b901c905090565b600061133861131d8680615194565b905086604001358660400135876060013588608001356123b7565b61137361135b611347876151fc565b61135636889003880188615208565b612c83565b6113658486615256565b876040013560001914612707565b50600195945050505050565b60006060468660c00135146113c0576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61142a6113cd8780615156565b87602001878787808060200260200160405190810160405280939291908181526020016000905b828210156114205761141160408302860136819003810190615224565b815260200190600101906113f4565b5050505050612d03565b915091505b94509492505050565b600061144c6114478484611b82565b6125e4565b9392505050565b606061129c612f3d565b611465612330565b6114c67f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061236b92505050565b5050565b6000606060005a90506114f86114e08780615194565b905087604001358760400135886060013560006123b7565b600061151a611506886151fc565b61151536899003890189615431565b61302c565b9050600061153182878a6040013560001914612707565b905061157d8861156361bd7484518c8060a0019061154f919061544d565b600802610172929092029290920101919050565b8a604001356000191461157757600061311c565b8461311c565b909550935084156115ed573361159960a08a0160808b01614b1a565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b836115d160a08d018d61544d565b6040516115e0939291906154b2565b60405180910390a3611650565b336115fe60a08a0160808b01614b1a565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f838761163760a08e018e61544d565b60405161164794939291906154e2565b60405180910390a35b60405160248101849052873560448201526116e3907f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef906001600160a01b0382169063984cee41906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061236b565b50505050935093915050565b6116f7612330565b6117587f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061236b92505050565b50505050565b600061178561176d8680615194565b905086604001358660400135876060013560006123b7565b61137361135b611794876151fc565b61151536889003880188615431565b600060606117fb858585856040519080825280602002602001820160405280156117f357816020015b60408051808201909152600080825260208201528152602001906001900390816117cc5790505b50600161343a565b915091505b935093915050565b600061129c7f6e452848784197f00927d379e3db9e69a5131d2269f862bfcd05a0b38f6abf7f612759565b60008061183e612f3d565b805190915060005b8181101561189357846001600160a01b031683828151811061186a5761186a615527565b60200260200101516001600160a01b03160361188b57506001949350505050565b600101611846565b506000949350505050565b6000547f0100000000000000000000000000000000000000000000000000000000000000900460ff16158080156118fd575060005460017e0100000000000000000000000000000000000000000000000000000000000090910460ff16105b806119395750303b15801561193957506000547e01000000000000000000000000000000000000000000000000000000000000900460ff166001145b6119ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790558015611a6357600080547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790555b611ac47f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061236b92505050565b508015611b2757600080547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50565b600060606117fb85858585604051908082528060200260200182016040528015611b7a57816020015b6040805180820190915260008082526020820152815260200190600190039081611b535790505b50600061343a565b6060816002811015611bc0576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff811115611bd957611bd96143fa565b604051908082528060200260200182016040528015611c1e57816020015b6040805180820190915260008082526020820152815260200190600190039081611bf75790505b50915060005b81811015611d2a576040518060400160405280611cd5878785818110611c4c57611c4c615527565b9050602002810190611c5e9190615556565b611c689080615156565b611c71906151fc565b888886818110611c8357611c83615527565b9050602002810190611c959190615556565b602001803603810190611ca89190615208565b898987818110611cba57611cba615527565b9050602002810190611ccc9190615556565b60c001356124ca565b8152602001868684818110611cec57611cec615527565b9050602002810190611cfe9190615556565b60c00135815250838281518110611d1757611d17615527565b6020908102919091010152600101611c24565b505092915050565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d72573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061129c9190615263565b604080518082018252601281527f657865637574654f7065617274696f6e282900000000000000000000000000006020918201529051600091611e01918591859142917f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e910161558a565b60408051601f19818403018152919052805160209091012060365460081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009081169116148015611e5a57506001600160a01b03841630145b611e90576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b603680547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81169091557f0100000000000000000000000000000000000000000000000000000000000000900460ff163261dead03611f4257611f3c7f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef6001600160a01b03811663f7b14fac611f27878901896155d7565b85600160405160240161169c9392919061560c565b50611f59565b611f59611f51848601866155d7565b826001613588565b5060019a9950505050505050505050565b600060606117fb85858585604051908082528060200260200182016040528015611fba57816020015b6040805180820190915260008082526020820152815260200190600190039081611f935790505b50612d03565b60006060468660c0013514612001576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61142a61200e8780615156565b87602001878787808060200260200160405190810160405280939291908181526020016000905b828210156120615761205260408302860136819003810190615224565b81526020019060010190612035565b5050505050600061343a565b60006060468660c00135146120ae576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61142a6120bb8780615156565b87602001878787808060200260200160405190810160405280939291908181526020016000905b8282101561210e576120ff60408302860136819003810190615224565b815260200190600101906120e2565b5050505050600161343a565b3261dead0361218e576117587f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef7f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef6001600160a01b031663f7b14fac868686600060405160240161169c94939291906157f8565b604080518082018252600e81527f5f63616c6c54617267657473282900000000000000000000000000000000000060209182015290516121f89185918591859142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b9101615824565b60408051601f19818403018152919052805160209091012060365460081b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00908116911614612273576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e4b6122808385615877565b826000613588565b600061144c838361302c565b600061144c8383612c83565b600061129c6139b4565b610e4b612330565b600061144c8383808060200260200160405190810160405280939291908181526020016000905b82821015610fa7576122f660408302860136819003810190615224565b815260200190600101906122d9565b600061129c7f56de5bbd3725e9c80073ed00b9767413d9ddf7d1ec1d602a8f924f15a9c75685612759565b333014612369576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6060600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e8115600181036123ae57816000803e816000fd5b50505092915050565b6000851180156123f757508360001914806123f7575060005474010000000000000000000000000000000000000000900469ffffffffffffffffffff1684145b61242d576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b42831180612445575060008211801561244557504282105b1561247c576040517f3c83d07f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008111801561248c5750803414155b156124c3576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b60007fc84a2c176321157bd55b70feb5871c4304d99c870656d2fc420998eff645e2076124f6856139e2565b6124ff85613bd4565b60408051602081019490945283019190915260608201526080810183905260a0016040516020818303038152906040528051906020012090509392505050565b805160018111156125b25760005b818110156125b05782818151811061256757612567615527565b6020026020010151600001518414801561259d575082818151811061258e5761258e615527565b60200260200101516020015146145b156125a85750505050565b60010161254d565b505b6040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600090606080835b83811015612681578286828151811061260957612609615527565b602002602001015160000151604051602001612626929190615884565b60405160208183030381529060405292508186828151811061264a5761264a615527565b602002602001015160200151604051602001612667929190615884565b60408051601f1981840301815291905291506001016125ee565b506126fe6126ae7f56de5bbd3725e9c80073ed00b9767413d9ddf7d1ec1d602a8f924f15a9c75685612759565b8351602080860191909120845185830120604080517fb7c77dbcd01eff35f637803daf7abe3f0e3b86b5102459be5665dc7d9a85ac5e94810194909452830191909152606082015260800161106b565b95945050505050565b6060600061271685858561286e565b9250905080612751576040517f70a1717900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509392505050565b604080518082018252601081527f41766f6361646f2d4d756c74697369670000000000000000000000000000000060209182015281518083018352600581527f312e312e300000000000000000000000000000000000000000000000000000009082015281517fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472918101919091527ff5d974efd8e94277ef83fff53580c07a80e6e3ffdb2e3fec63185a4e812655a2918101919091527f6815ba53416ba06aff1932cc76b3832272bafab9bc8e066be382e32b06ba5546606082015261027a60808201523060a082015260c0810182905260009060e0015b604051602081830303815290604052805190602001209050919050565b815160009060609061287e612c55565b60ff168110806128a457508380156128a457506000868152600360205260409020546001145b156128db576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8067ffffffffffffffff8111156128f4576128f46143fa565b60405190808252806020026020018201604052801561291d578160200160208202803683370190505b509150600061292a612f3d565b805190915060008080805b86811015612c425761296d8b828151811061295257612952615527565b6020026020010151602001516001600160a01b03163b151590565b156129cb578a818151811061298457612984615527565b6020026020010151602001518882815181106129a2576129a2615527565b60200260200101906001600160a01b031690816001600160a01b03168152505060019250612aa5565b6129f28c8c83815181106129e1576129e1615527565b602002602001015160000151613c48565b888281518110612a0457612a04615527565b60200260200101906001600160a01b031690816001600160a01b031681525050878181518110612a3657612a36615527565b60200260200101516001600160a01b03168b8281518110612a5957612a59615527565b6020026020010151602001516001600160a01b031614612aa5576040517fec92598500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b835b85811015612b1157888281518110612ac157612ac1615527565b60200260200101516001600160a01b0316878281518110612ae457612ae4615527565b60200260200101516001600160a01b031603612b095760019250806001019450612b11565b600101612aa7565b5081612b27576000985050505050505050611800565b600091508215612c3a578a517f1626ba7e00000000000000000000000000000000000000000000000000000000908c9083908110612b6757612b67615527565b6020026020010151602001516001600160a01b0316631626ba7e8e8e8581518110612b9457612b94615527565b6020026020010151600001516040518363ffffffff1660e01b8152600401612bbd9291906158a6565b602060405180830381865afa158015612bda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bfe91906158bf565b7fffffffff000000000000000000000000000000000000000000000000000000001614612c35576000985050505050505050611800565b600092505b600101612935565b5060019750505050505050935093915050565b60015474010000000000000000000000000000000000000000900460ff166000819003612c80575060015b90565b600061144c612cb17f6e452848784197f00927d379e3db9e69a5131d2269f862bfcd05a0b38f6abf7f612759565b7fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b612cdb866139e2565b612ce486613bd4565b604080516020810194909452830191909152606082015260800161106b565b60006060336001600160a01b037f00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a541614612d69576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84355a6101f4011015612da8576040517fe681bf7d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612dd0612db58780615194565b905087604001358760400135886060013589608001356123b7565b825160009015612e0e57612dfe612df8612de9896151fc565b610ef5368a90038a018a615208565b8561253f565b612e07846125e4565b9050612e2c565b612e29612e1a886151fc565b61135636899003890189615208565b90505b6000612e4182878a6040013560001914612707565b9050612e5f8861156361271084518c8060a0019061154f919061544d565b90945092508315612ecf5733612e7b60a08a0160808b01614b1a565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b83612eb360a08d018d61544d565b604051612ec2939291906154b2565b60405180910390a3612f32565b33612ee060a08a0160808b01614b1a565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f8386612f1960a08e018e61544d565b604051612f2994939291906154e2565b60405180910390a35b505094509492505050565b6001546060906001600160a01b03168061300a576040805160018082528183019092529060208083019080368337019050509150306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612faf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fd39190615263565b82600081518110612fe657612fe6615527565b60200260200101906001600160a01b031690816001600160a01b0316815250505090565b61301381613c64565b8060200190518101906130269190615940565b91505090565b600061144c61305a7f6e452848784197f00927d379e3db9e69a5131d2269f862bfcd05a0b38f6abf7f612759565b7f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c0613084866139e2565b85516020808801516040808a015160608b015191516130e7957f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb959094939101948552602085019390935260408401919091526060830152608082015260a00190565b60408051601f19818403018152828252805160209182012090830194909452810191909152606081019190915260800161106b565b6000606061312a8580615194565b604080518082018252600e81527f5f63616c6c5461726765747328290000000000000000000000000000000000006020918201529051613195939291808a01359142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b9101615824565b60408051808303601f190181529190528051602090910120603680547fff000000000000000000000000000000000000000000000000000000000000001660089290921c91909117905582613248576000805474010000000000000000000000000000000000000000900469ffffffffffffffffffff16906014613218836159a4565b91906101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff1602179055505061325b565b6000838152600360205260409020600190555b60006132678680615194565b876020013560405160240161327e939291906159cd565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb92e87fa0000000000000000000000000000000000000000000000000000000017905290506060609686015a1015613332576132ef6001603655565b60006040518060400160405280600f81526020017f41564f5f5f4f55545f4f465f4741530000000000000000000000000000000000815250935093505050611800565b6000808351602085016001600160a01b03600054168a5a03f493503d6040519150601f19601f6020830101168201604052808252806000602084013e5083613426578051600003613409576133886096876159f1565b5a10156133cc576040518060400160405280600f81526020017f41564f5f5f4f55545f4f465f47415300000000000000000000000000000000008152509250613426565b6040518060400160405280601781526020017f41564f5f5f524541534f4e5f4e4f545f444546494e45440000000000000000008152509250613426565b600481019050808060200190518101906134239190615a04565b92505b6134306001603655565b5050935093915050565b6000606061dead3314806134825750336001600160a01b037f00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a5416148015613482575061dead32145b6134b8576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6135227f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef7f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef6001600160a01b0316633e0cd8978a8a8a8a60405160240161169c9493929190615aad565b8060200190518101906135359190615c10565b9092509050828015613545575081155b1561357e57806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119c19190614b85565b9550959350505050565b6135926001603655565b600080600084600114806135a65750846015145b905080156135b957600054925060015491505b855160005b818110156139aa5760008882815181106135da576135da615527565b6020026020010151905060006060600083606001516000148015613613575060028b108061360857508a6014145b8061361357508a6015145b156136ad5760405a8161362857613628615c66565b04905083600001516001600160a01b0316846040015185602001516040516136509190615c95565b60006040518083038185875af1925050503d806000811461368d576040519150601f19603f3d011682016040523d82523d6000602084013e613692565b606091505b509093509150826136a8576136a8818684613c85565b61399b565b836060015160011480156136be5750865b156137ce5760405a816136d3576136d3615c66565b04905083600001516001600160a01b031684602001516040516136f69190615c95565b600060405180830381855af49150503d8060008114613731576040519150601f19603f3d011682016040523d82523d6000602084013e613736565b606091505b5090935091508261374c5761374c818684613c85565b6137566001603655565b6000546001548a8214801561376a57508981145b6137c75761377787613db3565b6040516020016137879190615ca7565b60408051601f19818403018152908290527f08c379a00000000000000000000000000000000000000000000000000000000082526119c191600401614b85565b505061399b565b836060015160021480156137ec57508a601414806137ec57508a6015145b156139825789156138105761380085613db3565b6040516020016137879190615ce8565b6020840151600481018051909161382f91810160249081019101615d29565b50604080518082018252601281527f657865637574654f7065617274696f6e28290000000000000000000000000000602091820152905191955061389c94508593504292507f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e9101615e28565b60408051808303601f19018152918152815160209092019190912060081c7f010000000000000000000000000000000000000000000000000000000000000060ff8f1602176036555a816138f2576138f2615c66565b04915084600001516001600160a01b03168560400151866020015160405161391a9190615c95565b60006040518083038185875af1925050503d8060008114613957576040519150601f19603f3d011682016040523d82523d6000602084013e61395c565b606091505b5090945092508361397257613972828785613c85565b61397c6001603655565b5061399b565b61398b85613db3565b6040516020016137879190615e73565b846001019450505050506135be565b5050505050505050565b6001547501000000000000000000000000000000000000000000900460ff166000819003612c805750600190565b8051516000906060825b82811015613b2157817f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6586600001518381518110613a2c57613a2c615527565b60200260200101516000015187600001518481518110613a4e57613a4e615527565b6020026020010151602001518051906020012088600001518581518110613a7757613a77615527565b60200260200101516040015189600001518681518110613a9957613a99615527565b602002602001015160600151604051602001613ae09594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120604051602001613b07929190615884565b60408051601f1981840301815291905291506001016139ec565b507fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc47818051906020012085602001518660400151876060015188608001518960a0015180519060200120604051602001613bb5979695949392919096875260208701959095526040860193909352606085019190915260808401526001600160a01b031660a083015260c082015260e00190565b6040516020818303038152906040528051906020012092505050919050565b60007f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e8260000151836020015184604001518560600151866080015160405160200161285196959493929190958652602086019490945260408501929092526060840152608083015260a082015260c00190565b6000806000613c578585613e53565b9150915061275181613e98565b6060610ddc826001613c80816001600160a01b0384163b6159f1565b61404b565b825a1015613cef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f41564f5f5f4f55545f4f465f474153000000000000000000000000000000000060448201526064016119c1565b613cf882613db3565b6040517f2b5378750000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef1690632b53787590613d5d908590600401614b85565b600060405180830381865afa158015613d7a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613da29190810190615a04565b604051602001613787929190615eb4565b60606000613dc08361406e565b600101905060008167ffffffffffffffff811115613de057613de06143fa565b6040519080825280601f01601f191660200182016040528015613e0a576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a8504945084613e1457509392505050565b6000808251604103613e895760208301516040840151606085015160001a613e7d87828585614150565b94509450505050613e91565b506000905060025b9250929050565b6000816004811115613eac57613eac615ee3565b03613eb45750565b6001816004811115613ec857613ec8615ee3565b03613f2f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016119c1565b6002816004811115613f4357613f43615ee3565b03613faa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016119c1565b6003816004811115613fbe57613fbe615ee3565b03611b27576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f756500000000000000000000000000000000000000000000000000000000000060648201526084016119c1565b60408051603f8301601f19168101909152818152818360208301863c9392505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083106140b7577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef810000000083106140e3576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061410157662386f26fc10000830492506010015b6305f5e1008310614119576305f5e100830492506008015b612710831061412d57612710830492506004015b6064831061413f576064830492506002015b600a8310610ddc5760010192915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115614187575060009050600361142f565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156141db573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166142045760006001925092505061142f565b9660009650945050505050565b7fffffffff0000000000000000000000000000000000000000000000000000000081168114611b2757600080fd5b60006020828403121561425157600080fd5b813561144c81614211565b60008083601f84011261426e57600080fd5b50813567ffffffffffffffff81111561428657600080fd5b6020830191508360208260051b8501011115613e9157600080fd5b600080602083850312156142b457600080fd5b823567ffffffffffffffff8111156142cb57600080fd5b6142d78582860161425c565b90969095509350505050565b600060e082840312156142f557600080fd5b50919050565b60008083601f84011261430d57600080fd5b50813567ffffffffffffffff81111561432557600080fd5b6020830191508360208260061b8501011115613e9157600080fd5b60008060008060006060868803121561435857600080fd5b853567ffffffffffffffff8082111561437057600080fd5b61437c89838a016142e3565b9650602088013591508082111561439257600080fd5b61439e89838a0161425c565b909650945060408801359150808211156143b757600080fd5b506143c4888289016142fb565b969995985093965092949392505050565b6001600160a01b0381168114611b2757600080fd5b80356143f5816143d5565b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561444c5761444c6143fa565b60405290565b6040516080810167ffffffffffffffff8111828210171561444c5761444c6143fa565b60405160c0810167ffffffffffffffff8111828210171561444c5761444c6143fa565b604051601f8201601f1916810167ffffffffffffffff811182821017156144c1576144c16143fa565b604052919050565b600067ffffffffffffffff8211156144e3576144e36143fa565b50601f01601f191660200190565b600082601f83011261450257600080fd5b8135614515614510826144c9565b614498565b81815284602083860101111561452a57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561455d57600080fd5b8435614568816143d5565b93506020850135614578816143d5565b925060408501359150606085013567ffffffffffffffff81111561459b57600080fd5b6145a7878288016144f1565b91505092959194509250565b60008083601f8401126145c557600080fd5b50813567ffffffffffffffff8111156145dd57600080fd5b602083019150836020828501011115613e9157600080fd5b60008060006040848603121561460a57600080fd5b83359250602084013567ffffffffffffffff81111561462857600080fd5b614634868287016145b3565b9497909650939450505050565b600060c082840312156142f557600080fd5b600060a082840312156142f557600080fd5b60008060008060e0858703121561467b57600080fd5b843567ffffffffffffffff8082111561469357600080fd5b61469f88838901614641565b95506146ae8860208901614653565b945060c08701359150808211156146c457600080fd5b506146d18782880161425c565b95989497509550505050565b6000602082840312156146ef57600080fd5b5035919050565b600067ffffffffffffffff821115614710576147106143fa565b5060051b60200190565b6000614728614510846146f6565b8381529050602080820190600585901b84018681111561474757600080fd5b845b818110156147c957803567ffffffffffffffff8082111561476a5760008081fd5b908701906040828b03121561477f5760008081fd5b614787614429565b8235828111156147975760008081fd5b6147a38c8286016144f1565b82525091850135916147b4836143d5565b80860192909252508452928201928201614749565b505050509392505050565b600082601f8301126147e557600080fd5b61144c8383356020850161471a565b6000806000806060858703121561480a57600080fd5b843567ffffffffffffffff8082111561482257600080fd5b61482e888389016142e3565b9550602087013591508082111561484457600080fd5b614850888389016147d4565b9450604087013591508082111561486657600080fd5b506146d1878288016142fb565b60005b8381101561488e578181015183820152602001614876565b50506000910152565b600081518084526148af816020860160208601614873565b601f01601f19169290920160200192915050565b82151581526040602082015260006110016040830184614897565b600081518084526020808501945080840160005b838110156149175781516001600160a01b0316875295820195908201906001016148f2565b509495945050505050565b60208152600061144c60208301846148de565b6000608082840312156142f557600080fd5b600080600060c0848603121561495c57600080fd5b833567ffffffffffffffff8082111561497457600080fd5b61498087838801614641565b945061498f8760208801614935565b935060a08601359150808211156149a557600080fd5b506149b2868287016147d4565b9150509250925092565b803560ff811681146143f557600080fd5b6000806000604084860312156149e257600080fd5b833567ffffffffffffffff8111156149f957600080fd5b614a058682870161425c565b9094509250614a189050602085016149bc565b90509250925092565b60008060008060c08587031215614a3757600080fd5b843567ffffffffffffffff80821115614a4f57600080fd5b614a5b88838901614641565b9550614a6a8860208901614935565b945060a08701359150808211156146c457600080fd5b600080600060408486031215614a9557600080fd5b8335614aa0816143d5565b9250602084013567ffffffffffffffff81111561462857600080fd5b600080600060e08486031215614ad157600080fd5b833567ffffffffffffffff80821115614ae957600080fd5b614af587838801614641565b9450614b048760208801614653565b935060c08601359150808211156149a557600080fd5b600060208284031215614b2c57600080fd5b813561144c816143d5565b600081518084526020808501945080840160005b83811015614917578151805188528301518388015260409096019590820190600101614b4b565b60208152600061144c6020830184614b37565b60208152600061144c6020830184614897565b600080600080600080600080600060a08a8c031215614bb657600080fd5b893567ffffffffffffffff80821115614bce57600080fd5b614bda8d838e0161425c565b909b50995060208c0135915080821115614bf357600080fd5b614bff8d838e0161425c565b909950975060408c0135915080821115614c1857600080fd5b614c248d838e0161425c565b909750955060608c01359150614c39826143d5565b90935060808b01359080821115614c4f57600080fd5b50614c5c8c828d016145b3565b915080935050809150509295985092959850929598565b600080600060408486031215614c8857600080fd5b833567ffffffffffffffff811115614c9f57600080fd5b614cab8682870161425c565b909790965060209590950135949350505050565b6000614ccd614510846146f6565b8381529050602080820190600585901b840186811115614cec57600080fd5b845b818110156147c957803567ffffffffffffffff80821115614d0f5760008081fd5b908701906080828b031215614d245760008081fd5b614d2c614452565b8235614d37816143d5565b81528286013582811115614d4b5760008081fd5b614d578c8286016144f1565b82880152506040838101359082015260609283013592810192909252508452928201928201614cee565b600082601f830112614d9257600080fd5b61144c83833560208501614cbf565b600060c08284031215614db357600080fd5b614dbb614475565b9050813567ffffffffffffffff80821115614dd557600080fd5b614de185838601614d81565b8352602084013560208401526040840135604084015260608401356060840152614e0d608085016143ea565b608084015260a0840135915080821115614e2657600080fd5b50614e33848285016144f1565b60a08301525092915050565b600060808284031215614e5157600080fd5b614e59614452565b90508135815260208201356020820152604082013560408201526060820135606082015292915050565b60008060a08385031215614e9657600080fd5b823567ffffffffffffffff811115614ead57600080fd5b614eb985828601614da1565b925050614ec98460208501614e3f565b90509250929050565b600082601f830112614ee357600080fd5b81356020614ef3614510836146f6565b82815260059290921b84018101918181019086841115614f1257600080fd5b8286015b84811015614f2d5780358352918301918301614f16565b509695505050505050565b600080600080600060a08688031215614f5057600080fd5b8535614f5b816143d5565b94506020860135614f6b816143d5565b9350604086013567ffffffffffffffff80821115614f8857600080fd5b614f9489838a01614ed2565b94506060880135915080821115614faa57600080fd5b614fb689838a01614ed2565b93506080880135915080821115614fcc57600080fd5b50614fd9888289016144f1565b9150509295509295909350565b600060a08284031215614ff857600080fd5b60405160a0810181811067ffffffffffffffff8211171561501b5761501b6143fa565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b60008060c0838503121561506957600080fd5b823567ffffffffffffffff81111561508057600080fd5b61508c85828601614da1565b925050614ec98460208501614fe6565b600080602083850312156150af57600080fd5b823567ffffffffffffffff8111156150c657600080fd5b6142d7858286016142fb565b6000602082840312156150e457600080fd5b61144c826149bc565b600080600080600060a0868803121561510557600080fd5b8535615110816143d5565b94506020860135615120816143d5565b93506040860135925060608601359150608086013567ffffffffffffffff81111561514a57600080fd5b614fd9888289016144f1565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261518a57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126151c957600080fd5b83018035915067ffffffffffffffff8211156151e457600080fd5b6020019150600581901b3603821315613e9157600080fd5b6000610ddc3683614da1565b600060a0828403121561521a57600080fd5b61144c8383614fe6565b60006040828403121561523657600080fd5b61523e614429565b82358152602083013560208201528091505092915050565b600061144c36848461471a565b60006020828403121561527557600080fd5b815161144c816143d5565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6040815260006152bf604083018587615280565b90506001600160a01b0383166020830152949350505050565b600082601f8301126152e957600080fd5b81516152f7614510826144c9565b81815284602083860101111561530c57600080fd5b611001826020830160208701614873565b6000602080838503121561533057600080fd5b825167ffffffffffffffff8082111561534857600080fd5b818501915085601f83011261535c57600080fd5b815161536a614510826146f6565b81815260059190911b8301840190848101908883111561538957600080fd5b8585015b8381101561540b578051858111156153a55760008081fd5b86016040818c03601f19018113156153bd5760008081fd5b6153c5614429565b89830151888111156153d75760008081fd5b6153e58e8c838701016152d8565b82525091810151916153f6836143d5565b808a019290925250835291860191860161538d565b5098975050505050505050565b60006020828403121561542a57600080fd5b5051919050565b60006080828403121561544357600080fd5b61144c8383614e3f565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261548257600080fd5b83018035915067ffffffffffffffff82111561549d57600080fd5b602001915036819003821315613e9157600080fd5b6040815260006154c560408301866148de565b82810360208401526154d8818587615280565b9695505050505050565b6060815260006154f560608301876148de565b82810360208401526155078187614897565b9050828103604084015261551c818587615280565b979650505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2183360301811261518a57600080fd5b60608152600061559e606083018688615280565b90508360208301527fffffffff000000000000000000000000000000000000000000000000000000008316604083015295945050505050565b6000602082840312156155e957600080fd5b813567ffffffffffffffff81111561560057600080fd5b61100184828501614d81565b60006060808301818452808751808352608092508286019150828160051b8701016020808b0160005b848110156156af577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808a850301865281516001600160a01b03815116855283810151888587015261568889870182614897565b60408381015190880152918a0151958a019590955295830195935090820190600101615635565b50508701899052871515604088015294506110019350505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126156ff57600080fd5b830160208101925035905067ffffffffffffffff81111561571f57600080fd5b803603821315613e9157600080fd5b81835260006020808501808196508560051b81019150846000805b888110156157ea578385038a5282357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81893603018112615787578283fd5b880160808135615796816143d5565b6001600160a01b031687526157ad828901836156ca565b828a8a01526157bf838a018284615280565b604085810135908b015260609485013594909901939093525050509885019891850191600101615749565b509298975050505050505050565b60608152600061580c60608301868861572e565b60208301949094525090151560409091015292915050565b60808152600061583860808301878961572e565b60208301959095525060408101929092527fffffffff000000000000000000000000000000000000000000000000000000001660609091015292915050565b600061144c368484614cbf565b60008351615896818460208801614873565b9190910191825250602001919050565b8281526040602082015260006110016040830184614897565b6000602082840312156158d157600080fd5b815161144c81614211565b600082601f8301126158ed57600080fd5b815160206158fd614510836146f6565b82815260059290921b8401810191818101908684111561591c57600080fd5b8286015b84811015614f2d578051615933816143d5565b8352918301918301615920565b60006020828403121561595257600080fd5b815167ffffffffffffffff81111561596957600080fd5b611001848285016158dc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600069ffffffffffffffffffff8083168181036159c3576159c3615975565b6001019392505050565b6040815260006159e160408301858761572e565b9050826020830152949350505050565b81810381811115610ddc57610ddc615975565b600060208284031215615a1657600080fd5b815167ffffffffffffffff811115615a2d57600080fd5b611001848285016152d8565b600081518084526020808501808196508360051b8101915082860160005b85811015615aa0578284038952815160408151818752615a7982880182614897565b928801516001600160a01b0316968801969096525098850198935090840190600101615a57565b5091979650505050505050565b600061010080835286357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1883603018112615ae757600080fd5b870160208101903567ffffffffffffffff811115615b0457600080fd5b8060051b3603821315615b1657600080fd5b60c083860152615b2b6101c08601828461572e565b92505050602087013561012084015260408701356101408401526060870135610160840152615b5c608088016143ea565b6001600160a01b0316610180840152615b7860a08801886156ca565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00858403016101a0860152615bae838284615280565b92505050615bea602084018780358252602081013560208301526040810135604083015260608101356060830152608081013560808301525050565b82810360c0840152615bfc8186615a39565b905082810360e084015261551c8185614b37565b60008060408385031215615c2357600080fd5b82518015158114615c3357600080fd5b602084015190925067ffffffffffffffff811115615c5057600080fd5b615c5c858286016152d8565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000825161518a818460208701614873565b60008251615cb9818460208701614873565b7f5f41564f5f5f4d4f4449464945445f53544f5241474500000000000000000000920191825250601601919050565b60008251615cfa818460208701614873565b7f5f41564f5f5f4e4f5f464c4153484c4f414e5f494e5f464c4153484c4f414e00920191825250601f01919050565b600080600080600060a08688031215615d4157600080fd5b855167ffffffffffffffff80821115615d5957600080fd5b615d6589838a016158dc565b9650602091508188015181811115615d7c57600080fd5b8801601f81018a13615d8d57600080fd5b8051615d9b614510826146f6565b81815260059190911b8201840190848101908c831115615dba57600080fd5b928501925b82841015615dd857835182529285019290850190615dbf565b60408c015160608d0151919a509850945050505080821115615df957600080fd5b615e0589838a016152d8565b93506080880151915080821115615e1b57600080fd5b50614fd9888289016152d8565b606081526000615e3b6060830186614897565b90508360208301527fffffffff0000000000000000000000000000000000000000000000000000000083166040830152949350505050565b60008251615e85818460208701614873565b7f5f41564f5f5f494e56414c49445f49445f4f525f4f5045524154494f4e000000920191825250601d01919050565b60008351615ec6818460208801614873565b835190830190615eda818360208801614873565b01949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea2646970667358221220074a82e1d408a622c861eb428960df3f50376d1b0125d8c604e38f13d30eaa1b64736f6c63430008120033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000779385ec7a04242259add4990e3130846f80ea6900000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068480000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef
-----Decoded View---------------
Arg [0] : avoRegistry_ (address): 0x779385EC7A04242259AdD4990e3130846f80eA69
Arg [1] : avoForwarder_ (address): 0x46978CD477A496028A18c02F07ab7F35EDBa5A54
Arg [2] : avoSignersList_ (address): 0xA27A71DD0348b5C69370eF9Da3743197DC006848
Arg [3] : avoConfigV1_ (address): 0x1412121E487478498b355C227c77D5ED6CF1798d
Arg [4] : secondary_ (address): 0x50bdE2DBF42172c85Fe358F0603eEd0b7eCbc2eF
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000779385ec7a04242259add4990e3130846f80ea69
Arg [1] : 00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54
Arg [2] : 000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc006848
Arg [3] : 0000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d
Arg [4] : 00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ 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.