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 | |||
---|---|---|---|---|---|---|
410643 | 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 800 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 { IAvocado } from "../Avocado.sol"; import { IAvoConfigV1 } from "../interfaces/IAvoConfigV1.sol"; import { 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.0.1 /// @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 /// /// 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 { if (avoImplementation_ == _avoImpl) { return; } // checks that `avoImplementation_` is a valid version at registry. reverts if not. avoRegistry.requireValidAvoVersion(avoImplementation_); // store previous implementation address to pass to after upgrade hook, for version x > version y specific logic address fromImplementation_ = _avoImpl; _avoImpl = avoImplementation_; emit Upgraded(avoImplementation_); // Address.functionDelegateCall will revert if success = false Address.functionDelegateCall( avoImplementation_, abi.encodeCall(this._afterUpgradeHook, (fromImplementation_, afterUpgradeHookData_)) ); } /// @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 { uint256 avoNoncesLength_ = avoNonces_.length; if (avoNoncesLength_ == 0) { // in case to cancel just one nonce via normal sequential nonce execution itself return; } if (avoNoncesLength_ > 5) { revert AvocadoMultisig__InvalidParams(); } uint256 nextAvoNonce_ = _avoNonce; for (uint256 i; i < avoNoncesLength_; ) { if (avoNonces_[i] == nextAvoNonce_) { // nonce to occupy is valid -> must match the current avoNonce emit AvoNonceOccupied(nextAvoNonce_); nextAvoNonce_++; } else if (avoNonces_[i] > nextAvoNonce_) { // input nonce is not smaller or equal current nonce -> invalid sorted ascending input params revert AvocadoMultisig__InvalidParams(); } // else while nonce to occupy is < current nonce, skip ahead unchecked { ++i; } } _avoNonce = uint80(nextAvoNonce_); } /// @notice occupies the `nonSequentialNonces_` in storage. This can be used to cancel / invalidate /// previously signed request(s) because the nonce will be "used" up. /// - Can only be self-called (authorization same as for `cast` methods). /// @param nonSequentialNonces_ the non-sequential nonces to occupy function occupyNonSequentialNonces(bytes32[] calldata nonSequentialNonces_) external onlySelf { uint256 nonSequentialNoncesLength_ = nonSequentialNonces_.length; for (uint256 i; i < nonSequentialNoncesLength_; ) { nonSequentialNonces[nonSequentialNonces_[i]] = 1; emit NonSequentialNonceOccupied(nonSequentialNonces_[i]); unchecked { ++i; } } } /***********************************| | 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; // 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 { // _transientAllowHash must be set or 0x000000000000000000000000000000000000dEaD used for backend gas estimations if ( !(_transientAllowHash == bytes31(keccak256(abi.encode(actions_, id_, block.timestamp, _CALL_TARGETS_SELECTOR))) || tx.origin == 0x000000000000000000000000000000000000dEaD) ) { revert AvocadoMultisig__Unauthorized(); } _executeActions(actions_, id_, false); } } abstract contract AvocadoMultisigEIP1271 is AvocadoMultisigCore { /// @dev length of a normal expected ECDSA signature uint256 private constant _SIGNATURE_LENGTH = 65; /// @dev signature must be 65 bytes or otherwise at least 90 bytes to be either a multiple /// of 85 bytes + prefix or a decodable `SignatureParams` struct array. uint256 private constant _MIN_SIGNATURE_LENGTH = 90; /// @dev prefix to signal decoding with multiple of 85 bytes is "0xDEC0DE6520" (appending 000000 to get to bytes8) bytes8 private constant _PREFIX_SIGNAL = bytes8(uint64(0xdec0de6520000000)); /// @dev prefix length to cut of is 5 bytes (DE_C0_DE_65_20) uint256 private constant _PREFIX_SIGNAL_LENGTH = 5; /// @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(), 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 { // decode signaturesParams_ from bytes signature SignatureParams[] memory signaturesParams_; uint256 signatureLength_ = signature.length; if (signatureLength_ == _SIGNATURE_LENGTH) { // signature must be from owner for a Multisig with requiredSigners = 1, signers=[owner] signaturesParams_ = new SignatureParams[](1); signaturesParams_[0] = SignatureParams({ signature: signature, signer: IAvocado(address(this))._owner() }); } else if (signatureLength_ < _MIN_SIGNATURE_LENGTH) { revert AvocadoMultisig__InvalidEIP1271Signature(); } else if (bytes8(signature[0:_PREFIX_SIGNAL_LENGTH]) == _PREFIX_SIGNAL) { // if signature is prefixed with _PREFIX_SIGNAL ("0xDEC0DE6520") -> // signature after the prefix should be divisible by 85 // (65 bytes signature and 20 bytes signer address) each uint256 signaturesCount_; unchecked { // -_PREFIX_SIGNAL_LENGTH to not count prefix signaturesCount_ = (signatureLength_ - _PREFIX_SIGNAL_LENGTH) / 85; } signaturesParams_ = new SignatureParams[](signaturesCount_); for (uint256 i; i < signaturesCount_; ) { // used operations can not overflow / underflow unchecked { // +_PREFIX_SIGNAL_LENGTH to start after prefix uint256 signerOffset_ = (i * 85) + _SIGNATURE_LENGTH + _PREFIX_SIGNAL_LENGTH; bytes memory signerBytes_ = signature[signerOffset_:signerOffset_ + 20]; address signer_; // cast bytes to address in the easiest way via assembly assembly { signer_ := shr(96, mload(add(signerBytes_, 0x20))) } signaturesParams_[i] = SignatureParams({ signature: signature[(signerOffset_ - _SIGNATURE_LENGTH):signerOffset_], signer: signer_ }); ++i; } } } else { // multiple signatures are present that should form `SignatureParams[]` through abi.decode // @dev this will fail and revert if invalid typed data is passed in signaturesParams_ = abi.decode(signature, (SignatureParams[])); } (bool validSignature_, ) = _verifySig( hash, signaturesParams_, // 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 { // hashing with domain separator mitigates any potential replaying on other networks or other Avocados of the same owner message_ = ECDSA.toTypedDataHash(_domainSeparatorV4(), keccak256(abi.encode(EIP1271_TYPE_HASH, message_))); _signedMessages[message_] = 1; emit SignedMessage(message_); } /// @notice Removes a previously `signMessage()` signed bytes32 `message_` (signature digest). /// - Can only be self-called (authorization same as for `cast` methods). /// @param message_ data hash to be removed from allow-listed signatures function removeSignedMessage(bytes32 message_) external onlySelf { _signedMessages[message_] = 0; emit RemoveSignedMessage(message_); } } abstract contract AvocadoMultisigSigners is AvocadoMultisigCore { /// @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; } /// @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 { uint256 addSignersLength_ = addSigners_.length; // check array length and make sure signers can not be zero address // (only check for first elem needed, rest is checked through sort) if (addSignersLength_ == 0 || addSigners_[0] == address(0)) { revert AvocadoMultisig__InvalidParams(); } address[] memory currentSigners_ = _getSigners(); uint256 currentSignersLength_ = currentSigners_.length; uint256 newSignersLength_ = currentSignersLength_ + addSignersLength_; if (newSignersLength_ > MAX_SIGNERS_COUNT) { revert AvocadoMultisig__InvalidParams(); } address[] memory newSigners_ = new address[](newSignersLength_); uint256 currentSignersPos_ = 0; // index of position of loop in currentSigners_ array uint256 addedCount_ = 0; // keep track of number of added signers of current signers array for (uint256 i; i < newSignersLength_; ) { unchecked { currentSignersPos_ = i - addedCount_; } if ( addedCount_ == addSignersLength_ || (currentSignersPos_ < currentSignersLength_ && currentSigners_[currentSignersPos_] < addSigners_[addedCount_]) ) { // if already added all signers or if current signer is < next signer, keep the current one newSigners_[i] = currentSigners_[currentSignersPos_]; } else { // add signer newSigners_[i] = addSigners_[addedCount_]; emit SignerAdded(addSigners_[addedCount_]); unchecked { ++addedCount_; } } if (i > 0 && newSigners_[i] <= newSigners_[i - 1]) { // make sure input signers are ordered ascending and no duplicate signers are added revert AvocadoMultisig__InvalidParams(); } unchecked { ++i; } } // update values in storage _setSigners(newSigners_, requiredSigners_); // updates `signersCount`, checks and sets `requiredSigners_` // sync mappings at AvoSignersList -> must happen *after* storage write update // use call with success_ here to not block users transaction if the helper contract fails. // in case of failure, only emit event ListSyncFailed() so off-chain tracking is informed to react. (bool success_, ) = address(avoSignersList).call( abi.encodeCall(IAvoSignersList.syncAddAvoSignerMappings, (address(this), addSigners_)) ); if (!success_) { emit ListSyncFailed(); } } /// @notice removes `removeSigners_` from allowed signers and sets required signers count to `requiredSigners_` /// Note the `removeSigners_` to be removed must: /// - NOT be the owner /// - be sorted ascending /// - be present in current allowed signers function removeSigners(address[] calldata removeSigners_, uint8 requiredSigners_) external onlySelf { uint256 removeSignersLength_ = removeSigners_.length; if (removeSignersLength_ == 0) { revert AvocadoMultisig__InvalidParams(); } address[] memory currentSigners_ = _getSigners(); uint256 currentSignersLength_ = currentSigners_.length; uint256 newSignersLength_ = currentSignersLength_ - removeSignersLength_; address owner_ = IAvocado(address(this))._owner(); address[] memory newSigners_ = new address[](newSignersLength_); uint256 currentInsertPos_ = 0; // index of position of loop in `newSigners_` array uint256 removedCount_ = 0; // keep track of number of removed signers of current signers array for (uint256 i; i < currentSignersLength_; ) { unchecked { currentInsertPos_ = i - removedCount_; } if (removedCount_ == removeSignersLength_ || currentSigners_[i] != removeSigners_[removedCount_]) { // if already removed all signers or if current signer is not a signer to be removed, keep the current one if (currentInsertPos_ < newSignersLength_) { // make sure index to insert is within bounds of newSigners_ array newSigners_[currentInsertPos_] = currentSigners_[i]; } else { // a signer has been passed in that was not found and thus we would be inserting at a position // in newSigners_ array that overflows its length revert AvocadoMultisig__InvalidParams(); } } else { // remove signer, i.e. do not insert the current signer in the newSigners_ array // make sure signer to be removed is not the owner if (removeSigners_[removedCount_] == owner_) { revert AvocadoMultisig__InvalidParams(); } emit SignerRemoved(removeSigners_[removedCount_]); unchecked { ++removedCount_; } } unchecked { ++i; } } if (removedCount_ != removeSignersLength_) { // this case should not be possible but it is a good cheap extra check to make sure nothing goes wrong // and the contract does not end up in an invalid signers state revert AvocadoMultisig__InvalidParams(); } // update values in storage _setSigners(newSigners_, requiredSigners_); // updates `signersCount`, checks and sets `requiredSigners_` // sync mappings at AvoSignersList -> must happen *after* storage write update // use call with success_ here to not block users transaction if the helper contract fails. // in case of failure, only emit event ListSyncFailed() so off-chain tracking is informed to react. (bool success_, ) = address(avoSignersList).call( abi.encodeCall(IAvoSignersList.syncRemoveAvoSignerMappings, (address(this), removeSigners_)) ); if (!success_) { emit ListSyncFailed(); } } /// @notice sets number of required signers for a valid request to `requiredSigners_` function setRequiredSigners(uint8 requiredSigners_) external onlySelf { _setRequiredSigners(requiredSigners_); } } 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 ); (bool validSignature_, ) = _verifySig( getSigDigest(params_, forwardParams_), signaturesParams_, params_.avoNonce == -1 ); // signature must be valid if (!validSignature_) { revert AvocadoMultisig__InvalidSignature(); } return true; } /// @inheritdoc IAvocadoMultisigV1Base function cast( CastParams calldata params_, CastForwardParams calldata forwardParams_, SignatureParams[] memory signaturesParams_ ) external payable returns (bool success_, string memory revertReason_) { bool notSimulate_ = tx.origin != 0x000000000000000000000000000000000000dEaD; { 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(); } } if (notSimulate_) { // @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_ = getSigDigest(params_, forwardParams_); address[] memory signers_; { if (notSimulate_) { bool validSignature_; (validSignature_, signers_) = _verifySig(digest_, signaturesParams_, params_.avoNonce == -1); // signature must be valid if (!validSignature_) { revert AvocadoMultisig__InvalidSignature(); } } } { uint256 reserveGas_; unchecked { reserveGas_ = CAST_EVENTS_RESERVE_GAS + _dynamicReserveGas(signers_.length, params_.metadata.length); } (success_, revertReason_) = _executeCast( params_, reserveGas_, 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()` } } 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 ); } (bool validSignature_, ) = _verifySig( getSigDigestAuthorized(params_, authorizedParams_), signaturesParams_, params_.avoNonce == -1 ); // signature must be valid if (!validSignature_) { revert AvocadoMultisig__InvalidSignature(); } 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_; { bool validSignature_; (validSignature_, signers_) = _verifySig(digest_, signaturesParams_, params_.avoNonce == -1); // signature must be valid if (!validSignature_) { revert AvocadoMultisig__InvalidSignature(); } } { uint256 reserveGas_; unchecked { reserveGas_ = CAST_AUTHORIZED_RESERVE_GAS + _dynamicReserveGas(signers_.length, params_.metadata.length); } (success_, revertReason_) = _executeCast( params_, reserveGas_, 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 _payAuthorizedFee(gasSnapshot_, authorizedParams_.maxFee); // @dev ending point for measuring reserve gas should be here. Also see comment in `AvocadoMultisigCore._executeCast()` } } contract AvocadoMultisig is AvocadoMultisigCore, AvocadoMultisigSelfUpgradeable, AvocadoMultisigProtected, AvocadoMultisigEIP1271, AvocadoMultisigSigners, AvocadoMultisigCast, AvocadoMultisigCastAuthorized { /***********************************| | CONSTRUCTOR / INITIALIZERS | |__________________________________*/ /// @notice constructor sets multiple immutable values for contracts and payFee fallback logic. /// @param avoRegistry_ address of the avoRegistry (proxy) contract /// @param avoForwarder_ address of the avoForwarder (proxy) contract /// to forward tx with valid signatures. must be valid version in AvoRegistry. /// @param avoSignersList_ address of the AvoSignersList (proxy) contract /// @param avoConfigV1_ AvoConfigV1 contract holding values for authorizedFee values constructor( IAvoRegistry avoRegistry_, address avoForwarder_, IAvoSignersList avoSignersList_, IAvoConfigV1 avoConfigV1_ ) AvocadoMultisigCore(avoRegistry_, avoForwarder_, avoSignersList_, avoConfigV1_) {} /// @inheritdoc IAvocadoMultisigV1Base function initialize() public initializer { _initialize(); } /***********************************| | PUBLIC API | |__________________________________*/ receive() external payable {} /// @inheritdoc IAvocadoMultisigV1Base function domainSeparatorV4() public view returns (bytes32) { return _domainSeparatorV4(); } /// @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 { 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"; abstract contract AvocadoMultisigCore is AvocadoMultisigErrors, AvocadoMultisigEvents, AvocadoMultisigVariables, AvocadoMultisigStructs, AvocadoMultisigInitializable, ERC721Holder, ERC1155Holder, InstaFlashReceiverInterface, IERC1271, IAvocadoMultisigV1Base { /// @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(); } } /***********************************| | 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(); } /// @dev sets the initial state of the Multisig for `owner_` as owner and first and only required signer function _initialize() internal { address owner_ = IAvocado(address(this))._owner(); // owner must be EOA if (Address.isContract(owner_) || owner_ == address(0)) { revert AvocadoMultisig__InvalidParams(); } // set _transientAllowHash so refund behaviour is already active for first tx and this cost is applied to deployment _resetTransientStorage(); // emit events emit SignerAdded(owner_); emit RequiredSignersSet(1); // add owner as signer at AvoSignersList address[] memory signers_ = new address[](1); signers_[0] = owner_; // use call with success_ here to not block users transaction if the helper contract fails. // in case of failure, only emit event ListSyncFailed() so off-chain tracking is informed to react. (bool success_, ) = address(avoSignersList).call( abi.encodeCall(IAvoSignersList.syncAddAvoSignerMappings, (address(this), signers_)) ); if (!success_) { emit ListSyncFailed(); } } /***********************************| | INTERNAL | |__________________________________*/ /// @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 7500 // So formula: // Avoado signersCount == 1 ? -> 11_000 gas // Avoado signersCount > 1 ? -> 6400 + allowedSignersCount * 160 + signersLength * 7500 // 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 returns the dynamic reserve gas to be kept back for emitting the CastExecuted or CastFailed event function _dynamicReserveGas( 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_ = (PER_SIGNER_RESERVE_GAS * signersCount_) + (EMIT_EVENT_COST_PER_BYTE * metadataLength_); } } /// @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_), _getRevertReasonFromReturnedData(result_))); } /// @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 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_) internal { // @dev part below costs ~24k gas for if `feeAmount_` and `maxFee_` is set uint256 feeAmount_; address payable feeCollector_; { uint256 gasUsed_; unchecked { // gas can not underflow // gasUsed already includes everything at this point except for paying fee logic gasUsed_ = gasUsedFrom_ - gasleft(); } // Using a low-level function call to prevent reverts (making sure the contract is truly non-custodial). // also limit gas, so that registry can not cause out of gas. (bool success_, bytes memory result_) = address(avoRegistry).staticcall{ gas: 15000 }( abi.encodeWithSignature("calcFee(uint256)", gasUsed_) ); // checks to ensure decoding does not fail, breaking non-custodial feature uint256 addressValue; assembly { addressValue := mload(add(result_, 0x40)) } if (success_ && result_.length > 63 && addressValue <= type(uint160).max) { // result bytes length < 64 or a too long address value would fail the abi.decode and cause revert (feeAmount_, feeCollector_) = abi.decode(result_, (uint256, address)); if (feeAmount_ > AUTHORIZED_MAX_FEE) { // make sure AvoRegistry fee is capped feeAmount_ = AUTHORIZED_MAX_FEE; } } else { // registry calcFee failed. Use local backup minimum fee feeCollector_ = AUTHORIZED_FEE_COLLECTOR; feeAmount_ = AUTHORIZED_MIN_FEE; } } // pay fee, if any if (feeAmount_ > 0) { if (maxFee_ > 0 && feeAmount_ > maxFee_) { revert AvocadoMultisig__MaxFee(feeAmount_, maxFee_); } // sending fee based on OZ Address.sendValue, but modified to properly act based on actual error case // (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/utils/Address.sol#L60) if (address(this).balance < feeAmount_) { revert AvocadoMultisig__InsufficientBalance(feeAmount_); } // Setting gas to very low 1000 because 2_300 gas is added automatically for a .call with a value amount. // This should be enough for any normal transfer to an EOA or an Avocado Multisig. (bool success_, ) = feeCollector_.call{ value: feeAmount_, gas: 1000 }(""); if (success_) { emit FeePaid(feeAmount_); } else { // do not revert, as an error on the feeCollector_ side should not be the "fault" of the Avo contract. // Letting this case pass ensures that the contract is truly non-custodial (not blockable by feeCollector) emit FeePayFailed(feeAmount_); } } else { emit FeePaid(feeAmount_); } } /// @dev builds the digest (hash) used to verify an EIP712 signature /// @param params_ Cast params such as id, avoNonce and actions to execute /// @param functionTypeHash_ whole function type hash, e.g. CAST_TYPE_HASH or CAST_AUTHORIZED_TYPE_HASH /// @param customStructHash_ struct hash added after CastParams hash, e.g. CastForwardParams or CastAuthorizedParams hash /// @return bytes32 digest e.g. for signature or non-sequential nonce function _buildSigDigest( CastParams memory params_, bytes32 functionTypeHash_, bytes32 customStructHash_ ) internal view returns (bytes32) { bytes32[] memory keccakActions_; { // get keccak256s for actions uint256 actionsLength_ = params_.actions.length; keccakActions_ = new bytes32[](actionsLength_); for (uint256 i; i < actionsLength_; ) { keccakActions_[i] = 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 ECDSA.toTypedDataHash( // domain separator _domainSeparatorV4(), // structHash keccak256( abi.encode( functionTypeHash_, // CastParams hash keccak256( abi.encode( CAST_PARAMS_TYPE_HASH, // actions keccak256(abi.encodePacked(keccakActions_)), params_.id, params_.avoNonce, params_.salt, params_.source, keccak256(params_.metadata) ) ), // CastForwardParams or CastAuthorizedParams hash customStructHash_ ) ) ); } /// @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 _buildSigDigest( params_, CAST_TYPE_HASH, // CastForwardParams hash keccak256( abi.encode( CAST_FORWARD_PARAMS_TYPE_HASH, forwardParams_.gas, forwardParams_.gasPrice, forwardParams_.validAfter, forwardParams_.validUntil, forwardParams_.value ) ) ); } /// @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 _buildSigDigest( params_, CAST_AUTHORIZED_TYPE_HASH, // CastAuthorizedParams hash keccak256( abi.encode( CAST_AUTHORIZED_PARAMS_TYPE_HASH, authorizedParams_.maxFee, authorizedParams_.gasPrice, authorizedParams_.validAfter, authorizedParams_.validUntil ) ) ); } /// @dev Returns the domain separator for the chain with id `DEFAULT_CHAIN_ID` function _domainSeparatorV4() internal view returns (bytes32) { return keccak256( abi.encode( TYPE_HASH, DOMAIN_SEPARATOR_NAME_HASHED, DOMAIN_SEPARATOR_VERSION_HASHED, DEFAULT_CHAIN_ID, address(this), DOMAIN_SEPARATOR_SALT_HASHED ) ); } /// @dev Get the revert reason from the returnedData (supports Panic, Error & Custom Errors). /// Based on https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/libs/CallUtils.sol /// This is needed in order to provide some human-readable revert message from a call. /// @param returnedData_ revert data of the call /// @return reason_ revert reason function _getRevertReasonFromReturnedData( bytes memory returnedData_ ) internal pure returns (string memory reason_) { if (returnedData_.length < 4) { // case 1: catch all return "_REASON_NOT_DEFINED"; } bytes4 errorSelector_; assembly { errorSelector_ := mload(add(returnedData_, 0x20)) } if (errorSelector_ == bytes4(0x4e487b71)) { // case 2: Panic(uint256), selector 0x4e487b71 (Defined since 0.8.0) // ref: https://docs.soliditylang.org/en/v0.8.0/control-structures.html#panic-via-assert-and-error-via-require) // convert last byte to hex digits -> string to decode the panic code bytes memory result_ = new bytes(2); result_[0] = _toHexDigit(uint8(returnedData_[returnedData_.length - 1]) / 16); result_[1] = _toHexDigit(uint8(returnedData_[returnedData_.length - 1]) % 16); reason_ = string.concat("_TARGET_PANICKED: 0x", string(result_)); } else if (errorSelector_ == bytes4(0x08c379a0)) { // case 3: Error(string), selector 0x08c379a0 (Defined at least since 0.7.0) // based on https://ethereum.stackexchange.com/a/83577 assembly { returnedData_ := add(returnedData_, 0x04) } reason_ = string.concat("_", abi.decode(returnedData_, (string))); } else { // case 4: Custom errors (Defined since 0.8.0) // convert bytes4 selector to string, params are ignored... // based on https://ethereum.stackexchange.com/a/111876 bytes memory result_ = new bytes(8); for (uint256 i; i < 4; ) { // use unchecked as i is < 4 and division. also errorSelector can not underflow unchecked { result_[2 * i] = _toHexDigit(uint8(errorSelector_[i]) / 16); result_[2 * i + 1] = _toHexDigit(uint8(errorSelector_[i]) % 16); ++i; } } reason_ = string.concat("_CUSTOM_ERROR: 0x", string(result_)); } { // truncate reason_ string to REVERT_REASON_MAX_LENGTH for reserveGas used to ensure Cast event is emitted if (bytes(reason_).length > REVERT_REASON_MAX_LENGTH) { bytes memory reasonBytes_ = bytes(reason_); uint256 maxLength_ = REVERT_REASON_MAX_LENGTH + 1; // cheaper than <= in each loop bytes memory truncatedRevertReason_ = new bytes(maxLength_); for (uint256 i; i < maxLength_; ) { truncatedRevertReason_[i] = reasonBytes_[i]; unchecked { ++i; } } reason_ = string(truncatedRevertReason_); } } } /// @dev used to convert bytes4 selector to string function _toHexDigit(uint8 d) internal pure returns (bytes1) { // use unchecked as the operations with d can not over / underflow unchecked { if (d < 10) { return bytes1(uint8(bytes1("0")) + d); } if (d < 16) { return bytes1(uint8(bytes1("a")) + d - 10); } } revert AvocadoMultisig__ToHexDigit(); } }
// 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 / AvoAuthoritiesList 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; } }
// 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.0.1"; // 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 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 45_000 as reserve gas for `castAuthorized()`. Usually it will cost less but 45_000 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 = 45_000; /// @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. 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)); /// @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; // 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 private _signersPointer; /// @notice signers count required to reach quorom and be able to execute actions uint8 private _requiredSigners; /// @notice number of signers currently listed as allowed signers // // @dev should be updated directly via `_setSigners()` uint8 private _signersCount; // 10 bytes empty /***********************************| | SIGNERS GETTER / SETTER | |__________________________________*/ /// @dev writes `signers_` to storage with SSTORE2 and updates `signersCount`. uses `requiredSigners_` for sanity checks function _setSigners(address[] memory signers_, uint8 requiredSigners_) internal { uint256 signersCount_ = signers_.length; if (signersCount_ > MAX_SIGNERS_COUNT || signersCount_ == 0) { revert AvocadoMultisig__InvalidParams(); } if (signersCount_ == 1) { // if signersCount is 1, owner must be the only signer (checked in `removeSigners`) // can reset to empty "uninitialized" signer vars state, making subsequent interactions cheaper // and even giving a gas refund for clearing out the slot 1 if (requiredSigners_ != 1) { revert AvocadoMultisig__InvalidParams(); } if (_requiredSigners > 1) { emit RequiredSignersSet(1); } assembly { sstore(1, 0) // Reset slot 1 (signers related vars) to 0 } } else { _signersCount = uint8(signersCount_); _signersPointer = SSTORE2.write(abi.encode(signers_)); // required signers vs signersCount is checked in _setRequiredSigners _setRequiredSigners(requiredSigners_); } } /// @dev 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 sets number of required signers to `requiredSigners_` and emits event RequiredSignersSet, if valid function _setRequiredSigners(uint8 requiredSigners_) internal { // check if number of actual signers is > `requiredSigners_` because otherwise // the multisig would end up in a broken state where no execution is possible anymore if (requiredSigners_ == 0 || requiredSigners_ > _getSignersCount()) { revert AvocadoMultisig__InvalidParams(); } if (_requiredSigners != requiredSigners_) { _requiredSigners = requiredSigners_; emit RequiredSignersSet(requiredSigners_); } } /// @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 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 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 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; 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": 800 }, "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"}],"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_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":"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":[],"name":"domainSeparatorV4","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","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":[{"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":[],"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":[{"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"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
4661018090815260206101608190526101a060405290206080523480156200002657600080fd5b50604051620063d3380380620063d38339810160408190526200004991620002bd565b8383838383838383838383836001600160a01b03841615806200007357506001600160a01b038316155b806200008657506001600160a01b038216155b806200009957506001600160a01b038116155b15620000b85760405163ec92598560e01b815260040160405180910390fd5b6001600160a01b0380851660a05283811660c05282811660e05260408051632271f8db60e01b81529051600092841691632271f8db9160048083019260609291908290030181865afa15801562000113573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000139919062000325565b805190915015806200014d57506020810151155b8062000164575060408101516001600160a01b0316155b8062000174575060208101518151115b15620001935760405163ec92598560e01b815260040160405180910390fd5b805161010052602081015161012052604001516001600160a01b03166101405250620001c7965050620001d5945050505050565b505050505050505062000398565b600054600160f81b900460ff1615620002445760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff600160f01b90910481161015620002a2576000805460ff60f01b191660ff60f01b17905560405160ff81527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6001600160a01b0381168114620002ba57600080fd5b50565b60008060008060808587031215620002d457600080fd5b8451620002e181620002a4565b6020860151909450620002f481620002a4565b60408601519093506200030781620002a4565b60608601519092506200031a81620002a4565b939692955090935050565b6000606082840312156200033857600080fd5b604051606081016001600160401b03811182821017156200036957634e487b7160e01b600052604160045260246000fd5b8060405250825181526020830151602082015260408301516200038c81620002a4565b60408201529392505050565b60805160a05160c05160e051610100516101205161014051615f9a62000439600039600081816109880152612f17015260008181610ae501528181612ec60152612eee015260008181610a310152612f3a0152600081816103fb015281816117590152818161239801526133740152600081816107970152611d1e0152600081816107cb015281816118c10152612dc6015260006125800152615f9a6000f3fe60806040526004361061032d5760003560e01c80638da5cb5b116101a5578063bb6fe03d116100ec578063e7e38cd311610095578063ec80dcc21161006f578063ec80dcc214610a53578063ed969f0a14610a73578063f23a6e6114610aa7578063fe6bfed314610ad357600080fd5b8063e7e38cd3146109df578063eb2856e9146109ff578063ec59048314610a1f57600080fd5b8063c7fe944f116100c6578063c7fe944f14610976578063e3a88970146109aa578063e40956b1146109ca57600080fd5b8063bb6fe03d14610902578063bc197c8114610922578063bda443311461094e57600080fd5b8063a08519b51161014e578063a80ebaf111610128578063a80ebaf11461088a578063b4907ddc146108be578063b92e87fa146108ef57600080fd5b8063a08519b514610802578063a0d0619314610822578063a755ecfc1461085657600080fd5b806394f0320e1161017f57806394f0320e146107855780639cae7aa8146107b95780639ee3f883146107ed57600080fd5b80638da5cb5b1461073d578063920f5c84146107525780639428ae4e1461077257600080fd5b8063447f537f116102745780636261b1631161021d57806378e890ba116101f757806378e890ba146106aa5780637df73e27146106bf5780638129fc1c146106df57806387265c95146106f457600080fd5b80636261b1631461063657806364d4c819146106565780636fbc15e91461068a57600080fd5b80634de992e81161024e5780634de992e8146105c15780635b502b40146105e25780636043848a1461061657600080fd5b8063447f537f1461054b57806346f0975a1461057f57806349926a66146105a157600080fd5b80632986c0e5116102d65780633a06cc8c116102b05780633a06cc8c146104e15780633edd91871461051557806343ea19961461053557600080fd5b80632986c0e51461045c5780632a4bf794146104865780632a5d1d66146104a657600080fd5b80631626ba7e116103075780631626ba7e146103c957806316914ac0146103e95780632772aed91461043557600080fd5b806301ffc9a714610339578063044d3cd21461036e578063150b7a021461039057600080fd5b3661033457005b600080fd5b34801561034557600080fd5b5061035961035436600461473e565b610b07565b60405190151581526020015b60405180910390f35b34801561037a57600080fd5b5061038e6103893660046147a0565b610b3e565b005b34801561039c57600080fd5b506103b06103ab36600461493b565b610cb3565b6040516001600160e01b03199091168152602001610365565b3480156103d557600080fd5b506103b06103e43660046149e9565b610cc4565b3480156103f557600080fd5b5061041d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610365565b34801561044157600080fd5b5061044a6110dc565b60405160ff9091168152602001610365565b34801561046857600080fd5b506104716110eb565b60405163ffffffff9091168152602001610365565b34801561049257600080fd5b506103596104a1366004614a59565b611158565b3480156104b257600080fd5b506104d36104c1366004614ad1565b60036020526000908152604090205481565b604051908152602001610365565b3480156104ed57600080fd5b506104d37f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e81565b34801561052157600080fd5b5061038e6105303660046147a0565b6111e7565b34801561054157600080fd5b506104d361027a81565b34801561055757600080fd5b506104d37f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2081565b34801561058b57600080fd5b5061059461127f565b6040516103659190614b2e565b3480156105ad57600080fd5b5061038e6105bc366004614ad1565b611289565b6105d46105cf366004614c51565b6112cc565b604051610365929190614d16565b3480156105ee57600080fd5b506104d37f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c081565b34801561062257600080fd5b5061038e610631366004614d42565b611489565b34801561064257600080fd5b50610359610651366004614d96565b61184e565b34801561066257600080fd5b506104d37fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647281565b34801561069657600080fd5b5061038e6106a5366004614df5565b611886565b3480156106b657600080fd5b506104d36119d3565b3480156106cb57600080fd5b506103596106da366004614e31565b6119dd565b3480156106eb57600080fd5b5061038e611a48565b34801561070057600080fd5b506107306040518060400160405280601081526020016f41766f6361646f2d4d756c746973696760801b81525081565b6040516103659190614e4e565b34801561074957600080fd5b5061041d611ba7565b34801561075e57600080fd5b5061035961076d366004614e61565b611c0b565b6105d4610780366004614f3c565b611d11565b34801561079157600080fd5b5061041d7f000000000000000000000000000000000000000000000000000000000000000081565b3480156107c557600080fd5b5061041d7f000000000000000000000000000000000000000000000000000000000000000081565b3480156107f957600080fd5b506104d3605a81565b34801561080e57600080fd5b5061038e61081d366004614ad1565b611f1c565b34801561082e57600080fd5b506104d37fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b81565b34801561086257600080fd5b506104d37fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4781565b34801561089657600080fd5b506104d37f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6581565b3480156108ca57600080fd5b5061073060405180604001604052806005815260200164312e302e3160d81b81525081565b61038e6108fd366004614f9a565b611fa7565b34801561090e57600080fd5b506104d361091d3660046151aa565b612067565b34801561092e57600080fd5b506103b061093d36600461525f565b63bc197c8160e01b95945050505050565b34801561095a57600080fd5b50600054600160a01b900469ffffffffffffffffffff166104d3565b34801561098257600080fd5b5061041d7f000000000000000000000000000000000000000000000000000000000000000081565b3480156109b657600080fd5b506104d36109c536600461537d565b612073565b3480156109d657600080fd5b5061044a61207f565b3480156109eb57600080fd5b5061038e6109fa366004614df5565b612089565b348015610a0b57600080fd5b5061038e610a1a366004614d42565b612091565b348015610a2b57600080fd5b506104d37f000000000000000000000000000000000000000000000000000000000000000081565b348015610a5f57600080fd5b5061038e610a6e3660046153c3565b61248c565b348015610a7f57600080fd5b506104d37f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb81565b348015610ab357600080fd5b506103b0610ac23660046153de565b63f23a6e6160e01b95945050505050565b348015610adf57600080fd5b506104d37f000000000000000000000000000000000000000000000000000000000000000081565b60006001600160e01b03198216630271189760e51b1480610b3857506301ffc9a760e01b6001600160e01b03198316145b92915050565b610b4661249d565b806000819003610b5557505050565b6005811115610b775760405163ec92598560e01b815260040160405180910390fd5b60008054600160a01b900469ffffffffffffffffffff16905b82811015610c6b5781858583818110610bab57610bab615447565b9050602002016020810190610bc0919061545d565b6affffffffffffffffffffff1603610c0f5760405182907f5e8592bbef3f468eddd77c711ee57100f76bbb79500d8871a700fb6108929d9290600090a281610c07816154a0565b925050610c63565b81858583818110610c2257610c22615447565b9050602002016020810190610c37919061545d565b6affffffffffffffffffffff161115610c635760405163ec92598560e01b815260040160405180910390fd5b600101610b90565b506000805469ffffffffffffffffffff909216600160a01b027fffff00000000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055505050565b630a85bd0160e11b5b949350505050565b6000610d51610cd16124bf565b604080517f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2060208201529081018790526060015b60408051601f19818403018152828252805160209182012061190160f01b8483015260228401949094526042808401949094528151808403909401845260629092019052815191012090565b93506000829003610d9057600084815260026020526040902054600114610d8b5760405163a8398eb960e01b815260040160405180910390fd5b6110cb565b6060826040198101610ea95760408051600180825281830190925290816020015b604080518082019091526060815260006020820152815260200190600190039081610db1579050506040805160606020601f89018190040282018101835291810187815292945091829188908890819085018382808284376000920191909152505050908252506040805163b2bdfa7b60e01b81529051602092830192309263b2bdfa7b92600480830193928290030181865afa158015610e56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7a91906154b9565b6001600160a01b031681525082600081518110610e9957610e99615447565b6020026020010181905250611098565b605a811015610ecb5760405163a8398eb960e01b815260040160405180910390fd5b7fdec0de6520000000000000000000000000000000000000000000000000000000610efa6005600087896154d6565b610f0391615500565b77ffffffffffffffffffffffffffffffffffffffffffffffff1916036110895760556004198201048067ffffffffffffffff811115610f4457610f44614807565b604051908082528060200260200182016040528015610f8a57816020015b604080518082019091526060815260006020820152815260200190600190039081610f625790505b50925060005b8181101561108257605581026046810190600090610fb390605a01838a8c6154d6565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050505060208101516040805180820190915291925060601c908061100e8560401981018d8f6154d6565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506001600160a01b038316602090910152875188908690811061106957611069615447565b6020026020010181905250836001019350505050610f90565b5050611098565b61109584860186615557565b91505b60006110a6878460016125c2565b509050806110c75760405163a8398eb960e01b815260040160405180910390fd5b5050505b50630b135d3f60e11b5b9392505050565b60006110e6612947565b905090565b600060a0306001600160a01b03166368beab3f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561112d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611151919061558c565b901c905090565b600061118261116786806155a5565b90508660400135866040013587606001358860800135612964565b60006111ba6111a2611193886155ef565b6109c5368990038901896155fb565b6111ac8587615617565b8860400135600019146125c2565b509050806111db576040516370a1717960e01b815260040160405180910390fd5b50600195945050505050565b6111ef61249d565b8060005b818110156112795760016003600086868581811061121357611213615447565b9050602002013581526020019081526020016000208190555083838281811061123e5761123e615447565b905060200201357f3749b30151183a20a5e6b93dce343f6010bde0e057441cca9548231f5727b70360405160405180910390a26001016111f3565b50505050565b60606110e6612a14565b61129161249d565b6000818152600260205260408082208290555182917fc93ef95774a42ae5f9ca1e82cc762d17a6c2424041b91bf34dcf23aacc28cf1291a250565b6000606060005a90506112fa6112e287806155a5565b90508760400135876040013588606001356000612964565b6000611317611308886155ef565b61091d36899003890189615624565b90506060600061133083888b60400135600019146125c2565b9250905080611352576040516370a1717960e01b815260040160405180910390fd5b50805160009061137a9061136960a08c018c615640565b905061017290910260089091020190565b61afc801905061139f89828b6040013560001914611399576000612b03565b85612b03565b9096509450851561140f57336113bb60a08b0160808c01614e31565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b846113f360a08e018e615640565b604051611402939291906156b0565b60405180910390a3611472565b3361142060a08b0160808c01614e31565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f848861145960a08f018f615640565b60405161146994939291906156d6565b60405180910390a35b5061147e838835612db7565b505050935093915050565b61149161249d565b8160008190036114b45760405163ec92598560e01b815260040160405180910390fd5b60006114be612a14565b805190915060006114cf848361571b565b90506000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611511573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061153591906154b9565b905060008267ffffffffffffffff81111561155257611552614807565b60405190808252806020026020018201604052801561157b578160200160208202803683370190505b50905060008060005b8681101561172a578181039250888214806115f057508b8b838181106115ac576115ac615447565b90506020020160208101906115c19190614e31565b6001600160a01b03168882815181106115dc576115dc615447565b60200260200101516001600160a01b031614155b15611667578583101561164e5787818151811061160f5761160f615447565b602002602001015184848151811061162957611629615447565b60200260200101906001600160a01b031690816001600160a01b031681525050611722565b60405163ec92598560e01b815260040160405180910390fd5b846001600160a01b03168c8c8481811061168357611683615447565b90506020020160208101906116989190614e31565b6001600160a01b0316036116bf5760405163ec92598560e01b815260040160405180910390fd5b8b8b838181106116d1576116d1615447565b90506020020160208101906116e69190614e31565b6001600160a01b03167f3525e22824a8a7df2c9a6029941c824cf95b6447f1e13d5128fd3826d35afe8b60405160405180910390a28160010191505b600101611584565b5087811461174b5760405163ec92598560e01b815260040160405180910390fd5b611755838a6130b8565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316308d8d6040516024016117969392919061572e565b60408051601f198184030181529181526020820180516001600160e01b031663a1153e7f60e01b179052516117cb919061578a565b6000604051808303816000865af19150503d8060008114611808576040519150601f19603f3d011682016040523d82523d6000602084013e61180d565b606091505b5050905080611840576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b505050505050505050505050565b600061187561185d86806155a5565b90508660400135866040013587606001356000612964565b60006111ba6111a2611308886155ef565b61188e61249d565b6000546001600160a01b038481169116146119ce5760405162fe90e360e01b81526001600160a01b0384811660048301527f0000000000000000000000000000000000000000000000000000000000000000169062fe90e39060240160006040518083038186803b15801561190257600080fd5b505afa158015611916573d6000803e3d6000fd5b50506000805473ffffffffffffffffffffffffffffffffffffffff1981166001600160a01b0388811691821784556040519216945092507fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b9190a26119cb84306001600160a01b031663e7e38cd3848787604051602401611999939291906157a6565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506131ee565b50505b505050565b60006110e66124bf565b6000806119e8612a14565b805190915060005b81811015611a3d57846001600160a01b0316838281518110611a1457611a14615447565b60200260200101516001600160a01b031603611a3557506001949350505050565b6001016119f0565b506000949350505050565b600054600160f81b900460ff1615808015611a7057506000546001600160f01b90910460ff16105b80611a915750303b158015611a915750600054600160f01b900460ff166001145b611b085760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160f01b1790558015611b5257600080546001600160f81b0316600160f81b1790555b611b5a613213565b8015611ba457600080546001600160f81b03169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611be7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110e691906154b9565b604080518082018252601281527f657865637574654f7065617274696f6e282900000000000000000000000000006020918201529051600091611c76918591859142917f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e91016157c9565b60408051601f19818403018152919052805160209091012060365460081b60ff199081169116148015611cb157506001600160a01b03841630145b611cce57604051635e5225b960e01b815260040160405180910390fd5b603680546001600160f81b038116909155600160f81b900460ff16611d00611cf8858501866157ff565b826001613453565b5060019a9950505050505050505050565b6000606061dead321415337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614611d6457604051635e5225b960e01b815260040160405180910390fd5b84355a6101f4011015611d8a5760405163e681bf7d60e01b815260040160405180910390fd5b8015611db857611db8611d9d87806155a5565b90508760400135876040013588606001358960800135612964565b6000611dc6611193886155ef565b905060608215611e09576000611de583888b60400135600019146125c2565b9250905080611e07576040516370a1717960e01b815260040160405180910390fd5b505b8051600090611e1f9061136960a08c018c615640565b612710019050611e3e89828b6040013560001914611399576000612b03565b9096509450508415611eaf5733611e5b60a08a0160808b01614e31565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b83611e9360a08d018d615640565b604051611ea2939291906156b0565b60405180910390a361147e565b33611ec060a08a0160808b01614e31565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f8387611ef960a08e018e615640565b604051611f0994939291906156d6565b60405180910390a3505050935093915050565b611f2461249d565b611f67611f2f6124bf565b604080517f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e206020820152908101849052606001610d05565b600081815260026020526040808220600190555191925082917fbef281d0d1046788f9baa37e73d57e85933f130d8ec1e223366e0b04fbf254029190a250565b604080518082018252600e81526d5f63616c6c54617267657473282960901b60209182015290516120029185918591859142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b9101615915565b60408051601f19818403018152919052805160209091012060365460081b60ff1990811691161480612035575061dead32145b61205257604051635e5225b960e01b815260040160405180910390fd5b6119ce61205f8385615950565b826000613453565b60006110d58383613849565b60006110d583836138f2565b60006110e661398b565b6119ce61249d565b61209961249d565b818015806120d457506000848482816120b4576120b4615447565b90506020020160208101906120c99190614e31565b6001600160a01b0316145b156120f25760405163ec92598560e01b815260040160405180910390fd5b60006120fc612a14565b8051909150600061210d848361595d565b9050605a8111156121315760405163ec92598560e01b815260040160405180910390fd5b60008167ffffffffffffffff81111561214c5761214c614807565b604051908082528060200260200182016040528015612175578160200160208202803683370190505b50905060008060005b84811015612389578181039250878214806121f3575085831080156121f357508a8a838181106121b0576121b0615447565b90506020020160208101906121c59190614e31565b6001600160a01b03168784815181106121e0576121e0615447565b60200260200101516001600160a01b0316105b156122495786838151811061220a5761220a615447565b602002602001015184828151811061222457612224615447565b60200260200101906001600160a01b031690816001600160a01b031681525050612305565b8a8a8381811061225b5761225b615447565b90506020020160208101906122709190614e31565b84828151811061228257612282615447565b60200260200101906001600160a01b031690816001600160a01b0316815250508a8a838181106122b4576122b4615447565b90506020020160208101906122c99190614e31565b6001600160a01b03167f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2460405160405180910390a28160010191505b60008111801561236357508361231c60018361571b565b8151811061232c5761232c615447565b60200260200101516001600160a01b031684828151811061234f5761234f615447565b60200260200101516001600160a01b031611155b156123815760405163ec92598560e01b815260040160405180910390fd5b60010161217e565b5061239483896130b8565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316308c8c6040516024016123d59392919061572e565b60408051601f198184030181529181526020820180516001600160e01b031663863deb1160e01b1790525161240a919061578a565b6000604051808303816000865af19150503d8060008114612447576040519150601f19603f3d011682016040523d82523d6000602084013e61244c565b606091505b505090508061247f576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b5050505050505050505050565b61249461249d565b611ba4816139a7565b3330146124bd57604051635e5225b960e01b815260040160405180910390fd5b565b604080518082018252601081526f41766f6361646f2d4d756c746973696760801b602091820152815180830183526005815264312e302e3160d81b9082015281517fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472818301527ff5d974efd8e94277ef83fff53580c07a80e6e3ffdb2e3fec63185a4e812655a2818401527ffc7f6d936935ae6385924f29da7af79e941070dafe46831a51595892abc1b97a606082015261027a60808201523060a08201527f000000000000000000000000000000000000000000000000000000000000000060c0808301919091528351808303909101815260e0909101909252815191012090565b81516000906060906125d2612947565b60ff168110806125f857508380156125f857506000868152600360205260409020546001145b156126165760405163ec92598560e01b815260040160405180910390fd5b8067ffffffffffffffff81111561262f5761262f614807565b604051908082528060200260200182016040528015612658578160200160208202803683370190505b5091506000612665612a14565b805190915060008080805b86811015612933576126a88b828151811061268d5761268d615447565b6020026020010151602001516001600160a01b03163b151590565b15612706578a81815181106126bf576126bf615447565b6020026020010151602001518882815181106126dd576126dd615447565b60200260200101906001600160a01b031690816001600160a01b031681525050600192506127c7565b61272d8c8c838151811061271c5761271c615447565b602002602001015160000151613a5c565b88828151811061273f5761273f615447565b60200260200101906001600160a01b031690816001600160a01b03168152505087818151811061277157612771615447565b60200260200101516001600160a01b03168b828151811061279457612794615447565b6020026020010151602001516001600160a01b0316146127c75760405163ec92598560e01b815260040160405180910390fd5b835b85811015612833578882815181106127e3576127e3615447565b60200260200101516001600160a01b031687828151811061280657612806615447565b60200260200101516001600160a01b03160361282b5760019250806001019450612833565b6001016127c9565b508161284957600098505050505050505061293f565b60009150821561292b578a51630b135d3f60e11b908c908390811061287057612870615447565b6020026020010151602001516001600160a01b0316631626ba7e8e8e858151811061289d5761289d615447565b6020026020010151600001516040518363ffffffff1660e01b81526004016128c6929190615970565b602060405180830381865afa1580156128e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129079190615989565b6001600160e01b0319161461292657600098505050505050505061293f565b600092505b600101612670565b50600197505050505050505b935093915050565b600154600160a01b900460ff166000819003612961575060015b90565b60008511801561299357508360001914806129935750600054600160a01b900469ffffffffffffffffffff1684145b6129b05760405163ec92598560e01b815260040160405180910390fd5b428311806129c857506000821180156129c857504282105b156129e657604051633c83d07f60e01b815260040160405180910390fd5b6000811180156129f65750803414155b156119cb5760405163ec92598560e01b815260040160405180910390fd5b6001546060906001600160a01b031680612ae1576040805160018082528183019092529060208083019080368337019050509150306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aaa91906154b9565b82600081518110612abd57612abd615447565b60200260200101906001600160a01b031690816001600160a01b0316815250505090565b612aea81613a80565b806020019051810190612afd9190615a0a565b91505090565b60006060612b1185806155a5565b604080518082018252600e81526d5f63616c6c54617267657473282960901b6020918201529051612b6d939291808a01359142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b9101615915565b60408051808303601f190181529190528051602090910120603680547fff000000000000000000000000000000000000000000000000000000000000001660089290921c91909117905582612c0f5760008054600160a01b900469ffffffffffffffffffff16906014612bdf83615a3f565b91906101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff16021790555050612c22565b6000838152600360205260409020600190555b6000612c2e86806155a5565b8760200135604051602401612c4593929190615a68565b60408051601f198184030181529190526020810180516001600160e01b0316635c9743fd60e11b17905290506060609686015a1015612cbd57612c886001603655565b60006040518060400160405280600f81526020016e41564f5f5f4f55545f4f465f47415360881b81525093509350505061293f565b6000808351602085016001600160a01b03600054168a5a03f493503d6040519150601f19601f6020830101168201604052808252806000602084013e5083612da3578051600003612d8657612d1360968761571b565b5a1015612d49576040518060400160405280600f81526020016e41564f5f5f4f55545f4f465f47415360881b8152509250612da3565b6040518060400160405280601781526020017f41564f5f5f524541534f4e5f4e4f545f444546494e45440000000000000000008152509250612da3565b60048101905080806020019051810190612da09190615abc565b92505b612dad6001603655565b5050935093915050565b60008060005a850390506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316613a9884604051602401612e0491815260200190565b60408051601f198184030181529181526020820180516001600160e01b0316631d771f6360e21b17905251612e39919061578a565b6000604051808303818686fa925050503d8060008114612e75576040519150601f19603f3d011682016040523d82523d6000602084013e612e7a565b606091505b5060408101519193509150828015612e935750603f8251115b8015612ea657506001600160a01b038111155b15612f155781806020019051810190612ebf9190615b05565b90965094507f0000000000000000000000000000000000000000000000000000000000000000861115612f10577f000000000000000000000000000000000000000000000000000000000000000095505b612f5c565b7f000000000000000000000000000000000000000000000000000000000000000094507f000000000000000000000000000000000000000000000000000000000000000095505b50505050600082111561308757600083118015612f7857508282115b15612fa05760405163360ceed560e01b81526004810183905260248101849052604401611aff565b81471015612fc45760405163fae5708760e01b815260048101839052602401611aff565b6000816001600160a01b0316836103e890604051600060405180830381858888f193505050503d8060008114613016576040519150601f19603f3d011682016040523d82523d6000602084013e61301b565b606091505b5050905080156130555760405183907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a2613081565b60405183907fd1b3877a34a9595e45447dad16b7354ec5a8bece40075fd60d647e76397b476490600090a25b50611279565b60405182907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a250505050565b8151605a8111806130c7575080155b156130e55760405163ec92598560e01b815260040160405180910390fd5b8060010361315c578160ff166001146131115760405163ec92598560e01b815260040160405180910390fd5b60018054600160a01b900460ff161115613152576040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a25b6000600155505050565b600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16600160a81b60ff8416021790556040516131b8906131a4908590602001614b2e565b604051602081830303815290604052613aa1565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03929092169190911790556119ce826139a7565b60606110d58383604051806060016040528060278152602001615f3e60279139613b52565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613253573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061327791906154b9565b90506001600160a01b0381163b15158061329857506001600160a01b038116155b156132b65760405163ec92598560e01b815260040160405180910390fd5b6132c06001603655565b6040516001600160a01b038216907f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2490600090a26040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a260408051600180825281830190925260009160208083019080368337019050509050818160008151811061335657613356615447565b6001600160a01b0392831660209182029290920101526040516000917f000000000000000000000000000000000000000000000000000000000000000016906133a59030908590602401615b35565b60408051601f198184030181529181526020820180516001600160e01b031663863deb1160e01b179052516133da919061578a565b6000604051808303816000865af19150503d8060008114613417576040519150601f19603f3d011682016040523d82523d6000602084013e61341c565b606091505b50509050806119ce576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a1505050565b61345d6001603655565b600080600084600114806134715750846015145b9050801561348457600054925060015491505b855160005b8181101561383f5760008882815181106134a5576134a5615447565b60200260200101519050600060606000836060015160001480156134de575060028b10806134d357508a6014145b806134de57508a6015145b156135785760405a816134f3576134f3615541565b04905083600001516001600160a01b03168460400151856020015160405161351b919061578a565b60006040518083038185875af1925050503d8060008114613558576040519150601f19603f3d011682016040523d82523d6000602084013e61355d565b606091505b5090935091508261357357613573818684613bca565b613830565b836060015160011480156135895750865b1561367f5760405a8161359e5761359e615541565b04905083600001516001600160a01b031684602001516040516135c1919061578a565b600060405180830381855af49150503d80600081146135fc576040519150601f19603f3d011682016040523d82523d6000602084013e613601565b606091505b5090935091508261361757613617818684613bca565b6136216001603655565b6000546001548a8214801561363557508981145b6136785761364287613c2f565b6040516020016136529190615b57565b60408051601f198184030181529082905262461bcd60e51b8252611aff91600401614e4e565b5050613830565b8360600151600214801561369d57508a6014148061369d57508a6015145b156138175789156136c1576136b185613c2f565b6040516020016136529190615b98565b602084015160048101805190916136e091810160249081019101615bf9565b50604080518082018252601281527f657865637574654f7065617274696f6e28290000000000000000000000000000602091820152905191955061374d94508593504292507f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e9101615cf8565b60408051808303601f19018152918152815160209092019190912060081c600160f81b60ff8f1602176036555a8161378757613787615541565b04915084600001516001600160a01b0316856040015186602001516040516137af919061578a565b60006040518083038185875af1925050503d80600081146137ec576040519150601f19603f3d011682016040523d82523d6000602084013e6137f1565b606091505b5090945092508361380757613807828785613bca565b6138116001603655565b50613830565b61382085613c2f565b6040516020016136529190615d2c565b84600101945050505050613489565b5050505050505050565b60006110d5837f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c07f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb85600001518660200151876040015188606001516040516020016138d7959493929190948552602085019390935260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120613ccf565b60006110d5837fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b7f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e856000015186602001518760400151886060015189608001516040516020016138d796959493929190958652602086019490945260408501929092526060840152608083015260a082015260c00190565b600154600160a81b900460ff1660008190036129615750600190565b60ff811615806139c357506139ba61398b565b60ff168160ff16115b156139e15760405163ec92598560e01b815260040160405180910390fd5b60015460ff828116600160a01b9092041614611ba457600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16600160a01b60ff8416908102919091179091556040517f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a250565b6000806000613a6b8585613f4b565b91509150613a7881613f90565b509392505050565b6060610b38826001613a9c816001600160a01b0384163b61571b565b6140da565b60008082604051602001613ab59190615d6d565b6040516020818303038152906040529050600081604051602001613ad99190615d93565b60405160208183030381529060405290508051602082016000f092506001600160a01b038316613b4b5760405162461bcd60e51b815260206004820152601160248201527f4445504c4f594d454e545f4641494c45440000000000000000000000000000006044820152606401611aff565b5050919050565b6060600080856001600160a01b031685604051613b6f919061578a565b600060405180830381855af49150503d8060008114613baa576040519150601f19603f3d011682016040523d82523d6000602084013e613baf565b606091505b5091509150613bc0868383876140fd565b9695505050505050565b825a1015613c0c5760405162461bcd60e51b815260206004820152600f60248201526e41564f5f5f4f55545f4f465f47415360881b6044820152606401611aff565b613c1582613c2f565b613c1e82614176565b604051602001613652929190615dd8565b60606000613c3c83614511565b600101905060008167ffffffffffffffff811115613c5c57613c5c614807565b6040519080825280601f01601f191660200182016040528015613c86576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a8504945084613c9057509392505050565b8251516000906060908067ffffffffffffffff811115613cf157613cf1614807565b604051908082528060200260200182016040528015613d1a578160200160208202803683370190505b50915060005b81811015613e4e577f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6587600001518281518110613d5f57613d5f615447565b60200260200101516000015188600001518381518110613d8157613d81615447565b6020026020010151602001518051906020012089600001518481518110613daa57613daa615447565b6020026020010151604001518a600001518581518110613dcc57613dcc615447565b602002602001015160600151604051602001613e139594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120838281518110613e3b57613e3b615447565b6020908102919091010152600101613d20565b5050613f42613e5b6124bf565b857fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4784604051602001613e8e9190615e07565b6040516020818303038152906040528051906020012089602001518a604001518b606001518c608001518d60a0015180519060200120604051602001613f0e979695949392919096875260208701959095526040860193909352606085019190915260808401526001600160a01b031660a083015260c082015260e00190565b60408051601f1981840301815282825280516020918201209083019390935281019190915260608101869052608001610d05565b95945050505050565b6000808251604103613f815760208301516040840151606085015160001a613f75878285856145f3565b94509450505050613f89565b506000905060025b9250929050565b6000816004811115613fa457613fa4615e3d565b03613fac5750565b6001816004811115613fc057613fc0615e3d565b0361400d5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611aff565b600281600481111561402157614021615e3d565b0361406e5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611aff565b600381600481111561408257614082615e3d565b03611ba45760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401611aff565b60408051603f8301601f19168101909152818152818360208301863c9392505050565b6060831561416c578251600003614165576001600160a01b0385163b6141655760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611aff565b5081610cbc565b610cbc83836146b7565b60606004825110156141bb57505060408051808201909152601381527f5f524541534f4e5f4e4f545f444546494e454400000000000000000000000000602082015290565b602082015163b1b7848f60e01b6001600160e01b03198216016142e6576040805160028082528183019092526000916020820181803683370190505090506142346010856001875161420d919061571b565b8151811061421d5761421d615447565b016020015161422f919060f81c615e53565b6146e1565b8160008151811061424757614247615447565b60200101906001600160f81b031916908160001a90535061429460108560018751614272919061571b565b8151811061428257614282615447565b016020015161422f919060f81c615e75565b816001815181106142a7576142a7615447565b60200101906001600160f81b031916908160001a905350806040516020016142cf9190615e97565b604051602081830303815290604052925050614449565b6307b9e43360e51b6001600160e01b031982160161433e57600483019250828060200190518101906143189190615abc565b6040516020016143289190615edc565b6040516020818303038152906040529150614449565b60408051600880825281830190925260009160208201818036833701905050905060005b600481101561442457614396601084836004811061438257614382615447565b1a8161439057614390615541565b046146e1565b8282600202815181106143ab576143ab615447565b60200101906001600160f81b031916908160001a9053506143ed60108483600481106143d9576143d9615447565b1a816143e7576143e7615541565b066146e1565b82826002026001018151811061440557614405615447565b60200101906001600160f81b031916908160001a905350600101614362565b50806040516020016144369190615ef8565b6040516020818303038152906040529250505b60fa8251111561450b5781600061446260fa600161595d565b905060008167ffffffffffffffff81111561447f5761447f614807565b6040519080825280601f01601f1916602001820160405280156144a9576020820181803683370190505b50905060005b82811015614505578381815181106144c9576144c9615447565b602001015160f81c60f81b8282815181106144e6576144e6615447565b60200101906001600160f81b031916908160001a9053506001016144af565b50935050505b50919050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061455a577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310614586576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106145a457662386f26fc10000830492506010015b6305f5e10083106145bc576305f5e100830492506008015b61271083106145d057612710830492506004015b606483106145e2576064830492506002015b600a8310610b385760010192915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561462a57506000905060036146ae565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561467e573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166146a7576000600192509250506146ae565b9150600090505b94509492505050565b8151156146c75781518083602001fd5b8060405162461bcd60e51b8152600401611aff9190614e4e565b6000600a8260ff1610156146f9575060300160f81b90565b60108260ff16101561470f575060570160f81b90565b604051639778fcab60e01b815260040160405180910390fd5b6001600160e01b031981168114611ba457600080fd5b60006020828403121561475057600080fd5b81356110d581614728565b60008083601f84011261476d57600080fd5b50813567ffffffffffffffff81111561478557600080fd5b6020830191508360208260051b8501011115613f8957600080fd5b600080602083850312156147b357600080fd5b823567ffffffffffffffff8111156147ca57600080fd5b6147d68582860161475b565b90969095509350505050565b6001600160a01b0381168114611ba457600080fd5b8035614802816147e2565b919050565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561484057614840614807565b60405290565b6040516080810167ffffffffffffffff8111828210171561484057614840614807565b60405160c0810167ffffffffffffffff8111828210171561484057614840614807565b604051601f8201601f1916810167ffffffffffffffff811182821017156148b5576148b5614807565b604052919050565b600067ffffffffffffffff8211156148d7576148d7614807565b50601f01601f191660200190565b600082601f8301126148f657600080fd5b8135614909614904826148bd565b61488c565b81815284602083860101111561491e57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561495157600080fd5b843561495c816147e2565b9350602085013561496c816147e2565b925060408501359150606085013567ffffffffffffffff81111561498f57600080fd5b61499b878288016148e5565b91505092959194509250565b60008083601f8401126149b957600080fd5b50813567ffffffffffffffff8111156149d157600080fd5b602083019150836020828501011115613f8957600080fd5b6000806000604084860312156149fe57600080fd5b83359250602084013567ffffffffffffffff811115614a1c57600080fd5b614a28868287016149a7565b9497909650939450505050565b600060c0828403121561450b57600080fd5b600060a0828403121561450b57600080fd5b60008060008060e08587031215614a6f57600080fd5b843567ffffffffffffffff80821115614a8757600080fd5b614a9388838901614a35565b9550614aa28860208901614a47565b945060c0870135915080821115614ab857600080fd5b50614ac58782880161475b565b95989497509550505050565b600060208284031215614ae357600080fd5b5035919050565b600081518084526020808501945080840160005b83811015614b235781516001600160a01b031687529582019590820190600101614afe565b509495945050505050565b6020815260006110d56020830184614aea565b60006080828403121561450b57600080fd5b600067ffffffffffffffff821115614b6d57614b6d614807565b5060051b60200190565b6000614b8561490484614b53565b8381529050602080820190600585901b840186811115614ba457600080fd5b845b81811015614c2657803567ffffffffffffffff80821115614bc75760008081fd5b908701906040828b031215614bdc5760008081fd5b614be461481d565b823582811115614bf45760008081fd5b614c008c8286016148e5565b8252509185013591614c11836147e2565b80860192909252508452928201928201614ba6565b505050509392505050565b600082601f830112614c4257600080fd5b6110d583833560208501614b77565b600080600060c08486031215614c6657600080fd5b833567ffffffffffffffff80821115614c7e57600080fd5b614c8a87838801614a35565b9450614c998760208801614b41565b935060a0860135915080821115614caf57600080fd5b50614cbc86828701614c31565b9150509250925092565b60005b83811015614ce1578181015183820152602001614cc9565b50506000910152565b60008151808452614d02816020860160208601614cc6565b601f01601f19169290920160200192915050565b8215158152604060208201526000610cbc6040830184614cea565b803560ff8116811461480257600080fd5b600080600060408486031215614d5757600080fd5b833567ffffffffffffffff811115614d6e57600080fd5b614d7a8682870161475b565b9094509250614d8d905060208501614d31565b90509250925092565b60008060008060c08587031215614dac57600080fd5b843567ffffffffffffffff80821115614dc457600080fd5b614dd088838901614a35565b9550614ddf8860208901614b41565b945060a0870135915080821115614ab857600080fd5b600080600060408486031215614e0a57600080fd5b8335614e15816147e2565b9250602084013567ffffffffffffffff811115614a1c57600080fd5b600060208284031215614e4357600080fd5b81356110d5816147e2565b6020815260006110d56020830184614cea565b600080600080600080600080600060a08a8c031215614e7f57600080fd5b893567ffffffffffffffff80821115614e9757600080fd5b614ea38d838e0161475b565b909b50995060208c0135915080821115614ebc57600080fd5b614ec88d838e0161475b565b909950975060408c0135915080821115614ee157600080fd5b614eed8d838e0161475b565b909750955060608c01359150614f02826147e2565b90935060808b01359080821115614f1857600080fd5b50614f258c828d016149a7565b915080935050809150509295985092959850929598565b600080600060e08486031215614f5157600080fd5b833567ffffffffffffffff80821115614f6957600080fd5b614f7587838801614a35565b9450614f848760208801614a47565b935060c0860135915080821115614caf57600080fd5b600080600060408486031215614faf57600080fd5b833567ffffffffffffffff811115614fc657600080fd5b614fd28682870161475b565b909790965060209590950135949350505050565b6000614ff461490484614b53565b8381529050602080820190600585901b84018681111561501357600080fd5b845b81811015614c2657803567ffffffffffffffff808211156150365760008081fd5b908701906080828b03121561504b5760008081fd5b615053614846565b823561505e816147e2565b815282860135828111156150725760008081fd5b61507e8c8286016148e5565b82880152506040838101359082015260609283013592810192909252508452928201928201615015565b600082601f8301126150b957600080fd5b6110d583833560208501614fe6565b600060c082840312156150da57600080fd5b6150e2614869565b9050813567ffffffffffffffff808211156150fc57600080fd5b615108858386016150a8565b8352602084013560208401526040840135604084015260608401356060840152615134608085016147f7565b608084015260a084013591508082111561514d57600080fd5b5061515a848285016148e5565b60a08301525092915050565b60006080828403121561517857600080fd5b615180614846565b90508135815260208201356020820152604082013560408201526060820135606082015292915050565b60008060a083850312156151bd57600080fd5b823567ffffffffffffffff8111156151d457600080fd5b6151e0858286016150c8565b9250506151f08460208501615166565b90509250929050565b600082601f83011261520a57600080fd5b8135602061521a61490483614b53565b82815260059290921b8401810191818101908684111561523957600080fd5b8286015b84811015615254578035835291830191830161523d565b509695505050505050565b600080600080600060a0868803121561527757600080fd5b8535615282816147e2565b94506020860135615292816147e2565b9350604086013567ffffffffffffffff808211156152af57600080fd5b6152bb89838a016151f9565b945060608801359150808211156152d157600080fd5b6152dd89838a016151f9565b935060808801359150808211156152f357600080fd5b50615300888289016148e5565b9150509295509295909350565b600060a0828403121561531f57600080fd5b60405160a0810181811067ffffffffffffffff8211171561534257615342614807565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b60008060c0838503121561539057600080fd5b823567ffffffffffffffff8111156153a757600080fd5b6153b3858286016150c8565b9250506151f0846020850161530d565b6000602082840312156153d557600080fd5b6110d582614d31565b600080600080600060a086880312156153f657600080fd5b8535615401816147e2565b94506020860135615411816147e2565b93506040860135925060608601359150608086013567ffffffffffffffff81111561543b57600080fd5b615300888289016148e5565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561546f57600080fd5b81356affffffffffffffffffffff811681146110d557600080fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016154b2576154b261548a565b5060010190565b6000602082840312156154cb57600080fd5b81516110d5816147e2565b600080858511156154e657600080fd5b838611156154f357600080fd5b5050820193919092039150565b77ffffffffffffffffffffffffffffffffffffffffffffffff1981358181169160088510156155395780818660080360031b1b83161692505b505092915050565b634e487b7160e01b600052601260045260246000fd5b60006020828403121561556957600080fd5b813567ffffffffffffffff81111561558057600080fd5b610cbc84828501614c31565b60006020828403121561559e57600080fd5b5051919050565b6000808335601e198436030181126155bc57600080fd5b83018035915067ffffffffffffffff8211156155d757600080fd5b6020019150600581901b3603821315613f8957600080fd5b6000610b3836836150c8565b600060a0828403121561560d57600080fd5b6110d5838361530d565b60006110d5368484614b77565b60006080828403121561563657600080fd5b6110d58383615166565b6000808335601e1984360301811261565757600080fd5b83018035915067ffffffffffffffff82111561567257600080fd5b602001915036819003821315613f8957600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6040815260006156c36040830186614aea565b8281036020840152613bc0818587615687565b6060815260006156e96060830187614aea565b82810360208401526156fb8187614cea565b90508281036040840152615710818587615687565b979650505050505050565b81810381811115610b3857610b3861548a565b6001600160a01b03848116825260406020808401829052908301849052600091859160608501845b8781101561577d578435615769816147e2565b841682529382019390820190600101615756565b5098975050505050505050565b6000825161579c818460208701614cc6565b9190910192915050565b6001600160a01b0384168152604060208201526000613f42604083018486615687565b6060815260006157dd606083018688615687565b6020830194909452506001600160e01b03199190911660409091015292915050565b60006020828403121561581157600080fd5b813567ffffffffffffffff81111561582857600080fd5b610cbc848285016150a8565b81835260006020808501808196508560051b810191508460005b878110156159085782840389528135607e1988360301811261586f57600080fd5b87016080813561587e816147e2565b6001600160a01b031686528187013536839003601e190181126158a057600080fd5b8201878101903567ffffffffffffffff8111156158bc57600080fd5b8036038213156158cb57600080fd5b82898901526158dd8389018284615687565b604085810135908a01526060948501359490980193909352505050978401979084019060010161584e565b5091979650505050505050565b608081526000615929608083018789615834565b60208301959095525060408101929092526001600160e01b03191660609091015292915050565b60006110d5368484614fe6565b80820180821115610b3857610b3861548a565b828152604060208201526000610cbc6040830184614cea565b60006020828403121561599b57600080fd5b81516110d581614728565b600082601f8301126159b757600080fd5b815160206159c761490483614b53565b82815260059290921b840181019181810190868411156159e657600080fd5b8286015b848110156152545780516159fd816147e2565b83529183019183016159ea565b600060208284031215615a1c57600080fd5b815167ffffffffffffffff811115615a3357600080fd5b610cbc848285016159a6565b600069ffffffffffffffffffff808316818103615a5e57615a5e61548a565b6001019392505050565b604081526000615a7c604083018587615834565b9050826020830152949350505050565b6000615a9a614904846148bd565b9050828152838383011115615aae57600080fd5b6110d5836020830184614cc6565b600060208284031215615ace57600080fd5b815167ffffffffffffffff811115615ae557600080fd5b8201601f81018413615af657600080fd5b610cbc84825160208401615a8c565b60008060408385031215615b1857600080fd5b825191506020830151615b2a816147e2565b809150509250929050565b6001600160a01b0383168152604060208201526000610cbc6040830184614aea565b60008251615b69818460208701614cc6565b7f5f41564f5f5f4d4f4449464945445f53544f5241474500000000000000000000920191825250601601919050565b60008251615baa818460208701614cc6565b7f5f41564f5f5f4e4f5f464c4153484c4f414e5f494e5f464c4153484c4f414e00920191825250601f01919050565b600082601f830112615bea57600080fd5b6110d583835160208501615a8c565b600080600080600060a08688031215615c1157600080fd5b855167ffffffffffffffff80821115615c2957600080fd5b615c3589838a016159a6565b9650602091508188015181811115615c4c57600080fd5b8801601f81018a13615c5d57600080fd5b8051615c6b61490482614b53565b81815260059190911b8201840190848101908c831115615c8a57600080fd5b928501925b82841015615ca857835182529285019290850190615c8f565b60408c015160608d0151919a509850945050505080821115615cc957600080fd5b615cd589838a01615bd9565b93506080880151915080821115615ceb57600080fd5b5061530088828901615bd9565b606081526000615d0b6060830186614cea565b6020830194909452506001600160e01b031991909116604090910152919050565b60008251615d3e818460208701614cc6565b7f5f41564f5f5f494e56414c49445f49445f4f525f4f5045524154494f4e000000920191825250601d01919050565b6000815260008251615d86816001850160208701614cc6565b9190910160010192915050565b7f600b5981380380925939f3000000000000000000000000000000000000000000815260008251615dcb81600b850160208701614cc6565b91909101600b0192915050565b60008351615dea818460208801614cc6565b835190830190615dfe818360208801614cc6565b01949350505050565b815160009082906020808601845b83811015615e3157815185529382019390820190600101615e15565b50929695505050505050565b634e487b7160e01b600052602160045260246000fd5b600060ff831680615e6657615e66615541565b8060ff84160491505092915050565b600060ff831680615e8857615e88615541565b8060ff84160691505092915050565b7f5f5441524745545f50414e49434b45443a203078000000000000000000000000815260008251615ecf816014850160208701614cc6565b9190910160140192915050565b605f60f81b815260008251615d86816001850160208701614cc6565b7f5f435553544f4d5f4552524f523a203078000000000000000000000000000000815260008251615f30816011850160208701614cc6565b919091016011019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220cd91ad13e114e9caf3d92898ea7b0fb17783d416b9039f744a0764c609f514b764736f6c63430008120033000000000000000000000000779385ec7a04242259add4990e3130846f80ea6900000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068480000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d
Deployed Bytecode
0x60806040526004361061032d5760003560e01c80638da5cb5b116101a5578063bb6fe03d116100ec578063e7e38cd311610095578063ec80dcc21161006f578063ec80dcc214610a53578063ed969f0a14610a73578063f23a6e6114610aa7578063fe6bfed314610ad357600080fd5b8063e7e38cd3146109df578063eb2856e9146109ff578063ec59048314610a1f57600080fd5b8063c7fe944f116100c6578063c7fe944f14610976578063e3a88970146109aa578063e40956b1146109ca57600080fd5b8063bb6fe03d14610902578063bc197c8114610922578063bda443311461094e57600080fd5b8063a08519b51161014e578063a80ebaf111610128578063a80ebaf11461088a578063b4907ddc146108be578063b92e87fa146108ef57600080fd5b8063a08519b514610802578063a0d0619314610822578063a755ecfc1461085657600080fd5b806394f0320e1161017f57806394f0320e146107855780639cae7aa8146107b95780639ee3f883146107ed57600080fd5b80638da5cb5b1461073d578063920f5c84146107525780639428ae4e1461077257600080fd5b8063447f537f116102745780636261b1631161021d57806378e890ba116101f757806378e890ba146106aa5780637df73e27146106bf5780638129fc1c146106df57806387265c95146106f457600080fd5b80636261b1631461063657806364d4c819146106565780636fbc15e91461068a57600080fd5b80634de992e81161024e5780634de992e8146105c15780635b502b40146105e25780636043848a1461061657600080fd5b8063447f537f1461054b57806346f0975a1461057f57806349926a66146105a157600080fd5b80632986c0e5116102d65780633a06cc8c116102b05780633a06cc8c146104e15780633edd91871461051557806343ea19961461053557600080fd5b80632986c0e51461045c5780632a4bf794146104865780632a5d1d66146104a657600080fd5b80631626ba7e116103075780631626ba7e146103c957806316914ac0146103e95780632772aed91461043557600080fd5b806301ffc9a714610339578063044d3cd21461036e578063150b7a021461039057600080fd5b3661033457005b600080fd5b34801561034557600080fd5b5061035961035436600461473e565b610b07565b60405190151581526020015b60405180910390f35b34801561037a57600080fd5b5061038e6103893660046147a0565b610b3e565b005b34801561039c57600080fd5b506103b06103ab36600461493b565b610cb3565b6040516001600160e01b03199091168152602001610365565b3480156103d557600080fd5b506103b06103e43660046149e9565b610cc4565b3480156103f557600080fd5b5061041d7f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc00684881565b6040516001600160a01b039091168152602001610365565b34801561044157600080fd5b5061044a6110dc565b60405160ff9091168152602001610365565b34801561046857600080fd5b506104716110eb565b60405163ffffffff9091168152602001610365565b34801561049257600080fd5b506103596104a1366004614a59565b611158565b3480156104b257600080fd5b506104d36104c1366004614ad1565b60036020526000908152604090205481565b604051908152602001610365565b3480156104ed57600080fd5b506104d37f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e81565b34801561052157600080fd5b5061038e6105303660046147a0565b6111e7565b34801561054157600080fd5b506104d361027a81565b34801561055757600080fd5b506104d37f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2081565b34801561058b57600080fd5b5061059461127f565b6040516103659190614b2e565b3480156105ad57600080fd5b5061038e6105bc366004614ad1565b611289565b6105d46105cf366004614c51565b6112cc565b604051610365929190614d16565b3480156105ee57600080fd5b506104d37f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c081565b34801561062257600080fd5b5061038e610631366004614d42565b611489565b34801561064257600080fd5b50610359610651366004614d96565b61184e565b34801561066257600080fd5b506104d37fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647281565b34801561069657600080fd5b5061038e6106a5366004614df5565b611886565b3480156106b657600080fd5b506104d36119d3565b3480156106cb57600080fd5b506103596106da366004614e31565b6119dd565b3480156106eb57600080fd5b5061038e611a48565b34801561070057600080fd5b506107306040518060400160405280601081526020016f41766f6361646f2d4d756c746973696760801b81525081565b6040516103659190614e4e565b34801561074957600080fd5b5061041d611ba7565b34801561075e57600080fd5b5061035961076d366004614e61565b611c0b565b6105d4610780366004614f3c565b611d11565b34801561079157600080fd5b5061041d7f00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a5481565b3480156107c557600080fd5b5061041d7f000000000000000000000000779385ec7a04242259add4990e3130846f80ea6981565b3480156107f957600080fd5b506104d3605a81565b34801561080e57600080fd5b5061038e61081d366004614ad1565b611f1c565b34801561082e57600080fd5b506104d37fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b81565b34801561086257600080fd5b506104d37fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4781565b34801561089657600080fd5b506104d37f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6581565b3480156108ca57600080fd5b5061073060405180604001604052806005815260200164312e302e3160d81b81525081565b61038e6108fd366004614f9a565b611fa7565b34801561090e57600080fd5b506104d361091d3660046151aa565b612067565b34801561092e57600080fd5b506103b061093d36600461525f565b63bc197c8160e01b95945050505050565b34801561095a57600080fd5b50600054600160a01b900469ffffffffffffffffffff166104d3565b34801561098257600080fd5b5061041d7f000000000000000000000000e8385fb3a5f15ded06eb5e20e5a81bf43115eb8e81565b3480156109b657600080fd5b506104d36109c536600461537d565b612073565b3480156109d657600080fd5b5061044a61207f565b3480156109eb57600080fd5b5061038e6109fa366004614df5565b612089565b348015610a0b57600080fd5b5061038e610a1a366004614d42565b612091565b348015610a2b57600080fd5b506104d37f0000000000000000000000000000000000000000000000000001c6bf5263400081565b348015610a5f57600080fd5b5061038e610a6e3660046153c3565b61248c565b348015610a7f57600080fd5b506104d37f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb81565b348015610ab357600080fd5b506103b0610ac23660046153de565b63f23a6e6160e01b95945050505050565b348015610adf57600080fd5b506104d37f00000000000000000000000000000000000000000000000000b1a2bc2ec5000081565b60006001600160e01b03198216630271189760e51b1480610b3857506301ffc9a760e01b6001600160e01b03198316145b92915050565b610b4661249d565b806000819003610b5557505050565b6005811115610b775760405163ec92598560e01b815260040160405180910390fd5b60008054600160a01b900469ffffffffffffffffffff16905b82811015610c6b5781858583818110610bab57610bab615447565b9050602002016020810190610bc0919061545d565b6affffffffffffffffffffff1603610c0f5760405182907f5e8592bbef3f468eddd77c711ee57100f76bbb79500d8871a700fb6108929d9290600090a281610c07816154a0565b925050610c63565b81858583818110610c2257610c22615447565b9050602002016020810190610c37919061545d565b6affffffffffffffffffffff161115610c635760405163ec92598560e01b815260040160405180910390fd5b600101610b90565b506000805469ffffffffffffffffffff909216600160a01b027fffff00000000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055505050565b630a85bd0160e11b5b949350505050565b6000610d51610cd16124bf565b604080517f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2060208201529081018790526060015b60408051601f19818403018152828252805160209182012061190160f01b8483015260228401949094526042808401949094528151808403909401845260629092019052815191012090565b93506000829003610d9057600084815260026020526040902054600114610d8b5760405163a8398eb960e01b815260040160405180910390fd5b6110cb565b6060826040198101610ea95760408051600180825281830190925290816020015b604080518082019091526060815260006020820152815260200190600190039081610db1579050506040805160606020601f89018190040282018101835291810187815292945091829188908890819085018382808284376000920191909152505050908252506040805163b2bdfa7b60e01b81529051602092830192309263b2bdfa7b92600480830193928290030181865afa158015610e56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7a91906154b9565b6001600160a01b031681525082600081518110610e9957610e99615447565b6020026020010181905250611098565b605a811015610ecb5760405163a8398eb960e01b815260040160405180910390fd5b7fdec0de6520000000000000000000000000000000000000000000000000000000610efa6005600087896154d6565b610f0391615500565b77ffffffffffffffffffffffffffffffffffffffffffffffff1916036110895760556004198201048067ffffffffffffffff811115610f4457610f44614807565b604051908082528060200260200182016040528015610f8a57816020015b604080518082019091526060815260006020820152815260200190600190039081610f625790505b50925060005b8181101561108257605581026046810190600090610fb390605a01838a8c6154d6565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050505060208101516040805180820190915291925060601c908061100e8560401981018d8f6154d6565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506001600160a01b038316602090910152875188908690811061106957611069615447565b6020026020010181905250836001019350505050610f90565b5050611098565b61109584860186615557565b91505b60006110a6878460016125c2565b509050806110c75760405163a8398eb960e01b815260040160405180910390fd5b5050505b50630b135d3f60e11b5b9392505050565b60006110e6612947565b905090565b600060a0306001600160a01b03166368beab3f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561112d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611151919061558c565b901c905090565b600061118261116786806155a5565b90508660400135866040013587606001358860800135612964565b60006111ba6111a2611193886155ef565b6109c5368990038901896155fb565b6111ac8587615617565b8860400135600019146125c2565b509050806111db576040516370a1717960e01b815260040160405180910390fd5b50600195945050505050565b6111ef61249d565b8060005b818110156112795760016003600086868581811061121357611213615447565b9050602002013581526020019081526020016000208190555083838281811061123e5761123e615447565b905060200201357f3749b30151183a20a5e6b93dce343f6010bde0e057441cca9548231f5727b70360405160405180910390a26001016111f3565b50505050565b60606110e6612a14565b61129161249d565b6000818152600260205260408082208290555182917fc93ef95774a42ae5f9ca1e82cc762d17a6c2424041b91bf34dcf23aacc28cf1291a250565b6000606060005a90506112fa6112e287806155a5565b90508760400135876040013588606001356000612964565b6000611317611308886155ef565b61091d36899003890189615624565b90506060600061133083888b60400135600019146125c2565b9250905080611352576040516370a1717960e01b815260040160405180910390fd5b50805160009061137a9061136960a08c018c615640565b905061017290910260089091020190565b61afc801905061139f89828b6040013560001914611399576000612b03565b85612b03565b9096509450851561140f57336113bb60a08b0160808c01614e31565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b846113f360a08e018e615640565b604051611402939291906156b0565b60405180910390a3611472565b3361142060a08b0160808c01614e31565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f848861145960a08f018f615640565b60405161146994939291906156d6565b60405180910390a35b5061147e838835612db7565b505050935093915050565b61149161249d565b8160008190036114b45760405163ec92598560e01b815260040160405180910390fd5b60006114be612a14565b805190915060006114cf848361571b565b90506000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611511573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061153591906154b9565b905060008267ffffffffffffffff81111561155257611552614807565b60405190808252806020026020018201604052801561157b578160200160208202803683370190505b50905060008060005b8681101561172a578181039250888214806115f057508b8b838181106115ac576115ac615447565b90506020020160208101906115c19190614e31565b6001600160a01b03168882815181106115dc576115dc615447565b60200260200101516001600160a01b031614155b15611667578583101561164e5787818151811061160f5761160f615447565b602002602001015184848151811061162957611629615447565b60200260200101906001600160a01b031690816001600160a01b031681525050611722565b60405163ec92598560e01b815260040160405180910390fd5b846001600160a01b03168c8c8481811061168357611683615447565b90506020020160208101906116989190614e31565b6001600160a01b0316036116bf5760405163ec92598560e01b815260040160405180910390fd5b8b8b838181106116d1576116d1615447565b90506020020160208101906116e69190614e31565b6001600160a01b03167f3525e22824a8a7df2c9a6029941c824cf95b6447f1e13d5128fd3826d35afe8b60405160405180910390a28160010191505b600101611584565b5087811461174b5760405163ec92598560e01b815260040160405180910390fd5b611755838a6130b8565b60007f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068486001600160a01b0316308d8d6040516024016117969392919061572e565b60408051601f198184030181529181526020820180516001600160e01b031663a1153e7f60e01b179052516117cb919061578a565b6000604051808303816000865af19150503d8060008114611808576040519150601f19603f3d011682016040523d82523d6000602084013e61180d565b606091505b5050905080611840576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b505050505050505050505050565b600061187561185d86806155a5565b90508660400135866040013587606001356000612964565b60006111ba6111a2611308886155ef565b61188e61249d565b6000546001600160a01b038481169116146119ce5760405162fe90e360e01b81526001600160a01b0384811660048301527f000000000000000000000000779385ec7a04242259add4990e3130846f80ea69169062fe90e39060240160006040518083038186803b15801561190257600080fd5b505afa158015611916573d6000803e3d6000fd5b50506000805473ffffffffffffffffffffffffffffffffffffffff1981166001600160a01b0388811691821784556040519216945092507fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b9190a26119cb84306001600160a01b031663e7e38cd3848787604051602401611999939291906157a6565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506131ee565b50505b505050565b60006110e66124bf565b6000806119e8612a14565b805190915060005b81811015611a3d57846001600160a01b0316838281518110611a1457611a14615447565b60200260200101516001600160a01b031603611a3557506001949350505050565b6001016119f0565b506000949350505050565b600054600160f81b900460ff1615808015611a7057506000546001600160f01b90910460ff16105b80611a915750303b158015611a915750600054600160f01b900460ff166001145b611b085760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160f01b1790558015611b5257600080546001600160f81b0316600160f81b1790555b611b5a613213565b8015611ba457600080546001600160f81b03169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611be7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110e691906154b9565b604080518082018252601281527f657865637574654f7065617274696f6e282900000000000000000000000000006020918201529051600091611c76918591859142917f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e91016157c9565b60408051601f19818403018152919052805160209091012060365460081b60ff199081169116148015611cb157506001600160a01b03841630145b611cce57604051635e5225b960e01b815260040160405180910390fd5b603680546001600160f81b038116909155600160f81b900460ff16611d00611cf8858501866157ff565b826001613453565b5060019a9950505050505050505050565b6000606061dead321415337f00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a546001600160a01b031614611d6457604051635e5225b960e01b815260040160405180910390fd5b84355a6101f4011015611d8a5760405163e681bf7d60e01b815260040160405180910390fd5b8015611db857611db8611d9d87806155a5565b90508760400135876040013588606001358960800135612964565b6000611dc6611193886155ef565b905060608215611e09576000611de583888b60400135600019146125c2565b9250905080611e07576040516370a1717960e01b815260040160405180910390fd5b505b8051600090611e1f9061136960a08c018c615640565b612710019050611e3e89828b6040013560001914611399576000612b03565b9096509450508415611eaf5733611e5b60a08a0160808b01614e31565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b83611e9360a08d018d615640565b604051611ea2939291906156b0565b60405180910390a361147e565b33611ec060a08a0160808b01614e31565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f8387611ef960a08e018e615640565b604051611f0994939291906156d6565b60405180910390a3505050935093915050565b611f2461249d565b611f67611f2f6124bf565b604080517f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e206020820152908101849052606001610d05565b600081815260026020526040808220600190555191925082917fbef281d0d1046788f9baa37e73d57e85933f130d8ec1e223366e0b04fbf254029190a250565b604080518082018252600e81526d5f63616c6c54617267657473282960901b60209182015290516120029185918591859142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b9101615915565b60408051601f19818403018152919052805160209091012060365460081b60ff1990811691161480612035575061dead32145b61205257604051635e5225b960e01b815260040160405180910390fd5b6119ce61205f8385615950565b826000613453565b60006110d58383613849565b60006110d583836138f2565b60006110e661398b565b6119ce61249d565b61209961249d565b818015806120d457506000848482816120b4576120b4615447565b90506020020160208101906120c99190614e31565b6001600160a01b0316145b156120f25760405163ec92598560e01b815260040160405180910390fd5b60006120fc612a14565b8051909150600061210d848361595d565b9050605a8111156121315760405163ec92598560e01b815260040160405180910390fd5b60008167ffffffffffffffff81111561214c5761214c614807565b604051908082528060200260200182016040528015612175578160200160208202803683370190505b50905060008060005b84811015612389578181039250878214806121f3575085831080156121f357508a8a838181106121b0576121b0615447565b90506020020160208101906121c59190614e31565b6001600160a01b03168784815181106121e0576121e0615447565b60200260200101516001600160a01b0316105b156122495786838151811061220a5761220a615447565b602002602001015184828151811061222457612224615447565b60200260200101906001600160a01b031690816001600160a01b031681525050612305565b8a8a8381811061225b5761225b615447565b90506020020160208101906122709190614e31565b84828151811061228257612282615447565b60200260200101906001600160a01b031690816001600160a01b0316815250508a8a838181106122b4576122b4615447565b90506020020160208101906122c99190614e31565b6001600160a01b03167f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2460405160405180910390a28160010191505b60008111801561236357508361231c60018361571b565b8151811061232c5761232c615447565b60200260200101516001600160a01b031684828151811061234f5761234f615447565b60200260200101516001600160a01b031611155b156123815760405163ec92598560e01b815260040160405180910390fd5b60010161217e565b5061239483896130b8565b60007f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068486001600160a01b0316308c8c6040516024016123d59392919061572e565b60408051601f198184030181529181526020820180516001600160e01b031663863deb1160e01b1790525161240a919061578a565b6000604051808303816000865af19150503d8060008114612447576040519150601f19603f3d011682016040523d82523d6000602084013e61244c565b606091505b505090508061247f576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b5050505050505050505050565b61249461249d565b611ba4816139a7565b3330146124bd57604051635e5225b960e01b815260040160405180910390fd5b565b604080518082018252601081526f41766f6361646f2d4d756c746973696760801b602091820152815180830183526005815264312e302e3160d81b9082015281517fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472818301527ff5d974efd8e94277ef83fff53580c07a80e6e3ffdb2e3fec63185a4e812655a2818401527ffc7f6d936935ae6385924f29da7af79e941070dafe46831a51595892abc1b97a606082015261027a60808201523060a08201527f6e452848784197f00927d379e3db9e69a5131d2269f862bfcd05a0b38f6abf7f60c0808301919091528351808303909101815260e0909101909252815191012090565b81516000906060906125d2612947565b60ff168110806125f857508380156125f857506000868152600360205260409020546001145b156126165760405163ec92598560e01b815260040160405180910390fd5b8067ffffffffffffffff81111561262f5761262f614807565b604051908082528060200260200182016040528015612658578160200160208202803683370190505b5091506000612665612a14565b805190915060008080805b86811015612933576126a88b828151811061268d5761268d615447565b6020026020010151602001516001600160a01b03163b151590565b15612706578a81815181106126bf576126bf615447565b6020026020010151602001518882815181106126dd576126dd615447565b60200260200101906001600160a01b031690816001600160a01b031681525050600192506127c7565b61272d8c8c838151811061271c5761271c615447565b602002602001015160000151613a5c565b88828151811061273f5761273f615447565b60200260200101906001600160a01b031690816001600160a01b03168152505087818151811061277157612771615447565b60200260200101516001600160a01b03168b828151811061279457612794615447565b6020026020010151602001516001600160a01b0316146127c75760405163ec92598560e01b815260040160405180910390fd5b835b85811015612833578882815181106127e3576127e3615447565b60200260200101516001600160a01b031687828151811061280657612806615447565b60200260200101516001600160a01b03160361282b5760019250806001019450612833565b6001016127c9565b508161284957600098505050505050505061293f565b60009150821561292b578a51630b135d3f60e11b908c908390811061287057612870615447565b6020026020010151602001516001600160a01b0316631626ba7e8e8e858151811061289d5761289d615447565b6020026020010151600001516040518363ffffffff1660e01b81526004016128c6929190615970565b602060405180830381865afa1580156128e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129079190615989565b6001600160e01b0319161461292657600098505050505050505061293f565b600092505b600101612670565b50600197505050505050505b935093915050565b600154600160a01b900460ff166000819003612961575060015b90565b60008511801561299357508360001914806129935750600054600160a01b900469ffffffffffffffffffff1684145b6129b05760405163ec92598560e01b815260040160405180910390fd5b428311806129c857506000821180156129c857504282105b156129e657604051633c83d07f60e01b815260040160405180910390fd5b6000811180156129f65750803414155b156119cb5760405163ec92598560e01b815260040160405180910390fd5b6001546060906001600160a01b031680612ae1576040805160018082528183019092529060208083019080368337019050509150306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aaa91906154b9565b82600081518110612abd57612abd615447565b60200260200101906001600160a01b031690816001600160a01b0316815250505090565b612aea81613a80565b806020019051810190612afd9190615a0a565b91505090565b60006060612b1185806155a5565b604080518082018252600e81526d5f63616c6c54617267657473282960901b6020918201529051612b6d939291808a01359142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b9101615915565b60408051808303601f190181529190528051602090910120603680547fff000000000000000000000000000000000000000000000000000000000000001660089290921c91909117905582612c0f5760008054600160a01b900469ffffffffffffffffffff16906014612bdf83615a3f565b91906101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff16021790555050612c22565b6000838152600360205260409020600190555b6000612c2e86806155a5565b8760200135604051602401612c4593929190615a68565b60408051601f198184030181529190526020810180516001600160e01b0316635c9743fd60e11b17905290506060609686015a1015612cbd57612c886001603655565b60006040518060400160405280600f81526020016e41564f5f5f4f55545f4f465f47415360881b81525093509350505061293f565b6000808351602085016001600160a01b03600054168a5a03f493503d6040519150601f19601f6020830101168201604052808252806000602084013e5083612da3578051600003612d8657612d1360968761571b565b5a1015612d49576040518060400160405280600f81526020016e41564f5f5f4f55545f4f465f47415360881b8152509250612da3565b6040518060400160405280601781526020017f41564f5f5f524541534f4e5f4e4f545f444546494e45440000000000000000008152509250612da3565b60048101905080806020019051810190612da09190615abc565b92505b612dad6001603655565b5050935093915050565b60008060005a850390506000807f000000000000000000000000779385ec7a04242259add4990e3130846f80ea696001600160a01b0316613a9884604051602401612e0491815260200190565b60408051601f198184030181529181526020820180516001600160e01b0316631d771f6360e21b17905251612e39919061578a565b6000604051808303818686fa925050503d8060008114612e75576040519150601f19603f3d011682016040523d82523d6000602084013e612e7a565b606091505b5060408101519193509150828015612e935750603f8251115b8015612ea657506001600160a01b038111155b15612f155781806020019051810190612ebf9190615b05565b90965094507f00000000000000000000000000000000000000000000000000b1a2bc2ec50000861115612f10577f00000000000000000000000000000000000000000000000000b1a2bc2ec5000095505b612f5c565b7f000000000000000000000000e8385fb3a5f15ded06eb5e20e5a81bf43115eb8e94507f0000000000000000000000000000000000000000000000000001c6bf5263400095505b50505050600082111561308757600083118015612f7857508282115b15612fa05760405163360ceed560e01b81526004810183905260248101849052604401611aff565b81471015612fc45760405163fae5708760e01b815260048101839052602401611aff565b6000816001600160a01b0316836103e890604051600060405180830381858888f193505050503d8060008114613016576040519150601f19603f3d011682016040523d82523d6000602084013e61301b565b606091505b5050905080156130555760405183907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a2613081565b60405183907fd1b3877a34a9595e45447dad16b7354ec5a8bece40075fd60d647e76397b476490600090a25b50611279565b60405182907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a250505050565b8151605a8111806130c7575080155b156130e55760405163ec92598560e01b815260040160405180910390fd5b8060010361315c578160ff166001146131115760405163ec92598560e01b815260040160405180910390fd5b60018054600160a01b900460ff161115613152576040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a25b6000600155505050565b600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16600160a81b60ff8416021790556040516131b8906131a4908590602001614b2e565b604051602081830303815290604052613aa1565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03929092169190911790556119ce826139a7565b60606110d58383604051806060016040528060278152602001615f3e60279139613b52565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613253573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061327791906154b9565b90506001600160a01b0381163b15158061329857506001600160a01b038116155b156132b65760405163ec92598560e01b815260040160405180910390fd5b6132c06001603655565b6040516001600160a01b038216907f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2490600090a26040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a260408051600180825281830190925260009160208083019080368337019050509050818160008151811061335657613356615447565b6001600160a01b0392831660209182029290920101526040516000917f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc00684816906133a59030908590602401615b35565b60408051601f198184030181529181526020820180516001600160e01b031663863deb1160e01b179052516133da919061578a565b6000604051808303816000865af19150503d8060008114613417576040519150601f19603f3d011682016040523d82523d6000602084013e61341c565b606091505b50509050806119ce576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a1505050565b61345d6001603655565b600080600084600114806134715750846015145b9050801561348457600054925060015491505b855160005b8181101561383f5760008882815181106134a5576134a5615447565b60200260200101519050600060606000836060015160001480156134de575060028b10806134d357508a6014145b806134de57508a6015145b156135785760405a816134f3576134f3615541565b04905083600001516001600160a01b03168460400151856020015160405161351b919061578a565b60006040518083038185875af1925050503d8060008114613558576040519150601f19603f3d011682016040523d82523d6000602084013e61355d565b606091505b5090935091508261357357613573818684613bca565b613830565b836060015160011480156135895750865b1561367f5760405a8161359e5761359e615541565b04905083600001516001600160a01b031684602001516040516135c1919061578a565b600060405180830381855af49150503d80600081146135fc576040519150601f19603f3d011682016040523d82523d6000602084013e613601565b606091505b5090935091508261361757613617818684613bca565b6136216001603655565b6000546001548a8214801561363557508981145b6136785761364287613c2f565b6040516020016136529190615b57565b60408051601f198184030181529082905262461bcd60e51b8252611aff91600401614e4e565b5050613830565b8360600151600214801561369d57508a6014148061369d57508a6015145b156138175789156136c1576136b185613c2f565b6040516020016136529190615b98565b602084015160048101805190916136e091810160249081019101615bf9565b50604080518082018252601281527f657865637574654f7065617274696f6e28290000000000000000000000000000602091820152905191955061374d94508593504292507f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e9101615cf8565b60408051808303601f19018152918152815160209092019190912060081c600160f81b60ff8f1602176036555a8161378757613787615541565b04915084600001516001600160a01b0316856040015186602001516040516137af919061578a565b60006040518083038185875af1925050503d80600081146137ec576040519150601f19603f3d011682016040523d82523d6000602084013e6137f1565b606091505b5090945092508361380757613807828785613bca565b6138116001603655565b50613830565b61382085613c2f565b6040516020016136529190615d2c565b84600101945050505050613489565b5050505050505050565b60006110d5837f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c07f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb85600001518660200151876040015188606001516040516020016138d7959493929190948552602085019390935260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120613ccf565b60006110d5837fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b7f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e856000015186602001518760400151886060015189608001516040516020016138d796959493929190958652602086019490945260408501929092526060840152608083015260a082015260c00190565b600154600160a81b900460ff1660008190036129615750600190565b60ff811615806139c357506139ba61398b565b60ff168160ff16115b156139e15760405163ec92598560e01b815260040160405180910390fd5b60015460ff828116600160a01b9092041614611ba457600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16600160a01b60ff8416908102919091179091556040517f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a250565b6000806000613a6b8585613f4b565b91509150613a7881613f90565b509392505050565b6060610b38826001613a9c816001600160a01b0384163b61571b565b6140da565b60008082604051602001613ab59190615d6d565b6040516020818303038152906040529050600081604051602001613ad99190615d93565b60405160208183030381529060405290508051602082016000f092506001600160a01b038316613b4b5760405162461bcd60e51b815260206004820152601160248201527f4445504c4f594d454e545f4641494c45440000000000000000000000000000006044820152606401611aff565b5050919050565b6060600080856001600160a01b031685604051613b6f919061578a565b600060405180830381855af49150503d8060008114613baa576040519150601f19603f3d011682016040523d82523d6000602084013e613baf565b606091505b5091509150613bc0868383876140fd565b9695505050505050565b825a1015613c0c5760405162461bcd60e51b815260206004820152600f60248201526e41564f5f5f4f55545f4f465f47415360881b6044820152606401611aff565b613c1582613c2f565b613c1e82614176565b604051602001613652929190615dd8565b60606000613c3c83614511565b600101905060008167ffffffffffffffff811115613c5c57613c5c614807565b6040519080825280601f01601f191660200182016040528015613c86576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a8504945084613c9057509392505050565b8251516000906060908067ffffffffffffffff811115613cf157613cf1614807565b604051908082528060200260200182016040528015613d1a578160200160208202803683370190505b50915060005b81811015613e4e577f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6587600001518281518110613d5f57613d5f615447565b60200260200101516000015188600001518381518110613d8157613d81615447565b6020026020010151602001518051906020012089600001518481518110613daa57613daa615447565b6020026020010151604001518a600001518581518110613dcc57613dcc615447565b602002602001015160600151604051602001613e139594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120838281518110613e3b57613e3b615447565b6020908102919091010152600101613d20565b5050613f42613e5b6124bf565b857fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4784604051602001613e8e9190615e07565b6040516020818303038152906040528051906020012089602001518a604001518b606001518c608001518d60a0015180519060200120604051602001613f0e979695949392919096875260208701959095526040860193909352606085019190915260808401526001600160a01b031660a083015260c082015260e00190565b60408051601f1981840301815282825280516020918201209083019390935281019190915260608101869052608001610d05565b95945050505050565b6000808251604103613f815760208301516040840151606085015160001a613f75878285856145f3565b94509450505050613f89565b506000905060025b9250929050565b6000816004811115613fa457613fa4615e3d565b03613fac5750565b6001816004811115613fc057613fc0615e3d565b0361400d5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611aff565b600281600481111561402157614021615e3d565b0361406e5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611aff565b600381600481111561408257614082615e3d565b03611ba45760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401611aff565b60408051603f8301601f19168101909152818152818360208301863c9392505050565b6060831561416c578251600003614165576001600160a01b0385163b6141655760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611aff565b5081610cbc565b610cbc83836146b7565b60606004825110156141bb57505060408051808201909152601381527f5f524541534f4e5f4e4f545f444546494e454400000000000000000000000000602082015290565b602082015163b1b7848f60e01b6001600160e01b03198216016142e6576040805160028082528183019092526000916020820181803683370190505090506142346010856001875161420d919061571b565b8151811061421d5761421d615447565b016020015161422f919060f81c615e53565b6146e1565b8160008151811061424757614247615447565b60200101906001600160f81b031916908160001a90535061429460108560018751614272919061571b565b8151811061428257614282615447565b016020015161422f919060f81c615e75565b816001815181106142a7576142a7615447565b60200101906001600160f81b031916908160001a905350806040516020016142cf9190615e97565b604051602081830303815290604052925050614449565b6307b9e43360e51b6001600160e01b031982160161433e57600483019250828060200190518101906143189190615abc565b6040516020016143289190615edc565b6040516020818303038152906040529150614449565b60408051600880825281830190925260009160208201818036833701905050905060005b600481101561442457614396601084836004811061438257614382615447565b1a8161439057614390615541565b046146e1565b8282600202815181106143ab576143ab615447565b60200101906001600160f81b031916908160001a9053506143ed60108483600481106143d9576143d9615447565b1a816143e7576143e7615541565b066146e1565b82826002026001018151811061440557614405615447565b60200101906001600160f81b031916908160001a905350600101614362565b50806040516020016144369190615ef8565b6040516020818303038152906040529250505b60fa8251111561450b5781600061446260fa600161595d565b905060008167ffffffffffffffff81111561447f5761447f614807565b6040519080825280601f01601f1916602001820160405280156144a9576020820181803683370190505b50905060005b82811015614505578381815181106144c9576144c9615447565b602001015160f81c60f81b8282815181106144e6576144e6615447565b60200101906001600160f81b031916908160001a9053506001016144af565b50935050505b50919050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061455a577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310614586576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106145a457662386f26fc10000830492506010015b6305f5e10083106145bc576305f5e100830492506008015b61271083106145d057612710830492506004015b606483106145e2576064830492506002015b600a8310610b385760010192915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561462a57506000905060036146ae565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561467e573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166146a7576000600192509250506146ae565b9150600090505b94509492505050565b8151156146c75781518083602001fd5b8060405162461bcd60e51b8152600401611aff9190614e4e565b6000600a8260ff1610156146f9575060300160f81b90565b60108260ff16101561470f575060570160f81b90565b604051639778fcab60e01b815260040160405180910390fd5b6001600160e01b031981168114611ba457600080fd5b60006020828403121561475057600080fd5b81356110d581614728565b60008083601f84011261476d57600080fd5b50813567ffffffffffffffff81111561478557600080fd5b6020830191508360208260051b8501011115613f8957600080fd5b600080602083850312156147b357600080fd5b823567ffffffffffffffff8111156147ca57600080fd5b6147d68582860161475b565b90969095509350505050565b6001600160a01b0381168114611ba457600080fd5b8035614802816147e2565b919050565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561484057614840614807565b60405290565b6040516080810167ffffffffffffffff8111828210171561484057614840614807565b60405160c0810167ffffffffffffffff8111828210171561484057614840614807565b604051601f8201601f1916810167ffffffffffffffff811182821017156148b5576148b5614807565b604052919050565b600067ffffffffffffffff8211156148d7576148d7614807565b50601f01601f191660200190565b600082601f8301126148f657600080fd5b8135614909614904826148bd565b61488c565b81815284602083860101111561491e57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561495157600080fd5b843561495c816147e2565b9350602085013561496c816147e2565b925060408501359150606085013567ffffffffffffffff81111561498f57600080fd5b61499b878288016148e5565b91505092959194509250565b60008083601f8401126149b957600080fd5b50813567ffffffffffffffff8111156149d157600080fd5b602083019150836020828501011115613f8957600080fd5b6000806000604084860312156149fe57600080fd5b83359250602084013567ffffffffffffffff811115614a1c57600080fd5b614a28868287016149a7565b9497909650939450505050565b600060c0828403121561450b57600080fd5b600060a0828403121561450b57600080fd5b60008060008060e08587031215614a6f57600080fd5b843567ffffffffffffffff80821115614a8757600080fd5b614a9388838901614a35565b9550614aa28860208901614a47565b945060c0870135915080821115614ab857600080fd5b50614ac58782880161475b565b95989497509550505050565b600060208284031215614ae357600080fd5b5035919050565b600081518084526020808501945080840160005b83811015614b235781516001600160a01b031687529582019590820190600101614afe565b509495945050505050565b6020815260006110d56020830184614aea565b60006080828403121561450b57600080fd5b600067ffffffffffffffff821115614b6d57614b6d614807565b5060051b60200190565b6000614b8561490484614b53565b8381529050602080820190600585901b840186811115614ba457600080fd5b845b81811015614c2657803567ffffffffffffffff80821115614bc75760008081fd5b908701906040828b031215614bdc5760008081fd5b614be461481d565b823582811115614bf45760008081fd5b614c008c8286016148e5565b8252509185013591614c11836147e2565b80860192909252508452928201928201614ba6565b505050509392505050565b600082601f830112614c4257600080fd5b6110d583833560208501614b77565b600080600060c08486031215614c6657600080fd5b833567ffffffffffffffff80821115614c7e57600080fd5b614c8a87838801614a35565b9450614c998760208801614b41565b935060a0860135915080821115614caf57600080fd5b50614cbc86828701614c31565b9150509250925092565b60005b83811015614ce1578181015183820152602001614cc9565b50506000910152565b60008151808452614d02816020860160208601614cc6565b601f01601f19169290920160200192915050565b8215158152604060208201526000610cbc6040830184614cea565b803560ff8116811461480257600080fd5b600080600060408486031215614d5757600080fd5b833567ffffffffffffffff811115614d6e57600080fd5b614d7a8682870161475b565b9094509250614d8d905060208501614d31565b90509250925092565b60008060008060c08587031215614dac57600080fd5b843567ffffffffffffffff80821115614dc457600080fd5b614dd088838901614a35565b9550614ddf8860208901614b41565b945060a0870135915080821115614ab857600080fd5b600080600060408486031215614e0a57600080fd5b8335614e15816147e2565b9250602084013567ffffffffffffffff811115614a1c57600080fd5b600060208284031215614e4357600080fd5b81356110d5816147e2565b6020815260006110d56020830184614cea565b600080600080600080600080600060a08a8c031215614e7f57600080fd5b893567ffffffffffffffff80821115614e9757600080fd5b614ea38d838e0161475b565b909b50995060208c0135915080821115614ebc57600080fd5b614ec88d838e0161475b565b909950975060408c0135915080821115614ee157600080fd5b614eed8d838e0161475b565b909750955060608c01359150614f02826147e2565b90935060808b01359080821115614f1857600080fd5b50614f258c828d016149a7565b915080935050809150509295985092959850929598565b600080600060e08486031215614f5157600080fd5b833567ffffffffffffffff80821115614f6957600080fd5b614f7587838801614a35565b9450614f848760208801614a47565b935060c0860135915080821115614caf57600080fd5b600080600060408486031215614faf57600080fd5b833567ffffffffffffffff811115614fc657600080fd5b614fd28682870161475b565b909790965060209590950135949350505050565b6000614ff461490484614b53565b8381529050602080820190600585901b84018681111561501357600080fd5b845b81811015614c2657803567ffffffffffffffff808211156150365760008081fd5b908701906080828b03121561504b5760008081fd5b615053614846565b823561505e816147e2565b815282860135828111156150725760008081fd5b61507e8c8286016148e5565b82880152506040838101359082015260609283013592810192909252508452928201928201615015565b600082601f8301126150b957600080fd5b6110d583833560208501614fe6565b600060c082840312156150da57600080fd5b6150e2614869565b9050813567ffffffffffffffff808211156150fc57600080fd5b615108858386016150a8565b8352602084013560208401526040840135604084015260608401356060840152615134608085016147f7565b608084015260a084013591508082111561514d57600080fd5b5061515a848285016148e5565b60a08301525092915050565b60006080828403121561517857600080fd5b615180614846565b90508135815260208201356020820152604082013560408201526060820135606082015292915050565b60008060a083850312156151bd57600080fd5b823567ffffffffffffffff8111156151d457600080fd5b6151e0858286016150c8565b9250506151f08460208501615166565b90509250929050565b600082601f83011261520a57600080fd5b8135602061521a61490483614b53565b82815260059290921b8401810191818101908684111561523957600080fd5b8286015b84811015615254578035835291830191830161523d565b509695505050505050565b600080600080600060a0868803121561527757600080fd5b8535615282816147e2565b94506020860135615292816147e2565b9350604086013567ffffffffffffffff808211156152af57600080fd5b6152bb89838a016151f9565b945060608801359150808211156152d157600080fd5b6152dd89838a016151f9565b935060808801359150808211156152f357600080fd5b50615300888289016148e5565b9150509295509295909350565b600060a0828403121561531f57600080fd5b60405160a0810181811067ffffffffffffffff8211171561534257615342614807565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b60008060c0838503121561539057600080fd5b823567ffffffffffffffff8111156153a757600080fd5b6153b3858286016150c8565b9250506151f0846020850161530d565b6000602082840312156153d557600080fd5b6110d582614d31565b600080600080600060a086880312156153f657600080fd5b8535615401816147e2565b94506020860135615411816147e2565b93506040860135925060608601359150608086013567ffffffffffffffff81111561543b57600080fd5b615300888289016148e5565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561546f57600080fd5b81356affffffffffffffffffffff811681146110d557600080fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016154b2576154b261548a565b5060010190565b6000602082840312156154cb57600080fd5b81516110d5816147e2565b600080858511156154e657600080fd5b838611156154f357600080fd5b5050820193919092039150565b77ffffffffffffffffffffffffffffffffffffffffffffffff1981358181169160088510156155395780818660080360031b1b83161692505b505092915050565b634e487b7160e01b600052601260045260246000fd5b60006020828403121561556957600080fd5b813567ffffffffffffffff81111561558057600080fd5b610cbc84828501614c31565b60006020828403121561559e57600080fd5b5051919050565b6000808335601e198436030181126155bc57600080fd5b83018035915067ffffffffffffffff8211156155d757600080fd5b6020019150600581901b3603821315613f8957600080fd5b6000610b3836836150c8565b600060a0828403121561560d57600080fd5b6110d5838361530d565b60006110d5368484614b77565b60006080828403121561563657600080fd5b6110d58383615166565b6000808335601e1984360301811261565757600080fd5b83018035915067ffffffffffffffff82111561567257600080fd5b602001915036819003821315613f8957600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6040815260006156c36040830186614aea565b8281036020840152613bc0818587615687565b6060815260006156e96060830187614aea565b82810360208401526156fb8187614cea565b90508281036040840152615710818587615687565b979650505050505050565b81810381811115610b3857610b3861548a565b6001600160a01b03848116825260406020808401829052908301849052600091859160608501845b8781101561577d578435615769816147e2565b841682529382019390820190600101615756565b5098975050505050505050565b6000825161579c818460208701614cc6565b9190910192915050565b6001600160a01b0384168152604060208201526000613f42604083018486615687565b6060815260006157dd606083018688615687565b6020830194909452506001600160e01b03199190911660409091015292915050565b60006020828403121561581157600080fd5b813567ffffffffffffffff81111561582857600080fd5b610cbc848285016150a8565b81835260006020808501808196508560051b810191508460005b878110156159085782840389528135607e1988360301811261586f57600080fd5b87016080813561587e816147e2565b6001600160a01b031686528187013536839003601e190181126158a057600080fd5b8201878101903567ffffffffffffffff8111156158bc57600080fd5b8036038213156158cb57600080fd5b82898901526158dd8389018284615687565b604085810135908a01526060948501359490980193909352505050978401979084019060010161584e565b5091979650505050505050565b608081526000615929608083018789615834565b60208301959095525060408101929092526001600160e01b03191660609091015292915050565b60006110d5368484614fe6565b80820180821115610b3857610b3861548a565b828152604060208201526000610cbc6040830184614cea565b60006020828403121561599b57600080fd5b81516110d581614728565b600082601f8301126159b757600080fd5b815160206159c761490483614b53565b82815260059290921b840181019181810190868411156159e657600080fd5b8286015b848110156152545780516159fd816147e2565b83529183019183016159ea565b600060208284031215615a1c57600080fd5b815167ffffffffffffffff811115615a3357600080fd5b610cbc848285016159a6565b600069ffffffffffffffffffff808316818103615a5e57615a5e61548a565b6001019392505050565b604081526000615a7c604083018587615834565b9050826020830152949350505050565b6000615a9a614904846148bd565b9050828152838383011115615aae57600080fd5b6110d5836020830184614cc6565b600060208284031215615ace57600080fd5b815167ffffffffffffffff811115615ae557600080fd5b8201601f81018413615af657600080fd5b610cbc84825160208401615a8c565b60008060408385031215615b1857600080fd5b825191506020830151615b2a816147e2565b809150509250929050565b6001600160a01b0383168152604060208201526000610cbc6040830184614aea565b60008251615b69818460208701614cc6565b7f5f41564f5f5f4d4f4449464945445f53544f5241474500000000000000000000920191825250601601919050565b60008251615baa818460208701614cc6565b7f5f41564f5f5f4e4f5f464c4153484c4f414e5f494e5f464c4153484c4f414e00920191825250601f01919050565b600082601f830112615bea57600080fd5b6110d583835160208501615a8c565b600080600080600060a08688031215615c1157600080fd5b855167ffffffffffffffff80821115615c2957600080fd5b615c3589838a016159a6565b9650602091508188015181811115615c4c57600080fd5b8801601f81018a13615c5d57600080fd5b8051615c6b61490482614b53565b81815260059190911b8201840190848101908c831115615c8a57600080fd5b928501925b82841015615ca857835182529285019290850190615c8f565b60408c015160608d0151919a509850945050505080821115615cc957600080fd5b615cd589838a01615bd9565b93506080880151915080821115615ceb57600080fd5b5061530088828901615bd9565b606081526000615d0b6060830186614cea565b6020830194909452506001600160e01b031991909116604090910152919050565b60008251615d3e818460208701614cc6565b7f5f41564f5f5f494e56414c49445f49445f4f525f4f5045524154494f4e000000920191825250601d01919050565b6000815260008251615d86816001850160208701614cc6565b9190910160010192915050565b7f600b5981380380925939f3000000000000000000000000000000000000000000815260008251615dcb81600b850160208701614cc6565b91909101600b0192915050565b60008351615dea818460208801614cc6565b835190830190615dfe818360208801614cc6565b01949350505050565b815160009082906020808601845b83811015615e3157815185529382019390820190600101615e15565b50929695505050505050565b634e487b7160e01b600052602160045260246000fd5b600060ff831680615e6657615e66615541565b8060ff84160491505092915050565b600060ff831680615e8857615e88615541565b8060ff84160691505092915050565b7f5f5441524745545f50414e49434b45443a203078000000000000000000000000815260008251615ecf816014850160208701614cc6565b9190910160140192915050565b605f60f81b815260008251615d86816001850160208701614cc6565b7f5f435553544f4d5f4552524f523a203078000000000000000000000000000000815260008251615f30816011850160208701614cc6565b919091016011019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220cd91ad13e114e9caf3d92898ea7b0fb17783d416b9039f744a0764c609f514b764736f6c63430008120033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000779385ec7a04242259add4990e3130846f80ea6900000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068480000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d
-----Decoded View---------------
Arg [0] : avoRegistry_ (address): 0x779385EC7A04242259AdD4990e3130846f80eA69
Arg [1] : avoForwarder_ (address): 0x46978CD477A496028A18c02F07ab7F35EDBa5A54
Arg [2] : avoSignersList_ (address): 0xA27A71DD0348b5C69370eF9Da3743197DC006848
Arg [3] : avoConfigV1_ (address): 0x1412121E487478498b355C227c77D5ED6CF1798d
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000779385ec7a04242259add4990e3130846f80ea69
Arg [1] : 00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54
Arg [2] : 000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc006848
Arg [3] : 0000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d
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.