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 | |||
---|---|---|---|---|---|---|
410511 | 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.0 /// @notice Smart wallet enabling meta transactions through multiple EIP712 signatures (Multisig n out of m). /// /// Supports: /// - Executing arbitrary actions /// - Receiving NFTs (ERC721) /// - Receiving ERC1155 tokens /// - ERC1271 smart contract signatures /// - Instadapp Flashloan callbacks /// /// 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. function isValidSignature( bytes32 hash, bytes calldata signature ) external view override returns (bytes4 magicValue) { // @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 function signMessage(bytes32 message_) external onlySelf { _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(); } 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 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_); } } /// @notice 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_ ) ) ); } /// @notice 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 ) ) ); } /// @notice 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 ) ) ); } /// @notice 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 is not allowed 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 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.0"; // hashed EIP712 values bytes32 internal constant DOMAIN_SEPARATOR_NAME_HASHED = keccak256(bytes(DOMAIN_SEPARATOR_NAME)); bytes32 internal constant DOMAIN_SEPARATOR_VERSION_HASHED = keccak256(bytes(DOMAIN_SEPARATOR_VERSION)); /// @notice _TYPE_HASH is copied from OpenZeppelin EIP712 but with added salt as last param (we use it for `block.chainid`) bytes32 public constant TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"); /// @notice EIP712 typehash for `cast()` calls, including structs bytes32 public constant CAST_TYPE_HASH = keccak256( "Cast(CastParams params,CastForwardParams forwardParams)Action(address target,bytes data,uint256 value,uint256 operation)CastForwardParams(uint256 gas,uint256 gasPrice,uint256 validAfter,uint256 validUntil,uint256 value)CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)" ); /// @notice EIP712 typehash for Action struct bytes32 public constant ACTION_TYPE_HASH = keccak256("Action(address target,bytes data,uint256 value,uint256 operation)"); /// @notice EIP712 typehash for CastParams struct bytes32 public constant CAST_PARAMS_TYPE_HASH = keccak256( "CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)Action(address target,bytes data,uint256 value,uint256 operation)" ); /// @notice EIP712 typehash for CastForwardParams struct bytes32 public constant CAST_FORWARD_PARAMS_TYPE_HASH = keccak256( "CastForwardParams(uint256 gas,uint256 gasPrice,uint256 validAfter,uint256 validUntil,uint256 value)" ); /// @notice EIP712 typehash for `castAuthorized()` calls, including structs bytes32 public constant CAST_AUTHORIZED_TYPE_HASH = keccak256( "CastAuthorized(CastParams params,CastAuthorizedParams authorizedParams)Action(address target,bytes data,uint256 value,uint256 operation)CastAuthorizedParams(uint256 maxFee,uint256 gasPrice,uint256 validAfter,uint256 validUntil)CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)" ); /// @notice EIP712 typehash for CastAuthorizedParams struct bytes32 public constant CAST_AUTHORIZED_PARAMS_TYPE_HASH = keccak256("CastAuthorizedParams(uint256 maxFee,uint256 gasPrice,uint256 validAfter,uint256 validUntil)"); /// @notice 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. 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 is 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 used 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 { uint256 authorizedMinFee; uint256 authorizedMaxFee; 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_ calculate 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 `addSigners_` 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":"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
4661018090815260206101608190526101a060405290206080523480156200002657600080fd5b5060405162006303380380620063038339810160408190526200004991620002bd565b8383838383838383838383836001600160a01b03841615806200007357506001600160a01b038316155b806200008657506001600160a01b038216155b806200009957506001600160a01b038116155b15620000b85760405163ec92598560e01b815260040160405180910390fd5b6001600160a01b0380851660a05283811660c05282811660e05260408051632271f8db60e01b81529051600092841691632271f8db9160048083019260609291908290030181865afa15801562000113573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000139919062000325565b805190915015806200014d57506020810151155b8062000164575060408101516001600160a01b0316155b8062000174575060208101518151115b15620001935760405163ec92598560e01b815260040160405180910390fd5b805161010052602081015161012052604001516001600160a01b03166101405250620001c7965050620001d5945050505050565b505050505050505062000398565b600054600160f81b900460ff1615620002445760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff600160f01b90910481161015620002a2576000805460ff60f01b191660ff60f01b17905560405160ff81527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6001600160a01b0381168114620002ba57600080fd5b50565b60008060008060808587031215620002d457600080fd5b8451620002e181620002a4565b6020860151909450620002f481620002a4565b60408601519093506200030781620002a4565b60608601519092506200031a81620002a4565b939692955090935050565b6000606082840312156200033857600080fd5b604051606081016001600160401b03811182821017156200036957634e487b7160e01b600052604160045260246000fd5b8060405250825181526020830151602082015260408301516200038c81620002a4565b60408201529392505050565b60805160a05160c05160e051610100516101205161014051615eca62000439600039600081816109490152612cff015260008181610aa601528181612cae0152612cd60152600081816109f20152612d220152600081816103f00152818161168b01528181612283015261325f0152600081816107580152611c5001526000818161078c015281816117f30152612bae015260006130bc0152615eca6000f3fe6080604052600436106103225760003560e01c80638da5cb5b116101a5578063bb6fe03d116100ec578063e7e38cd311610095578063ec80dcc21161006f578063ec80dcc214610a14578063ed969f0a14610a34578063f23a6e6114610a68578063fe6bfed314610a9457600080fd5b8063e7e38cd3146109a0578063eb2856e9146109c0578063ec590483146109e057600080fd5b8063c7fe944f116100c6578063c7fe944f14610937578063e3a889701461096b578063e40956b11461098b57600080fd5b8063bb6fe03d146108c3578063bc197c81146108e3578063bda443311461090f57600080fd5b8063a08519b51161014e578063a80ebaf111610128578063a80ebaf11461084b578063b4907ddc1461087f578063b92e87fa146108b057600080fd5b8063a08519b5146107c3578063a0d06193146107e3578063a755ecfc1461081757600080fd5b806394f0320e1161017f57806394f0320e146107465780639cae7aa81461077a5780639ee3f883146107ae57600080fd5b80638da5cb5b146106fe578063920f5c84146107135780639428ae4e1461073357600080fd5b806346f0975a1161026957806364d4c819116102125780637df73e27116101ec5780637df73e27146106805780638129fc1c146106a057806387265c95146106b557600080fd5b806364d4c819146106175780636fbc15e91461064b57806378e890ba1461066b57600080fd5b80635b502b40116102435780635b502b40146105a35780636043848a146105d75780636261b163146105f757600080fd5b806346f0975a1461054057806349926a66146105625780634de992e81461058257600080fd5b80632986c0e5116102cb5780633a06cc8c116102a55780633a06cc8c146104d65780633edd91871461050a57806343ea19961461052a57600080fd5b80632986c0e5146104515780632a4bf7941461047b5780632a5d1d661461049b57600080fd5b80631626ba7e116102fc5780631626ba7e146103be57806316914ac0146103de5780632772aed91461042a57600080fd5b806301ffc9a71461032e578063044d3cd214610363578063150b7a021461038557600080fd5b3661032957005b600080fd5b34801561033a57600080fd5b5061034e61034936600461466e565b610ac8565b60405190151581526020015b60405180910390f35b34801561036f57600080fd5b5061038361037e3660046146d0565b610aff565b005b34801561039157600080fd5b506103a56103a036600461486b565b610c74565b6040516001600160e01b0319909116815260200161035a565b3480156103ca57600080fd5b506103a56103d9366004614919565b610c85565b3480156103ea57600080fd5b506104127f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161035a565b34801561043657600080fd5b5061043f61100e565b60405160ff909116815260200161035a565b34801561045d57600080fd5b5061046661101d565b60405163ffffffff909116815260200161035a565b34801561048757600080fd5b5061034e610496366004614989565b61108a565b3480156104a757600080fd5b506104c86104b6366004614a01565b60036020526000908152604090205481565b60405190815260200161035a565b3480156104e257600080fd5b506104c87f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e81565b34801561051657600080fd5b506103836105253660046146d0565b611119565b34801561053657600080fd5b506104c861027a81565b34801561054c57600080fd5b506105556111b1565b60405161035a9190614a5e565b34801561056e57600080fd5b5061038361057d366004614a01565b6111bb565b610595610590366004614b81565b6111fe565b60405161035a929190614c46565b3480156105af57600080fd5b506104c87f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c081565b3480156105e357600080fd5b506103836105f2366004614c72565b6113bb565b34801561060357600080fd5b5061034e610612366004614cc6565b611780565b34801561062357600080fd5b506104c87fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647281565b34801561065757600080fd5b50610383610666366004614d25565b6117b8565b34801561067757600080fd5b506104c8611905565b34801561068c57600080fd5b5061034e61069b366004614d61565b61190f565b3480156106ac57600080fd5b5061038361197a565b3480156106c157600080fd5b506106f16040518060400160405280601081526020016f41766f6361646f2d4d756c746973696760801b81525081565b60405161035a9190614d7e565b34801561070a57600080fd5b50610412611ad9565b34801561071f57600080fd5b5061034e61072e366004614d91565b611b3d565b610595610741366004614e6c565b611c43565b34801561075257600080fd5b506104127f000000000000000000000000000000000000000000000000000000000000000081565b34801561078657600080fd5b506104127f000000000000000000000000000000000000000000000000000000000000000081565b3480156107ba57600080fd5b506104c8605a81565b3480156107cf57600080fd5b506103836107de366004614a01565b611e4e565b3480156107ef57600080fd5b506104c87fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b81565b34801561082357600080fd5b506104c87fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4781565b34801561085757600080fd5b506104c87f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6581565b34801561088b57600080fd5b506106f1604051806040016040528060058152602001640312e302e360dc1b81525081565b6103836108be366004614eca565b611e92565b3480156108cf57600080fd5b506104c86108de3660046150da565b611f52565b3480156108ef57600080fd5b506103a56108fe36600461518f565b63bc197c8160e01b95945050505050565b34801561091b57600080fd5b50600054600160a01b900469ffffffffffffffffffff166104c8565b34801561094357600080fd5b506104127f000000000000000000000000000000000000000000000000000000000000000081565b34801561097757600080fd5b506104c86109863660046152ad565b611f5e565b34801561099757600080fd5b5061043f611f6a565b3480156109ac57600080fd5b506103836109bb366004614d25565b611f74565b3480156109cc57600080fd5b506103836109db366004614c72565b611f7c565b3480156109ec57600080fd5b506104c87f000000000000000000000000000000000000000000000000000000000000000081565b348015610a2057600080fd5b50610383610a2f3660046152f3565b612377565b348015610a4057600080fd5b506104c87f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb81565b348015610a7457600080fd5b506103a5610a8336600461530e565b63f23a6e6160e01b95945050505050565b348015610aa057600080fd5b506104c87f000000000000000000000000000000000000000000000000000000000000000081565b60006001600160e01b03198216630271189760e51b1480610af957506301ffc9a760e01b6001600160e01b03198316145b92915050565b610b07612388565b806000819003610b1657505050565b6005811115610b385760405163ec92598560e01b815260040160405180910390fd5b60008054600160a01b900469ffffffffffffffffffff16905b82811015610c2c5781858583818110610b6c57610b6c615377565b9050602002016020810190610b81919061538d565b6affffffffffffffffffffff1603610bd05760405182907f5e8592bbef3f468eddd77c711ee57100f76bbb79500d8871a700fb6108929d9290600090a281610bc8816153d0565b925050610c24565b81858583818110610be357610be3615377565b9050602002016020810190610bf8919061538d565b6affffffffffffffffffffff161115610c245760405163ec92598560e01b815260040160405180910390fd5b600101610b51565b506000805469ffffffffffffffffffff909216600160a01b027fffff00000000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055505050565b630a85bd0160e11b5b949350505050565b6000818103610cc257600084815260026020526040902054600114610cbd5760405163a8398eb960e01b815260040160405180910390fd5b610ffd565b6060826040198101610ddb5760408051600180825281830190925290816020015b604080518082019091526060815260006020820152815260200190600190039081610ce3579050506040805160606020601f89018190040282018101835291810187815292945091829188908890819085018382808284376000920191909152505050908252506040805163b2bdfa7b60e01b81529051602092830192309263b2bdfa7b92600480830193928290030181865afa158015610d88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dac91906153e9565b6001600160a01b031681525082600081518110610dcb57610dcb615377565b6020026020010181905250610fca565b605a811015610dfd5760405163a8398eb960e01b815260040160405180910390fd5b7fdec0de6520000000000000000000000000000000000000000000000000000000610e2c600560008789615406565b610e3591615430565b77ffffffffffffffffffffffffffffffffffffffffffffffff191603610fbb5760556004198201048067ffffffffffffffff811115610e7657610e76614737565b604051908082528060200260200182016040528015610ebc57816020015b604080518082019091526060815260006020820152815260200190600190039081610e945790505b50925060005b81811015610fb457605581026046810190600090610ee590605a01838a8c615406565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050505060208101516040805180820190915291925060601c9080610f408560401981018d8f615406565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506001600160a01b0383166020909101528751889086908110610f9b57610f9b615377565b6020026020010181905250836001019350505050610ec2565b5050610fca565b610fc784860186615487565b91505b6000610fd8878460016123aa565b50905080610ff95760405163a8398eb960e01b815260040160405180910390fd5b5050505b50630b135d3f60e11b5b9392505050565b600061101861272f565b905090565b600060a0306001600160a01b03166368beab3f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561105f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108391906154bc565b901c905090565b60006110b461109986806154d5565b9050866040013586604001358760600135886080013561274c565b60006110ec6110d46110c58861551f565b6109863689900389018961552b565b6110de8587615547565b8860400135600019146123aa565b5090508061110d576040516370a1717960e01b815260040160405180910390fd5b50600195945050505050565b611121612388565b8060005b818110156111ab5760016003600086868581811061114557611145615377565b9050602002013581526020019081526020016000208190555083838281811061117057611170615377565b905060200201357f3749b30151183a20a5e6b93dce343f6010bde0e057441cca9548231f5727b70360405160405180910390a2600101611125565b50505050565b60606110186127fc565b6111c3612388565b6000818152600260205260408082208290555182917fc93ef95774a42ae5f9ca1e82cc762d17a6c2424041b91bf34dcf23aacc28cf1291a250565b6000606060005a905061122c61121487806154d5565b9050876040013587604001358860600135600061274c565b600061124961123a8861551f565b6108de36899003890189615554565b90506060600061126283888b60400135600019146123aa565b9250905080611284576040516370a1717960e01b815260040160405180910390fd5b5080516000906112ac9061129b60a08c018c615570565b905061017290910260089091020190565b61afc80190506112d189828b60400135600019146112cb5760006128eb565b856128eb565b9096509450851561134157336112ed60a08b0160808c01614d61565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b8461132560a08e018e615570565b604051611334939291906155e0565b60405180910390a36113a4565b3361135260a08b0160808c01614d61565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f848861138b60a08f018f615570565b60405161139b9493929190615606565b60405180910390a35b506113b0838835612b9f565b505050935093915050565b6113c3612388565b8160008190036113e65760405163ec92598560e01b815260040160405180910390fd5b60006113f06127fc565b80519091506000611401848361564b565b90506000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611443573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146791906153e9565b905060008267ffffffffffffffff81111561148457611484614737565b6040519080825280602002602001820160405280156114ad578160200160208202803683370190505b50905060008060005b8681101561165c5781810392508882148061152257508b8b838181106114de576114de615377565b90506020020160208101906114f39190614d61565b6001600160a01b031688828151811061150e5761150e615377565b60200260200101516001600160a01b031614155b1561159957858310156115805787818151811061154157611541615377565b602002602001015184848151811061155b5761155b615377565b60200260200101906001600160a01b031690816001600160a01b031681525050611654565b60405163ec92598560e01b815260040160405180910390fd5b846001600160a01b03168c8c848181106115b5576115b5615377565b90506020020160208101906115ca9190614d61565b6001600160a01b0316036115f15760405163ec92598560e01b815260040160405180910390fd5b8b8b8381811061160357611603615377565b90506020020160208101906116189190614d61565b6001600160a01b03167f3525e22824a8a7df2c9a6029941c824cf95b6447f1e13d5128fd3826d35afe8b60405160405180910390a28160010191505b6001016114b6565b5087811461167d5760405163ec92598560e01b815260040160405180910390fd5b611687838a612ea0565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316308d8d6040516024016116c89392919061565e565b60408051601f198184030181529181526020820180516001600160e01b031663a1153e7f60e01b179052516116fd91906156ba565b6000604051808303816000865af19150503d806000811461173a576040519150601f19603f3d011682016040523d82523d6000602084013e61173f565b606091505b5050905080611772576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b505050505050505050505050565b60006117a761178f86806154d5565b9050866040013586604001358760600135600061274c565b60006110ec6110d461123a8861551f565b6117c0612388565b6000546001600160a01b038481169116146119005760405162fe90e360e01b81526001600160a01b0384811660048301527f0000000000000000000000000000000000000000000000000000000000000000169062fe90e39060240160006040518083038186803b15801561183457600080fd5b505afa158015611848573d6000803e3d6000fd5b50506000805473ffffffffffffffffffffffffffffffffffffffff1981166001600160a01b0388811691821784556040519216945092507fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b9190a26118fd84306001600160a01b031663e7e38cd38487876040516024016118cb939291906156d6565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612fd6565b50505b505050565b6000611018612ffb565b60008061191a6127fc565b805190915060005b8181101561196f57846001600160a01b031683828151811061194657611946615377565b60200260200101516001600160a01b03160361196757506001949350505050565b600101611922565b506000949350505050565b600054600160f81b900460ff16158080156119a257506000546001600160f01b90910460ff16105b806119c35750303b1580156119c35750600054600160f01b900460ff166001145b611a3a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160f01b1790558015611a8457600080546001600160f81b0316600160f81b1790555b611a8c6130fe565b8015611ad657600080546001600160f81b03169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b19573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101891906153e9565b604080518082018252601281527f657865637574654f7065617274696f6e282900000000000000000000000000006020918201529051600091611ba8918591859142917f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e91016156f9565b60408051601f19818403018152919052805160209091012060365460081b60ff199081169116148015611be357506001600160a01b03841630145b611c0057604051635e5225b960e01b815260040160405180910390fd5b603680546001600160f81b038116909155600160f81b900460ff16611c32611c2a8585018661572f565b82600161333e565b5060019a9950505050505050505050565b6000606061dead321415337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614611c9657604051635e5225b960e01b815260040160405180910390fd5b84355a6101f4011015611cbc5760405163e681bf7d60e01b815260040160405180910390fd5b8015611cea57611cea611ccf87806154d5565b9050876040013587604001358860600135896080013561274c565b6000611cf86110c58861551f565b905060608215611d3b576000611d1783888b60400135600019146123aa565b9250905080611d39576040516370a1717960e01b815260040160405180910390fd5b505b8051600090611d519061129b60a08c018c615570565b612710019050611d7089828b60400135600019146112cb5760006128eb565b9096509450508415611de15733611d8d60a08a0160808b01614d61565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b83611dc560a08d018d615570565b604051611dd4939291906155e0565b60405180910390a36113b0565b33611df260a08a0160808b01614d61565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f8387611e2b60a08e018e615570565b604051611e3b9493929190615606565b60405180910390a3505050935093915050565b611e56612388565b600081815260026020526040808220600190555182917fbef281d0d1046788f9baa37e73d57e85933f130d8ec1e223366e0b04fbf2540291a250565b604080518082018252600e81526d5f63616c6c54617267657473282960901b6020918201529051611eed9185918591859142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b9101615845565b60408051601f19818403018152919052805160209091012060365460081b60ff1990811691161480611f20575061dead32145b611f3d57604051635e5225b960e01b815260040160405180910390fd5b611900611f4a8385615880565b82600061333e565b60006110078383613734565b600061100783836137dd565b6000611018613876565b611900612388565b611f84612388565b81801580611fbf5750600084848281611f9f57611f9f615377565b9050602002016020810190611fb49190614d61565b6001600160a01b0316145b15611fdd5760405163ec92598560e01b815260040160405180910390fd5b6000611fe76127fc565b80519091506000611ff8848361588d565b9050605a81111561201c5760405163ec92598560e01b815260040160405180910390fd5b60008167ffffffffffffffff81111561203757612037614737565b604051908082528060200260200182016040528015612060578160200160208202803683370190505b50905060008060005b84811015612274578181039250878214806120de575085831080156120de57508a8a8381811061209b5761209b615377565b90506020020160208101906120b09190614d61565b6001600160a01b03168784815181106120cb576120cb615377565b60200260200101516001600160a01b0316105b15612134578683815181106120f5576120f5615377565b602002602001015184828151811061210f5761210f615377565b60200260200101906001600160a01b031690816001600160a01b0316815250506121f0565b8a8a8381811061214657612146615377565b905060200201602081019061215b9190614d61565b84828151811061216d5761216d615377565b60200260200101906001600160a01b031690816001600160a01b0316815250508a8a8381811061219f5761219f615377565b90506020020160208101906121b49190614d61565b6001600160a01b03167f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2460405160405180910390a28160010191505b60008111801561224e57508361220760018361564b565b8151811061221757612217615377565b60200260200101516001600160a01b031684828151811061223a5761223a615377565b60200260200101516001600160a01b031611155b1561226c5760405163ec92598560e01b815260040160405180910390fd5b600101612069565b5061227f8389612ea0565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316308c8c6040516024016122c09392919061565e565b60408051601f198184030181529181526020820180516001600160e01b031663863deb1160e01b179052516122f591906156ba565b6000604051808303816000865af19150503d8060008114612332576040519150601f19603f3d011682016040523d82523d6000602084013e612337565b606091505b505090508061236a576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b5050505050505050505050565b61237f612388565b611ad681613892565b3330146123a857604051635e5225b960e01b815260040160405180910390fd5b565b81516000906060906123ba61272f565b60ff168110806123e057508380156123e057506000868152600360205260409020546001145b156123fe5760405163ec92598560e01b815260040160405180910390fd5b8067ffffffffffffffff81111561241757612417614737565b604051908082528060200260200182016040528015612440578160200160208202803683370190505b509150600061244d6127fc565b805190915060008080805b8681101561271b576124908b828151811061247557612475615377565b6020026020010151602001516001600160a01b03163b151590565b156124ee578a81815181106124a7576124a7615377565b6020026020010151602001518882815181106124c5576124c5615377565b60200260200101906001600160a01b031690816001600160a01b031681525050600192506125af565b6125158c8c838151811061250457612504615377565b602002602001015160000151613947565b88828151811061252757612527615377565b60200260200101906001600160a01b031690816001600160a01b03168152505087818151811061255957612559615377565b60200260200101516001600160a01b03168b828151811061257c5761257c615377565b6020026020010151602001516001600160a01b0316146125af5760405163ec92598560e01b815260040160405180910390fd5b835b8581101561261b578882815181106125cb576125cb615377565b60200260200101516001600160a01b03168782815181106125ee576125ee615377565b60200260200101516001600160a01b031603612613576001925080600101945061261b565b6001016125b1565b5081612631576000985050505050505050612727565b600091508215612713578a51630b135d3f60e11b908c908390811061265857612658615377565b6020026020010151602001516001600160a01b0316631626ba7e8e8e858151811061268557612685615377565b6020026020010151600001516040518363ffffffff1660e01b81526004016126ae9291906158a0565b602060405180830381865afa1580156126cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ef91906158b9565b6001600160e01b0319161461270e576000985050505050505050612727565b600092505b600101612458565b50600197505050505050505b935093915050565b600154600160a01b900460ff166000819003612749575060015b90565b60008511801561277b575083600019148061277b5750600054600160a01b900469ffffffffffffffffffff1684145b6127985760405163ec92598560e01b815260040160405180910390fd5b428311806127b057506000821180156127b057504282105b156127ce57604051633c83d07f60e01b815260040160405180910390fd5b6000811180156127de5750803414155b156118fd5760405163ec92598560e01b815260040160405180910390fd5b6001546060906001600160a01b0316806128c9576040805160018082528183019092529060208083019080368337019050509150306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561286e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061289291906153e9565b826000815181106128a5576128a5615377565b60200260200101906001600160a01b031690816001600160a01b0316815250505090565b6128d28161396b565b8060200190518101906128e5919061593a565b91505090565b600060606128f985806154d5565b604080518082018252600e81526d5f63616c6c54617267657473282960901b6020918201529051612955939291808a01359142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b9101615845565b60408051808303601f190181529190528051602090910120603680547fff000000000000000000000000000000000000000000000000000000000000001660089290921c919091179055826129f75760008054600160a01b900469ffffffffffffffffffff169060146129c78361596f565b91906101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff16021790555050612a0a565b6000838152600360205260409020600190555b6000612a1686806154d5565b8760200135604051602401612a2d93929190615998565b60408051601f198184030181529190526020810180516001600160e01b0316635c9743fd60e11b17905290506060609686015a1015612aa557612a706001603655565b60006040518060400160405280600f81526020016e41564f5f5f4f55545f4f465f47415360881b815250935093505050612727565b6000808351602085016001600160a01b03600054168a5a03f493503d6040519150601f19601f6020830101168201604052808252806000602084013e5083612b8b578051600003612b6e57612afb60968761564b565b5a1015612b31576040518060400160405280600f81526020016e41564f5f5f4f55545f4f465f47415360881b8152509250612b8b565b6040518060400160405280601781526020017f41564f5f5f524541534f4e5f4e4f545f444546494e45440000000000000000008152509250612b8b565b60048101905080806020019051810190612b8891906159ec565b92505b612b956001603655565b5050935093915050565b60008060005a850390506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316613a9884604051602401612bec91815260200190565b60408051601f198184030181529181526020820180516001600160e01b0316631d771f6360e21b17905251612c2191906156ba565b6000604051808303818686fa925050503d8060008114612c5d576040519150601f19603f3d011682016040523d82523d6000602084013e612c62565b606091505b5060408101519193509150828015612c7b5750603f8251115b8015612c8e57506001600160a01b038111155b15612cfd5781806020019051810190612ca79190615a35565b90965094507f0000000000000000000000000000000000000000000000000000000000000000861115612cf8577f000000000000000000000000000000000000000000000000000000000000000095505b612d44565b7f000000000000000000000000000000000000000000000000000000000000000094507f000000000000000000000000000000000000000000000000000000000000000095505b505050506000821115612e6f57600083118015612d6057508282115b15612d885760405163360ceed560e01b81526004810183905260248101849052604401611a31565b81471015612dac5760405163fae5708760e01b815260048101839052602401611a31565b6000816001600160a01b0316836103e890604051600060405180830381858888f193505050503d8060008114612dfe576040519150601f19603f3d011682016040523d82523d6000602084013e612e03565b606091505b505090508015612e3d5760405183907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a2612e69565b60405183907fd1b3877a34a9595e45447dad16b7354ec5a8bece40075fd60d647e76397b476490600090a25b506111ab565b60405182907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a250505050565b8151605a811180612eaf575080155b15612ecd5760405163ec92598560e01b815260040160405180910390fd5b80600103612f44578160ff16600114612ef95760405163ec92598560e01b815260040160405180910390fd5b60018054600160a01b900460ff161115612f3a576040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a25b6000600155505050565b600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16600160a81b60ff841602179055604051612fa090612f8c908590602001614a5e565b60405160208183030381529060405261398c565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039290921691909117905561190082613892565b60606110078383604051806060016040528060278152602001615e6e60279139613a3d565b604080518082018252601081526f41766f6361646f2d4d756c746973696760801b6020918201528151808301835260058152640312e302e360dc1b9082015281517fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472818301527ff5d974efd8e94277ef83fff53580c07a80e6e3ffdb2e3fec63185a4e812655a2818401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c606082015261027a60808201523060a08201527f000000000000000000000000000000000000000000000000000000000000000060c0808301919091528351808303909101815260e0909101909252815191012090565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561313e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061316291906153e9565b90506001600160a01b0381163b15158061318357506001600160a01b038116155b156131a15760405163ec92598560e01b815260040160405180910390fd5b6131ab6001603655565b6040516001600160a01b038216907f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2490600090a26040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a260408051600180825281830190925260009160208083019080368337019050509050818160008151811061324157613241615377565b6001600160a01b0392831660209182029290920101526040516000917f000000000000000000000000000000000000000000000000000000000000000016906132909030908590602401615a65565b60408051601f198184030181529181526020820180516001600160e01b031663863deb1160e01b179052516132c591906156ba565b6000604051808303816000865af19150503d8060008114613302576040519150601f19603f3d011682016040523d82523d6000602084013e613307565b606091505b5050905080611900576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a1505050565b6133486001603655565b6000806000846001148061335c5750846015145b9050801561336f57600054925060015491505b855160005b8181101561372a57600088828151811061339057613390615377565b60200260200101519050600060606000836060015160001480156133c9575060028b10806133be57508a6014145b806133c957508a6015145b156134635760405a816133de576133de615471565b04905083600001516001600160a01b03168460400151856020015160405161340691906156ba565b60006040518083038185875af1925050503d8060008114613443576040519150601f19603f3d011682016040523d82523d6000602084013e613448565b606091505b5090935091508261345e5761345e818684613ab5565b61371b565b836060015160011480156134745750865b1561356a5760405a8161348957613489615471565b04905083600001516001600160a01b031684602001516040516134ac91906156ba565b600060405180830381855af49150503d80600081146134e7576040519150601f19603f3d011682016040523d82523d6000602084013e6134ec565b606091505b5090935091508261350257613502818684613ab5565b61350c6001603655565b6000546001548a8214801561352057508981145b6135635761352d87613b1a565b60405160200161353d9190615a87565b60408051601f198184030181529082905262461bcd60e51b8252611a3191600401614d7e565b505061371b565b8360600151600214801561358857508a6014148061358857508a6015145b156137025789156135ac5761359c85613b1a565b60405160200161353d9190615ac8565b602084015160048101805190916135cb91810160249081019101615b29565b50604080518082018252601281527f657865637574654f7065617274696f6e28290000000000000000000000000000602091820152905191955061363894508593504292507f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e9101615c28565b60408051808303601f19018152918152815160209092019190912060081c600160f81b60ff8f1602176036555a8161367257613672615471565b04915084600001516001600160a01b03168560400151866020015160405161369a91906156ba565b60006040518083038185875af1925050503d80600081146136d7576040519150601f19603f3d011682016040523d82523d6000602084013e6136dc565b606091505b509094509250836136f2576136f2828785613ab5565b6136fc6001603655565b5061371b565b61370b85613b1a565b60405160200161353d9190615c5c565b84600101945050505050613374565b5050505050505050565b6000611007837f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c07f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb85600001518660200151876040015188606001516040516020016137c2959493929190948552602085019390935260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120613bba565b6000611007837fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b7f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e856000015186602001518760400151886060015189608001516040516020016137c296959493929190958652602086019490945260408501929092526060840152608083015260a082015260c00190565b600154600160a81b900460ff1660008190036127495750600190565b60ff811615806138ae57506138a5613876565b60ff168160ff16115b156138cc5760405163ec92598560e01b815260040160405180910390fd5b60015460ff828116600160a01b9092041614611ad657600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16600160a01b60ff8416908102919091179091556040517f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a250565b60008060006139568585613e7b565b9150915061396381613ec0565b509392505050565b6060610af9826001613987816001600160a01b0384163b61564b565b61400a565b600080826040516020016139a09190615c9d565b60405160208183030381529060405290506000816040516020016139c49190615cc3565b60405160208183030381529060405290508051602082016000f092506001600160a01b038316613a365760405162461bcd60e51b815260206004820152601160248201527f4445504c4f594d454e545f4641494c45440000000000000000000000000000006044820152606401611a31565b5050919050565b6060600080856001600160a01b031685604051613a5a91906156ba565b600060405180830381855af49150503d8060008114613a95576040519150601f19603f3d011682016040523d82523d6000602084013e613a9a565b606091505b5091509150613aab8683838761402d565b9695505050505050565b825a1015613af75760405162461bcd60e51b815260206004820152600f60248201526e41564f5f5f4f55545f4f465f47415360881b6044820152606401611a31565b613b0082613b1a565b613b09826140a6565b60405160200161353d929190615d08565b60606000613b2783614441565b600101905060008167ffffffffffffffff811115613b4757613b47614737565b6040519080825280601f01601f191660200182016040528015613b71576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a8504945084613b7b57509392505050565b8251516000906060908067ffffffffffffffff811115613bdc57613bdc614737565b604051908082528060200260200182016040528015613c05578160200160208202803683370190505b50915060005b81811015613d39577f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6587600001518281518110613c4a57613c4a615377565b60200260200101516000015188600001518381518110613c6c57613c6c615377565b6020026020010151602001518051906020012089600001518481518110613c9557613c95615377565b6020026020010151604001518a600001518581518110613cb757613cb7615377565b602002602001015160600151604051602001613cfe9594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120838281518110613d2657613d26615377565b6020908102919091010152600101613c0b565b5050613e72613d46612ffb565b857fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4784604051602001613d799190615d37565b6040516020818303038152906040528051906020012089602001518a604001518b606001518c608001518d60a0015180519060200120604051602001613df9979695949392919096875260208701959095526040860193909352606085019190915260808401526001600160a01b031660a083015260c082015260e00190565b60408051808303601f190181528282528051602091820120818401949094528282019390935260608083018990528151808403909101815260808301825280519084012061190160f01b60a084015260a283019490945260c2808301949094528051808303909401845260e29091019052815191012090565b95945050505050565b6000808251604103613eb15760208301516040840151606085015160001a613ea587828585614523565b94509450505050613eb9565b506000905060025b9250929050565b6000816004811115613ed457613ed4615d6d565b03613edc5750565b6001816004811115613ef057613ef0615d6d565b03613f3d5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611a31565b6002816004811115613f5157613f51615d6d565b03613f9e5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611a31565b6003816004811115613fb257613fb2615d6d565b03611ad65760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401611a31565b60408051603f8301601f19168101909152818152818360208301863c9392505050565b6060831561409c578251600003614095576001600160a01b0385163b6140955760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611a31565b5081610c7d565b610c7d83836145e7565b60606004825110156140eb57505060408051808201909152601381527f5f524541534f4e5f4e4f545f444546494e454400000000000000000000000000602082015290565b602082015163b1b7848f60e01b6001600160e01b0319821601614216576040805160028082528183019092526000916020820181803683370190505090506141646010856001875161413d919061564b565b8151811061414d5761414d615377565b016020015161415f919060f81c615d83565b614611565b8160008151811061417757614177615377565b60200101906001600160f81b031916908160001a9053506141c4601085600187516141a2919061564b565b815181106141b2576141b2615377565b016020015161415f919060f81c615da5565b816001815181106141d7576141d7615377565b60200101906001600160f81b031916908160001a905350806040516020016141ff9190615dc7565b604051602081830303815290604052925050614379565b6307b9e43360e51b6001600160e01b031982160161426e576004830192508280602001905181019061424891906159ec565b6040516020016142589190615e0c565b6040516020818303038152906040529150614379565b60408051600880825281830190925260009160208201818036833701905050905060005b6004811015614354576142c660108483600481106142b2576142b2615377565b1a816142c0576142c0615471565b04614611565b8282600202815181106142db576142db615377565b60200101906001600160f81b031916908160001a90535061431d601084836004811061430957614309615377565b1a8161431757614317615471565b06614611565b82826002026001018151811061433557614335615377565b60200101906001600160f81b031916908160001a905350600101614292565b50806040516020016143669190615e28565b6040516020818303038152906040529250505b60fa8251111561443b5781600061439260fa600161588d565b905060008167ffffffffffffffff8111156143af576143af614737565b6040519080825280601f01601f1916602001820160405280156143d9576020820181803683370190505b50905060005b82811015614435578381815181106143f9576143f9615377565b602001015160f81c60f81b82828151811061441657614416615377565b60200101906001600160f81b031916908160001a9053506001016143df565b50935050505b50919050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061448a577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef810000000083106144b6576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106144d457662386f26fc10000830492506010015b6305f5e10083106144ec576305f5e100830492506008015b612710831061450057612710830492506004015b60648310614512576064830492506002015b600a8310610af95760010192915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561455a57506000905060036145de565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156145ae573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166145d7576000600192509250506145de565b9150600090505b94509492505050565b8151156145f75781518083602001fd5b8060405162461bcd60e51b8152600401611a319190614d7e565b6000600a8260ff161015614629575060300160f81b90565b60108260ff16101561463f575060570160f81b90565b604051639778fcab60e01b815260040160405180910390fd5b6001600160e01b031981168114611ad657600080fd5b60006020828403121561468057600080fd5b813561100781614658565b60008083601f84011261469d57600080fd5b50813567ffffffffffffffff8111156146b557600080fd5b6020830191508360208260051b8501011115613eb957600080fd5b600080602083850312156146e357600080fd5b823567ffffffffffffffff8111156146fa57600080fd5b6147068582860161468b565b90969095509350505050565b6001600160a01b0381168114611ad657600080fd5b803561473281614712565b919050565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561477057614770614737565b60405290565b6040516080810167ffffffffffffffff8111828210171561477057614770614737565b60405160c0810167ffffffffffffffff8111828210171561477057614770614737565b604051601f8201601f1916810167ffffffffffffffff811182821017156147e5576147e5614737565b604052919050565b600067ffffffffffffffff82111561480757614807614737565b50601f01601f191660200190565b600082601f83011261482657600080fd5b8135614839614834826147ed565b6147bc565b81815284602083860101111561484e57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561488157600080fd5b843561488c81614712565b9350602085013561489c81614712565b925060408501359150606085013567ffffffffffffffff8111156148bf57600080fd5b6148cb87828801614815565b91505092959194509250565b60008083601f8401126148e957600080fd5b50813567ffffffffffffffff81111561490157600080fd5b602083019150836020828501011115613eb957600080fd5b60008060006040848603121561492e57600080fd5b83359250602084013567ffffffffffffffff81111561494c57600080fd5b614958868287016148d7565b9497909650939450505050565b600060c0828403121561443b57600080fd5b600060a0828403121561443b57600080fd5b60008060008060e0858703121561499f57600080fd5b843567ffffffffffffffff808211156149b757600080fd5b6149c388838901614965565b95506149d28860208901614977565b945060c08701359150808211156149e857600080fd5b506149f58782880161468b565b95989497509550505050565b600060208284031215614a1357600080fd5b5035919050565b600081518084526020808501945080840160005b83811015614a535781516001600160a01b031687529582019590820190600101614a2e565b509495945050505050565b6020815260006110076020830184614a1a565b60006080828403121561443b57600080fd5b600067ffffffffffffffff821115614a9d57614a9d614737565b5060051b60200190565b6000614ab561483484614a83565b8381529050602080820190600585901b840186811115614ad457600080fd5b845b81811015614b5657803567ffffffffffffffff80821115614af75760008081fd5b908701906040828b031215614b0c5760008081fd5b614b1461474d565b823582811115614b245760008081fd5b614b308c828601614815565b8252509185013591614b4183614712565b80860192909252508452928201928201614ad6565b505050509392505050565b600082601f830112614b7257600080fd5b61100783833560208501614aa7565b600080600060c08486031215614b9657600080fd5b833567ffffffffffffffff80821115614bae57600080fd5b614bba87838801614965565b9450614bc98760208801614a71565b935060a0860135915080821115614bdf57600080fd5b50614bec86828701614b61565b9150509250925092565b60005b83811015614c11578181015183820152602001614bf9565b50506000910152565b60008151808452614c32816020860160208601614bf6565b601f01601f19169290920160200192915050565b8215158152604060208201526000610c7d6040830184614c1a565b803560ff8116811461473257600080fd5b600080600060408486031215614c8757600080fd5b833567ffffffffffffffff811115614c9e57600080fd5b614caa8682870161468b565b9094509250614cbd905060208501614c61565b90509250925092565b60008060008060c08587031215614cdc57600080fd5b843567ffffffffffffffff80821115614cf457600080fd5b614d0088838901614965565b9550614d0f8860208901614a71565b945060a08701359150808211156149e857600080fd5b600080600060408486031215614d3a57600080fd5b8335614d4581614712565b9250602084013567ffffffffffffffff81111561494c57600080fd5b600060208284031215614d7357600080fd5b813561100781614712565b6020815260006110076020830184614c1a565b600080600080600080600080600060a08a8c031215614daf57600080fd5b893567ffffffffffffffff80821115614dc757600080fd5b614dd38d838e0161468b565b909b50995060208c0135915080821115614dec57600080fd5b614df88d838e0161468b565b909950975060408c0135915080821115614e1157600080fd5b614e1d8d838e0161468b565b909750955060608c01359150614e3282614712565b90935060808b01359080821115614e4857600080fd5b50614e558c828d016148d7565b915080935050809150509295985092959850929598565b600080600060e08486031215614e8157600080fd5b833567ffffffffffffffff80821115614e9957600080fd5b614ea587838801614965565b9450614eb48760208801614977565b935060c0860135915080821115614bdf57600080fd5b600080600060408486031215614edf57600080fd5b833567ffffffffffffffff811115614ef657600080fd5b614f028682870161468b565b909790965060209590950135949350505050565b6000614f2461483484614a83565b8381529050602080820190600585901b840186811115614f4357600080fd5b845b81811015614b5657803567ffffffffffffffff80821115614f665760008081fd5b908701906080828b031215614f7b5760008081fd5b614f83614776565b8235614f8e81614712565b81528286013582811115614fa25760008081fd5b614fae8c828601614815565b82880152506040838101359082015260609283013592810192909252508452928201928201614f45565b600082601f830112614fe957600080fd5b61100783833560208501614f16565b600060c0828403121561500a57600080fd5b615012614799565b9050813567ffffffffffffffff8082111561502c57600080fd5b61503885838601614fd8565b835260208401356020840152604084013560408401526060840135606084015261506460808501614727565b608084015260a084013591508082111561507d57600080fd5b5061508a84828501614815565b60a08301525092915050565b6000608082840312156150a857600080fd5b6150b0614776565b90508135815260208201356020820152604082013560408201526060820135606082015292915050565b60008060a083850312156150ed57600080fd5b823567ffffffffffffffff81111561510457600080fd5b61511085828601614ff8565b9250506151208460208501615096565b90509250929050565b600082601f83011261513a57600080fd5b8135602061514a61483483614a83565b82815260059290921b8401810191818101908684111561516957600080fd5b8286015b84811015615184578035835291830191830161516d565b509695505050505050565b600080600080600060a086880312156151a757600080fd5b85356151b281614712565b945060208601356151c281614712565b9350604086013567ffffffffffffffff808211156151df57600080fd5b6151eb89838a01615129565b9450606088013591508082111561520157600080fd5b61520d89838a01615129565b9350608088013591508082111561522357600080fd5b5061523088828901614815565b9150509295509295909350565b600060a0828403121561524f57600080fd5b60405160a0810181811067ffffffffffffffff8211171561527257615272614737565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b60008060c083850312156152c057600080fd5b823567ffffffffffffffff8111156152d757600080fd5b6152e385828601614ff8565b925050615120846020850161523d565b60006020828403121561530557600080fd5b61100782614c61565b600080600080600060a0868803121561532657600080fd5b853561533181614712565b9450602086013561534181614712565b93506040860135925060608601359150608086013567ffffffffffffffff81111561536b57600080fd5b61523088828901614815565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561539f57600080fd5b81356affffffffffffffffffffff8116811461100757600080fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016153e2576153e26153ba565b5060010190565b6000602082840312156153fb57600080fd5b815161100781614712565b6000808585111561541657600080fd5b8386111561542357600080fd5b5050820193919092039150565b77ffffffffffffffffffffffffffffffffffffffffffffffff1981358181169160088510156154695780818660080360031b1b83161692505b505092915050565b634e487b7160e01b600052601260045260246000fd5b60006020828403121561549957600080fd5b813567ffffffffffffffff8111156154b057600080fd5b610c7d84828501614b61565b6000602082840312156154ce57600080fd5b5051919050565b6000808335601e198436030181126154ec57600080fd5b83018035915067ffffffffffffffff82111561550757600080fd5b6020019150600581901b3603821315613eb957600080fd5b6000610af93683614ff8565b600060a0828403121561553d57600080fd5b611007838361523d565b6000611007368484614aa7565b60006080828403121561556657600080fd5b6110078383615096565b6000808335601e1984360301811261558757600080fd5b83018035915067ffffffffffffffff8211156155a257600080fd5b602001915036819003821315613eb957600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6040815260006155f36040830186614a1a565b8281036020840152613aab8185876155b7565b6060815260006156196060830187614a1a565b828103602084015261562b8187614c1a565b905082810360408401526156408185876155b7565b979650505050505050565b81810381811115610af957610af96153ba565b6001600160a01b03848116825260406020808401829052908301849052600091859160608501845b878110156156ad57843561569981614712565b841682529382019390820190600101615686565b5098975050505050505050565b600082516156cc818460208701614bf6565b9190910192915050565b6001600160a01b0384168152604060208201526000613e726040830184866155b7565b60608152600061570d6060830186886155b7565b6020830194909452506001600160e01b03199190911660409091015292915050565b60006020828403121561574157600080fd5b813567ffffffffffffffff81111561575857600080fd5b610c7d84828501614fd8565b81835260006020808501808196508560051b810191508460005b878110156158385782840389528135607e1988360301811261579f57600080fd5b8701608081356157ae81614712565b6001600160a01b031686528187013536839003601e190181126157d057600080fd5b8201878101903567ffffffffffffffff8111156157ec57600080fd5b8036038213156157fb57600080fd5b828989015261580d83890182846155b7565b604085810135908a01526060948501359490980193909352505050978401979084019060010161577e565b5091979650505050505050565b608081526000615859608083018789615764565b60208301959095525060408101929092526001600160e01b03191660609091015292915050565b6000611007368484614f16565b80820180821115610af957610af96153ba565b828152604060208201526000610c7d6040830184614c1a565b6000602082840312156158cb57600080fd5b815161100781614658565b600082601f8301126158e757600080fd5b815160206158f761483483614a83565b82815260059290921b8401810191818101908684111561591657600080fd5b8286015b8481101561518457805161592d81614712565b835291830191830161591a565b60006020828403121561594c57600080fd5b815167ffffffffffffffff81111561596357600080fd5b610c7d848285016158d6565b600069ffffffffffffffffffff80831681810361598e5761598e6153ba565b6001019392505050565b6040815260006159ac604083018587615764565b9050826020830152949350505050565b60006159ca614834846147ed565b90508281528383830111156159de57600080fd5b611007836020830184614bf6565b6000602082840312156159fe57600080fd5b815167ffffffffffffffff811115615a1557600080fd5b8201601f81018413615a2657600080fd5b610c7d848251602084016159bc565b60008060408385031215615a4857600080fd5b825191506020830151615a5a81614712565b809150509250929050565b6001600160a01b0383168152604060208201526000610c7d6040830184614a1a565b60008251615a99818460208701614bf6565b7f5f41564f5f5f4d4f4449464945445f53544f5241474500000000000000000000920191825250601601919050565b60008251615ada818460208701614bf6565b7f5f41564f5f5f4e4f5f464c4153484c4f414e5f494e5f464c4153484c4f414e00920191825250601f01919050565b600082601f830112615b1a57600080fd5b611007838351602085016159bc565b600080600080600060a08688031215615b4157600080fd5b855167ffffffffffffffff80821115615b5957600080fd5b615b6589838a016158d6565b9650602091508188015181811115615b7c57600080fd5b8801601f81018a13615b8d57600080fd5b8051615b9b61483482614a83565b81815260059190911b8201840190848101908c831115615bba57600080fd5b928501925b82841015615bd857835182529285019290850190615bbf565b60408c015160608d0151919a509850945050505080821115615bf957600080fd5b615c0589838a01615b09565b93506080880151915080821115615c1b57600080fd5b5061523088828901615b09565b606081526000615c3b6060830186614c1a565b6020830194909452506001600160e01b031991909116604090910152919050565b60008251615c6e818460208701614bf6565b7f5f41564f5f5f494e56414c49445f49445f4f525f4f5045524154494f4e000000920191825250601d01919050565b6000815260008251615cb6816001850160208701614bf6565b9190910160010192915050565b7f600b5981380380925939f3000000000000000000000000000000000000000000815260008251615cfb81600b850160208701614bf6565b91909101600b0192915050565b60008351615d1a818460208801614bf6565b835190830190615d2e818360208801614bf6565b01949350505050565b815160009082906020808601845b83811015615d6157815185529382019390820190600101615d45565b50929695505050505050565b634e487b7160e01b600052602160045260246000fd5b600060ff831680615d9657615d96615471565b8060ff84160491505092915050565b600060ff831680615db857615db8615471565b8060ff84160691505092915050565b7f5f5441524745545f50414e49434b45443a203078000000000000000000000000815260008251615dff816014850160208701614bf6565b9190910160140192915050565b605f60f81b815260008251615cb6816001850160208701614bf6565b7f5f435553544f4d5f4552524f523a203078000000000000000000000000000000815260008251615e60816011850160208701614bf6565b919091016011019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212206683cae62a86307e412c4a275e0e595b294d053575fae2765aa2c5dfeae8ca2d64736f6c63430008120033000000000000000000000000779385ec7a04242259add4990e3130846f80ea6900000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068480000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d
Deployed Bytecode
0x6080604052600436106103225760003560e01c80638da5cb5b116101a5578063bb6fe03d116100ec578063e7e38cd311610095578063ec80dcc21161006f578063ec80dcc214610a14578063ed969f0a14610a34578063f23a6e6114610a68578063fe6bfed314610a9457600080fd5b8063e7e38cd3146109a0578063eb2856e9146109c0578063ec590483146109e057600080fd5b8063c7fe944f116100c6578063c7fe944f14610937578063e3a889701461096b578063e40956b11461098b57600080fd5b8063bb6fe03d146108c3578063bc197c81146108e3578063bda443311461090f57600080fd5b8063a08519b51161014e578063a80ebaf111610128578063a80ebaf11461084b578063b4907ddc1461087f578063b92e87fa146108b057600080fd5b8063a08519b5146107c3578063a0d06193146107e3578063a755ecfc1461081757600080fd5b806394f0320e1161017f57806394f0320e146107465780639cae7aa81461077a5780639ee3f883146107ae57600080fd5b80638da5cb5b146106fe578063920f5c84146107135780639428ae4e1461073357600080fd5b806346f0975a1161026957806364d4c819116102125780637df73e27116101ec5780637df73e27146106805780638129fc1c146106a057806387265c95146106b557600080fd5b806364d4c819146106175780636fbc15e91461064b57806378e890ba1461066b57600080fd5b80635b502b40116102435780635b502b40146105a35780636043848a146105d75780636261b163146105f757600080fd5b806346f0975a1461054057806349926a66146105625780634de992e81461058257600080fd5b80632986c0e5116102cb5780633a06cc8c116102a55780633a06cc8c146104d65780633edd91871461050a57806343ea19961461052a57600080fd5b80632986c0e5146104515780632a4bf7941461047b5780632a5d1d661461049b57600080fd5b80631626ba7e116102fc5780631626ba7e146103be57806316914ac0146103de5780632772aed91461042a57600080fd5b806301ffc9a71461032e578063044d3cd214610363578063150b7a021461038557600080fd5b3661032957005b600080fd5b34801561033a57600080fd5b5061034e61034936600461466e565b610ac8565b60405190151581526020015b60405180910390f35b34801561036f57600080fd5b5061038361037e3660046146d0565b610aff565b005b34801561039157600080fd5b506103a56103a036600461486b565b610c74565b6040516001600160e01b0319909116815260200161035a565b3480156103ca57600080fd5b506103a56103d9366004614919565b610c85565b3480156103ea57600080fd5b506104127f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc00684881565b6040516001600160a01b03909116815260200161035a565b34801561043657600080fd5b5061043f61100e565b60405160ff909116815260200161035a565b34801561045d57600080fd5b5061046661101d565b60405163ffffffff909116815260200161035a565b34801561048757600080fd5b5061034e610496366004614989565b61108a565b3480156104a757600080fd5b506104c86104b6366004614a01565b60036020526000908152604090205481565b60405190815260200161035a565b3480156104e257600080fd5b506104c87f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e81565b34801561051657600080fd5b506103836105253660046146d0565b611119565b34801561053657600080fd5b506104c861027a81565b34801561054c57600080fd5b506105556111b1565b60405161035a9190614a5e565b34801561056e57600080fd5b5061038361057d366004614a01565b6111bb565b610595610590366004614b81565b6111fe565b60405161035a929190614c46565b3480156105af57600080fd5b506104c87f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c081565b3480156105e357600080fd5b506103836105f2366004614c72565b6113bb565b34801561060357600080fd5b5061034e610612366004614cc6565b611780565b34801561062357600080fd5b506104c87fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647281565b34801561065757600080fd5b50610383610666366004614d25565b6117b8565b34801561067757600080fd5b506104c8611905565b34801561068c57600080fd5b5061034e61069b366004614d61565b61190f565b3480156106ac57600080fd5b5061038361197a565b3480156106c157600080fd5b506106f16040518060400160405280601081526020016f41766f6361646f2d4d756c746973696760801b81525081565b60405161035a9190614d7e565b34801561070a57600080fd5b50610412611ad9565b34801561071f57600080fd5b5061034e61072e366004614d91565b611b3d565b610595610741366004614e6c565b611c43565b34801561075257600080fd5b506104127f00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a5481565b34801561078657600080fd5b506104127f000000000000000000000000779385ec7a04242259add4990e3130846f80ea6981565b3480156107ba57600080fd5b506104c8605a81565b3480156107cf57600080fd5b506103836107de366004614a01565b611e4e565b3480156107ef57600080fd5b506104c87fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b81565b34801561082357600080fd5b506104c87fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4781565b34801561085757600080fd5b506104c87f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6581565b34801561088b57600080fd5b506106f1604051806040016040528060058152602001640312e302e360dc1b81525081565b6103836108be366004614eca565b611e92565b3480156108cf57600080fd5b506104c86108de3660046150da565b611f52565b3480156108ef57600080fd5b506103a56108fe36600461518f565b63bc197c8160e01b95945050505050565b34801561091b57600080fd5b50600054600160a01b900469ffffffffffffffffffff166104c8565b34801561094357600080fd5b506104127f000000000000000000000000e8385fb3a5f15ded06eb5e20e5a81bf43115eb8e81565b34801561097757600080fd5b506104c86109863660046152ad565b611f5e565b34801561099757600080fd5b5061043f611f6a565b3480156109ac57600080fd5b506103836109bb366004614d25565b611f74565b3480156109cc57600080fd5b506103836109db366004614c72565b611f7c565b3480156109ec57600080fd5b506104c87f0000000000000000000000000000000000000000000000000001c6bf5263400081565b348015610a2057600080fd5b50610383610a2f3660046152f3565b612377565b348015610a4057600080fd5b506104c87f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb81565b348015610a7457600080fd5b506103a5610a8336600461530e565b63f23a6e6160e01b95945050505050565b348015610aa057600080fd5b506104c87f00000000000000000000000000000000000000000000000000b1a2bc2ec5000081565b60006001600160e01b03198216630271189760e51b1480610af957506301ffc9a760e01b6001600160e01b03198316145b92915050565b610b07612388565b806000819003610b1657505050565b6005811115610b385760405163ec92598560e01b815260040160405180910390fd5b60008054600160a01b900469ffffffffffffffffffff16905b82811015610c2c5781858583818110610b6c57610b6c615377565b9050602002016020810190610b81919061538d565b6affffffffffffffffffffff1603610bd05760405182907f5e8592bbef3f468eddd77c711ee57100f76bbb79500d8871a700fb6108929d9290600090a281610bc8816153d0565b925050610c24565b81858583818110610be357610be3615377565b9050602002016020810190610bf8919061538d565b6affffffffffffffffffffff161115610c245760405163ec92598560e01b815260040160405180910390fd5b600101610b51565b506000805469ffffffffffffffffffff909216600160a01b027fffff00000000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055505050565b630a85bd0160e11b5b949350505050565b6000818103610cc257600084815260026020526040902054600114610cbd5760405163a8398eb960e01b815260040160405180910390fd5b610ffd565b6060826040198101610ddb5760408051600180825281830190925290816020015b604080518082019091526060815260006020820152815260200190600190039081610ce3579050506040805160606020601f89018190040282018101835291810187815292945091829188908890819085018382808284376000920191909152505050908252506040805163b2bdfa7b60e01b81529051602092830192309263b2bdfa7b92600480830193928290030181865afa158015610d88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dac91906153e9565b6001600160a01b031681525082600081518110610dcb57610dcb615377565b6020026020010181905250610fca565b605a811015610dfd5760405163a8398eb960e01b815260040160405180910390fd5b7fdec0de6520000000000000000000000000000000000000000000000000000000610e2c600560008789615406565b610e3591615430565b77ffffffffffffffffffffffffffffffffffffffffffffffff191603610fbb5760556004198201048067ffffffffffffffff811115610e7657610e76614737565b604051908082528060200260200182016040528015610ebc57816020015b604080518082019091526060815260006020820152815260200190600190039081610e945790505b50925060005b81811015610fb457605581026046810190600090610ee590605a01838a8c615406565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050505060208101516040805180820190915291925060601c9080610f408560401981018d8f615406565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506001600160a01b0383166020909101528751889086908110610f9b57610f9b615377565b6020026020010181905250836001019350505050610ec2565b5050610fca565b610fc784860186615487565b91505b6000610fd8878460016123aa565b50905080610ff95760405163a8398eb960e01b815260040160405180910390fd5b5050505b50630b135d3f60e11b5b9392505050565b600061101861272f565b905090565b600060a0306001600160a01b03166368beab3f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561105f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108391906154bc565b901c905090565b60006110b461109986806154d5565b9050866040013586604001358760600135886080013561274c565b60006110ec6110d46110c58861551f565b6109863689900389018961552b565b6110de8587615547565b8860400135600019146123aa565b5090508061110d576040516370a1717960e01b815260040160405180910390fd5b50600195945050505050565b611121612388565b8060005b818110156111ab5760016003600086868581811061114557611145615377565b9050602002013581526020019081526020016000208190555083838281811061117057611170615377565b905060200201357f3749b30151183a20a5e6b93dce343f6010bde0e057441cca9548231f5727b70360405160405180910390a2600101611125565b50505050565b60606110186127fc565b6111c3612388565b6000818152600260205260408082208290555182917fc93ef95774a42ae5f9ca1e82cc762d17a6c2424041b91bf34dcf23aacc28cf1291a250565b6000606060005a905061122c61121487806154d5565b9050876040013587604001358860600135600061274c565b600061124961123a8861551f565b6108de36899003890189615554565b90506060600061126283888b60400135600019146123aa565b9250905080611284576040516370a1717960e01b815260040160405180910390fd5b5080516000906112ac9061129b60a08c018c615570565b905061017290910260089091020190565b61afc80190506112d189828b60400135600019146112cb5760006128eb565b856128eb565b9096509450851561134157336112ed60a08b0160808c01614d61565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b8461132560a08e018e615570565b604051611334939291906155e0565b60405180910390a36113a4565b3361135260a08b0160808c01614d61565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f848861138b60a08f018f615570565b60405161139b9493929190615606565b60405180910390a35b506113b0838835612b9f565b505050935093915050565b6113c3612388565b8160008190036113e65760405163ec92598560e01b815260040160405180910390fd5b60006113f06127fc565b80519091506000611401848361564b565b90506000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611443573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146791906153e9565b905060008267ffffffffffffffff81111561148457611484614737565b6040519080825280602002602001820160405280156114ad578160200160208202803683370190505b50905060008060005b8681101561165c5781810392508882148061152257508b8b838181106114de576114de615377565b90506020020160208101906114f39190614d61565b6001600160a01b031688828151811061150e5761150e615377565b60200260200101516001600160a01b031614155b1561159957858310156115805787818151811061154157611541615377565b602002602001015184848151811061155b5761155b615377565b60200260200101906001600160a01b031690816001600160a01b031681525050611654565b60405163ec92598560e01b815260040160405180910390fd5b846001600160a01b03168c8c848181106115b5576115b5615377565b90506020020160208101906115ca9190614d61565b6001600160a01b0316036115f15760405163ec92598560e01b815260040160405180910390fd5b8b8b8381811061160357611603615377565b90506020020160208101906116189190614d61565b6001600160a01b03167f3525e22824a8a7df2c9a6029941c824cf95b6447f1e13d5128fd3826d35afe8b60405160405180910390a28160010191505b6001016114b6565b5087811461167d5760405163ec92598560e01b815260040160405180910390fd5b611687838a612ea0565b60007f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068486001600160a01b0316308d8d6040516024016116c89392919061565e565b60408051601f198184030181529181526020820180516001600160e01b031663a1153e7f60e01b179052516116fd91906156ba565b6000604051808303816000865af19150503d806000811461173a576040519150601f19603f3d011682016040523d82523d6000602084013e61173f565b606091505b5050905080611772576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b505050505050505050505050565b60006117a761178f86806154d5565b9050866040013586604001358760600135600061274c565b60006110ec6110d461123a8861551f565b6117c0612388565b6000546001600160a01b038481169116146119005760405162fe90e360e01b81526001600160a01b0384811660048301527f000000000000000000000000779385ec7a04242259add4990e3130846f80ea69169062fe90e39060240160006040518083038186803b15801561183457600080fd5b505afa158015611848573d6000803e3d6000fd5b50506000805473ffffffffffffffffffffffffffffffffffffffff1981166001600160a01b0388811691821784556040519216945092507fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b9190a26118fd84306001600160a01b031663e7e38cd38487876040516024016118cb939291906156d6565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612fd6565b50505b505050565b6000611018612ffb565b60008061191a6127fc565b805190915060005b8181101561196f57846001600160a01b031683828151811061194657611946615377565b60200260200101516001600160a01b03160361196757506001949350505050565b600101611922565b506000949350505050565b600054600160f81b900460ff16158080156119a257506000546001600160f01b90910460ff16105b806119c35750303b1580156119c35750600054600160f01b900460ff166001145b611a3a5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160f01b1790558015611a8457600080546001600160f81b0316600160f81b1790555b611a8c6130fe565b8015611ad657600080546001600160f81b03169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b19573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101891906153e9565b604080518082018252601281527f657865637574654f7065617274696f6e282900000000000000000000000000006020918201529051600091611ba8918591859142917f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e91016156f9565b60408051601f19818403018152919052805160209091012060365460081b60ff199081169116148015611be357506001600160a01b03841630145b611c0057604051635e5225b960e01b815260040160405180910390fd5b603680546001600160f81b038116909155600160f81b900460ff16611c32611c2a8585018661572f565b82600161333e565b5060019a9950505050505050505050565b6000606061dead321415337f00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a546001600160a01b031614611c9657604051635e5225b960e01b815260040160405180910390fd5b84355a6101f4011015611cbc5760405163e681bf7d60e01b815260040160405180910390fd5b8015611cea57611cea611ccf87806154d5565b9050876040013587604001358860600135896080013561274c565b6000611cf86110c58861551f565b905060608215611d3b576000611d1783888b60400135600019146123aa565b9250905080611d39576040516370a1717960e01b815260040160405180910390fd5b505b8051600090611d519061129b60a08c018c615570565b612710019050611d7089828b60400135600019146112cb5760006128eb565b9096509450508415611de15733611d8d60a08a0160808b01614d61565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b83611dc560a08d018d615570565b604051611dd4939291906155e0565b60405180910390a36113b0565b33611df260a08a0160808b01614d61565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f8387611e2b60a08e018e615570565b604051611e3b9493929190615606565b60405180910390a3505050935093915050565b611e56612388565b600081815260026020526040808220600190555182917fbef281d0d1046788f9baa37e73d57e85933f130d8ec1e223366e0b04fbf2540291a250565b604080518082018252600e81526d5f63616c6c54617267657473282960901b6020918201529051611eed9185918591859142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b9101615845565b60408051601f19818403018152919052805160209091012060365460081b60ff1990811691161480611f20575061dead32145b611f3d57604051635e5225b960e01b815260040160405180910390fd5b611900611f4a8385615880565b82600061333e565b60006110078383613734565b600061100783836137dd565b6000611018613876565b611900612388565b611f84612388565b81801580611fbf5750600084848281611f9f57611f9f615377565b9050602002016020810190611fb49190614d61565b6001600160a01b0316145b15611fdd5760405163ec92598560e01b815260040160405180910390fd5b6000611fe76127fc565b80519091506000611ff8848361588d565b9050605a81111561201c5760405163ec92598560e01b815260040160405180910390fd5b60008167ffffffffffffffff81111561203757612037614737565b604051908082528060200260200182016040528015612060578160200160208202803683370190505b50905060008060005b84811015612274578181039250878214806120de575085831080156120de57508a8a8381811061209b5761209b615377565b90506020020160208101906120b09190614d61565b6001600160a01b03168784815181106120cb576120cb615377565b60200260200101516001600160a01b0316105b15612134578683815181106120f5576120f5615377565b602002602001015184828151811061210f5761210f615377565b60200260200101906001600160a01b031690816001600160a01b0316815250506121f0565b8a8a8381811061214657612146615377565b905060200201602081019061215b9190614d61565b84828151811061216d5761216d615377565b60200260200101906001600160a01b031690816001600160a01b0316815250508a8a8381811061219f5761219f615377565b90506020020160208101906121b49190614d61565b6001600160a01b03167f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2460405160405180910390a28160010191505b60008111801561224e57508361220760018361564b565b8151811061221757612217615377565b60200260200101516001600160a01b031684828151811061223a5761223a615377565b60200260200101516001600160a01b031611155b1561226c5760405163ec92598560e01b815260040160405180910390fd5b600101612069565b5061227f8389612ea0565b60007f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068486001600160a01b0316308c8c6040516024016122c09392919061565e565b60408051601f198184030181529181526020820180516001600160e01b031663863deb1160e01b179052516122f591906156ba565b6000604051808303816000865af19150503d8060008114612332576040519150601f19603f3d011682016040523d82523d6000602084013e612337565b606091505b505090508061236a576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b5050505050505050505050565b61237f612388565b611ad681613892565b3330146123a857604051635e5225b960e01b815260040160405180910390fd5b565b81516000906060906123ba61272f565b60ff168110806123e057508380156123e057506000868152600360205260409020546001145b156123fe5760405163ec92598560e01b815260040160405180910390fd5b8067ffffffffffffffff81111561241757612417614737565b604051908082528060200260200182016040528015612440578160200160208202803683370190505b509150600061244d6127fc565b805190915060008080805b8681101561271b576124908b828151811061247557612475615377565b6020026020010151602001516001600160a01b03163b151590565b156124ee578a81815181106124a7576124a7615377565b6020026020010151602001518882815181106124c5576124c5615377565b60200260200101906001600160a01b031690816001600160a01b031681525050600192506125af565b6125158c8c838151811061250457612504615377565b602002602001015160000151613947565b88828151811061252757612527615377565b60200260200101906001600160a01b031690816001600160a01b03168152505087818151811061255957612559615377565b60200260200101516001600160a01b03168b828151811061257c5761257c615377565b6020026020010151602001516001600160a01b0316146125af5760405163ec92598560e01b815260040160405180910390fd5b835b8581101561261b578882815181106125cb576125cb615377565b60200260200101516001600160a01b03168782815181106125ee576125ee615377565b60200260200101516001600160a01b031603612613576001925080600101945061261b565b6001016125b1565b5081612631576000985050505050505050612727565b600091508215612713578a51630b135d3f60e11b908c908390811061265857612658615377565b6020026020010151602001516001600160a01b0316631626ba7e8e8e858151811061268557612685615377565b6020026020010151600001516040518363ffffffff1660e01b81526004016126ae9291906158a0565b602060405180830381865afa1580156126cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ef91906158b9565b6001600160e01b0319161461270e576000985050505050505050612727565b600092505b600101612458565b50600197505050505050505b935093915050565b600154600160a01b900460ff166000819003612749575060015b90565b60008511801561277b575083600019148061277b5750600054600160a01b900469ffffffffffffffffffff1684145b6127985760405163ec92598560e01b815260040160405180910390fd5b428311806127b057506000821180156127b057504282105b156127ce57604051633c83d07f60e01b815260040160405180910390fd5b6000811180156127de5750803414155b156118fd5760405163ec92598560e01b815260040160405180910390fd5b6001546060906001600160a01b0316806128c9576040805160018082528183019092529060208083019080368337019050509150306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561286e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061289291906153e9565b826000815181106128a5576128a5615377565b60200260200101906001600160a01b031690816001600160a01b0316815250505090565b6128d28161396b565b8060200190518101906128e5919061593a565b91505090565b600060606128f985806154d5565b604080518082018252600e81526d5f63616c6c54617267657473282960901b6020918201529051612955939291808a01359142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b9101615845565b60408051808303601f190181529190528051602090910120603680547fff000000000000000000000000000000000000000000000000000000000000001660089290921c919091179055826129f75760008054600160a01b900469ffffffffffffffffffff169060146129c78361596f565b91906101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff16021790555050612a0a565b6000838152600360205260409020600190555b6000612a1686806154d5565b8760200135604051602401612a2d93929190615998565b60408051601f198184030181529190526020810180516001600160e01b0316635c9743fd60e11b17905290506060609686015a1015612aa557612a706001603655565b60006040518060400160405280600f81526020016e41564f5f5f4f55545f4f465f47415360881b815250935093505050612727565b6000808351602085016001600160a01b03600054168a5a03f493503d6040519150601f19601f6020830101168201604052808252806000602084013e5083612b8b578051600003612b6e57612afb60968761564b565b5a1015612b31576040518060400160405280600f81526020016e41564f5f5f4f55545f4f465f47415360881b8152509250612b8b565b6040518060400160405280601781526020017f41564f5f5f524541534f4e5f4e4f545f444546494e45440000000000000000008152509250612b8b565b60048101905080806020019051810190612b8891906159ec565b92505b612b956001603655565b5050935093915050565b60008060005a850390506000807f000000000000000000000000779385ec7a04242259add4990e3130846f80ea696001600160a01b0316613a9884604051602401612bec91815260200190565b60408051601f198184030181529181526020820180516001600160e01b0316631d771f6360e21b17905251612c2191906156ba565b6000604051808303818686fa925050503d8060008114612c5d576040519150601f19603f3d011682016040523d82523d6000602084013e612c62565b606091505b5060408101519193509150828015612c7b5750603f8251115b8015612c8e57506001600160a01b038111155b15612cfd5781806020019051810190612ca79190615a35565b90965094507f00000000000000000000000000000000000000000000000000b1a2bc2ec50000861115612cf8577f00000000000000000000000000000000000000000000000000b1a2bc2ec5000095505b612d44565b7f000000000000000000000000e8385fb3a5f15ded06eb5e20e5a81bf43115eb8e94507f0000000000000000000000000000000000000000000000000001c6bf5263400095505b505050506000821115612e6f57600083118015612d6057508282115b15612d885760405163360ceed560e01b81526004810183905260248101849052604401611a31565b81471015612dac5760405163fae5708760e01b815260048101839052602401611a31565b6000816001600160a01b0316836103e890604051600060405180830381858888f193505050503d8060008114612dfe576040519150601f19603f3d011682016040523d82523d6000602084013e612e03565b606091505b505090508015612e3d5760405183907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a2612e69565b60405183907fd1b3877a34a9595e45447dad16b7354ec5a8bece40075fd60d647e76397b476490600090a25b506111ab565b60405182907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a250505050565b8151605a811180612eaf575080155b15612ecd5760405163ec92598560e01b815260040160405180910390fd5b80600103612f44578160ff16600114612ef95760405163ec92598560e01b815260040160405180910390fd5b60018054600160a01b900460ff161115612f3a576040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a25b6000600155505050565b600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16600160a81b60ff841602179055604051612fa090612f8c908590602001614a5e565b60405160208183030381529060405261398c565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039290921691909117905561190082613892565b60606110078383604051806060016040528060278152602001615e6e60279139613a3d565b604080518082018252601081526f41766f6361646f2d4d756c746973696760801b6020918201528151808301835260058152640312e302e360dc1b9082015281517fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472818301527ff5d974efd8e94277ef83fff53580c07a80e6e3ffdb2e3fec63185a4e812655a2818401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c606082015261027a60808201523060a08201527f6e452848784197f00927d379e3db9e69a5131d2269f862bfcd05a0b38f6abf7f60c0808301919091528351808303909101815260e0909101909252815191012090565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561313e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061316291906153e9565b90506001600160a01b0381163b15158061318357506001600160a01b038116155b156131a15760405163ec92598560e01b815260040160405180910390fd5b6131ab6001603655565b6040516001600160a01b038216907f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2490600090a26040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a260408051600180825281830190925260009160208083019080368337019050509050818160008151811061324157613241615377565b6001600160a01b0392831660209182029290920101526040516000917f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc00684816906132909030908590602401615a65565b60408051601f198184030181529181526020820180516001600160e01b031663863deb1160e01b179052516132c591906156ba565b6000604051808303816000865af19150503d8060008114613302576040519150601f19603f3d011682016040523d82523d6000602084013e613307565b606091505b5050905080611900576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a1505050565b6133486001603655565b6000806000846001148061335c5750846015145b9050801561336f57600054925060015491505b855160005b8181101561372a57600088828151811061339057613390615377565b60200260200101519050600060606000836060015160001480156133c9575060028b10806133be57508a6014145b806133c957508a6015145b156134635760405a816133de576133de615471565b04905083600001516001600160a01b03168460400151856020015160405161340691906156ba565b60006040518083038185875af1925050503d8060008114613443576040519150601f19603f3d011682016040523d82523d6000602084013e613448565b606091505b5090935091508261345e5761345e818684613ab5565b61371b565b836060015160011480156134745750865b1561356a5760405a8161348957613489615471565b04905083600001516001600160a01b031684602001516040516134ac91906156ba565b600060405180830381855af49150503d80600081146134e7576040519150601f19603f3d011682016040523d82523d6000602084013e6134ec565b606091505b5090935091508261350257613502818684613ab5565b61350c6001603655565b6000546001548a8214801561352057508981145b6135635761352d87613b1a565b60405160200161353d9190615a87565b60408051601f198184030181529082905262461bcd60e51b8252611a3191600401614d7e565b505061371b565b8360600151600214801561358857508a6014148061358857508a6015145b156137025789156135ac5761359c85613b1a565b60405160200161353d9190615ac8565b602084015160048101805190916135cb91810160249081019101615b29565b50604080518082018252601281527f657865637574654f7065617274696f6e28290000000000000000000000000000602091820152905191955061363894508593504292507f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e9101615c28565b60408051808303601f19018152918152815160209092019190912060081c600160f81b60ff8f1602176036555a8161367257613672615471565b04915084600001516001600160a01b03168560400151866020015160405161369a91906156ba565b60006040518083038185875af1925050503d80600081146136d7576040519150601f19603f3d011682016040523d82523d6000602084013e6136dc565b606091505b509094509250836136f2576136f2828785613ab5565b6136fc6001603655565b5061371b565b61370b85613b1a565b60405160200161353d9190615c5c565b84600101945050505050613374565b5050505050505050565b6000611007837f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c07f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb85600001518660200151876040015188606001516040516020016137c2959493929190948552602085019390935260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120613bba565b6000611007837fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b7f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e856000015186602001518760400151886060015189608001516040516020016137c296959493929190958652602086019490945260408501929092526060840152608083015260a082015260c00190565b600154600160a81b900460ff1660008190036127495750600190565b60ff811615806138ae57506138a5613876565b60ff168160ff16115b156138cc5760405163ec92598560e01b815260040160405180910390fd5b60015460ff828116600160a01b9092041614611ad657600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16600160a01b60ff8416908102919091179091556040517f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a250565b60008060006139568585613e7b565b9150915061396381613ec0565b509392505050565b6060610af9826001613987816001600160a01b0384163b61564b565b61400a565b600080826040516020016139a09190615c9d565b60405160208183030381529060405290506000816040516020016139c49190615cc3565b60405160208183030381529060405290508051602082016000f092506001600160a01b038316613a365760405162461bcd60e51b815260206004820152601160248201527f4445504c4f594d454e545f4641494c45440000000000000000000000000000006044820152606401611a31565b5050919050565b6060600080856001600160a01b031685604051613a5a91906156ba565b600060405180830381855af49150503d8060008114613a95576040519150601f19603f3d011682016040523d82523d6000602084013e613a9a565b606091505b5091509150613aab8683838761402d565b9695505050505050565b825a1015613af75760405162461bcd60e51b815260206004820152600f60248201526e41564f5f5f4f55545f4f465f47415360881b6044820152606401611a31565b613b0082613b1a565b613b09826140a6565b60405160200161353d929190615d08565b60606000613b2783614441565b600101905060008167ffffffffffffffff811115613b4757613b47614737565b6040519080825280601f01601f191660200182016040528015613b71576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a8504945084613b7b57509392505050565b8251516000906060908067ffffffffffffffff811115613bdc57613bdc614737565b604051908082528060200260200182016040528015613c05578160200160208202803683370190505b50915060005b81811015613d39577f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6587600001518281518110613c4a57613c4a615377565b60200260200101516000015188600001518381518110613c6c57613c6c615377565b6020026020010151602001518051906020012089600001518481518110613c9557613c95615377565b6020026020010151604001518a600001518581518110613cb757613cb7615377565b602002602001015160600151604051602001613cfe9594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120838281518110613d2657613d26615377565b6020908102919091010152600101613c0b565b5050613e72613d46612ffb565b857fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4784604051602001613d799190615d37565b6040516020818303038152906040528051906020012089602001518a604001518b606001518c608001518d60a0015180519060200120604051602001613df9979695949392919096875260208701959095526040860193909352606085019190915260808401526001600160a01b031660a083015260c082015260e00190565b60408051808303601f190181528282528051602091820120818401949094528282019390935260608083018990528151808403909101815260808301825280519084012061190160f01b60a084015260a283019490945260c2808301949094528051808303909401845260e29091019052815191012090565b95945050505050565b6000808251604103613eb15760208301516040840151606085015160001a613ea587828585614523565b94509450505050613eb9565b506000905060025b9250929050565b6000816004811115613ed457613ed4615d6d565b03613edc5750565b6001816004811115613ef057613ef0615d6d565b03613f3d5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611a31565b6002816004811115613f5157613f51615d6d565b03613f9e5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611a31565b6003816004811115613fb257613fb2615d6d565b03611ad65760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401611a31565b60408051603f8301601f19168101909152818152818360208301863c9392505050565b6060831561409c578251600003614095576001600160a01b0385163b6140955760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611a31565b5081610c7d565b610c7d83836145e7565b60606004825110156140eb57505060408051808201909152601381527f5f524541534f4e5f4e4f545f444546494e454400000000000000000000000000602082015290565b602082015163b1b7848f60e01b6001600160e01b0319821601614216576040805160028082528183019092526000916020820181803683370190505090506141646010856001875161413d919061564b565b8151811061414d5761414d615377565b016020015161415f919060f81c615d83565b614611565b8160008151811061417757614177615377565b60200101906001600160f81b031916908160001a9053506141c4601085600187516141a2919061564b565b815181106141b2576141b2615377565b016020015161415f919060f81c615da5565b816001815181106141d7576141d7615377565b60200101906001600160f81b031916908160001a905350806040516020016141ff9190615dc7565b604051602081830303815290604052925050614379565b6307b9e43360e51b6001600160e01b031982160161426e576004830192508280602001905181019061424891906159ec565b6040516020016142589190615e0c565b6040516020818303038152906040529150614379565b60408051600880825281830190925260009160208201818036833701905050905060005b6004811015614354576142c660108483600481106142b2576142b2615377565b1a816142c0576142c0615471565b04614611565b8282600202815181106142db576142db615377565b60200101906001600160f81b031916908160001a90535061431d601084836004811061430957614309615377565b1a8161431757614317615471565b06614611565b82826002026001018151811061433557614335615377565b60200101906001600160f81b031916908160001a905350600101614292565b50806040516020016143669190615e28565b6040516020818303038152906040529250505b60fa8251111561443b5781600061439260fa600161588d565b905060008167ffffffffffffffff8111156143af576143af614737565b6040519080825280601f01601f1916602001820160405280156143d9576020820181803683370190505b50905060005b82811015614435578381815181106143f9576143f9615377565b602001015160f81c60f81b82828151811061441657614416615377565b60200101906001600160f81b031916908160001a9053506001016143df565b50935050505b50919050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061448a577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef810000000083106144b6576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106144d457662386f26fc10000830492506010015b6305f5e10083106144ec576305f5e100830492506008015b612710831061450057612710830492506004015b60648310614512576064830492506002015b600a8310610af95760010192915050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561455a57506000905060036145de565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156145ae573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166145d7576000600192509250506145de565b9150600090505b94509492505050565b8151156145f75781518083602001fd5b8060405162461bcd60e51b8152600401611a319190614d7e565b6000600a8260ff161015614629575060300160f81b90565b60108260ff16101561463f575060570160f81b90565b604051639778fcab60e01b815260040160405180910390fd5b6001600160e01b031981168114611ad657600080fd5b60006020828403121561468057600080fd5b813561100781614658565b60008083601f84011261469d57600080fd5b50813567ffffffffffffffff8111156146b557600080fd5b6020830191508360208260051b8501011115613eb957600080fd5b600080602083850312156146e357600080fd5b823567ffffffffffffffff8111156146fa57600080fd5b6147068582860161468b565b90969095509350505050565b6001600160a01b0381168114611ad657600080fd5b803561473281614712565b919050565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561477057614770614737565b60405290565b6040516080810167ffffffffffffffff8111828210171561477057614770614737565b60405160c0810167ffffffffffffffff8111828210171561477057614770614737565b604051601f8201601f1916810167ffffffffffffffff811182821017156147e5576147e5614737565b604052919050565b600067ffffffffffffffff82111561480757614807614737565b50601f01601f191660200190565b600082601f83011261482657600080fd5b8135614839614834826147ed565b6147bc565b81815284602083860101111561484e57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561488157600080fd5b843561488c81614712565b9350602085013561489c81614712565b925060408501359150606085013567ffffffffffffffff8111156148bf57600080fd5b6148cb87828801614815565b91505092959194509250565b60008083601f8401126148e957600080fd5b50813567ffffffffffffffff81111561490157600080fd5b602083019150836020828501011115613eb957600080fd5b60008060006040848603121561492e57600080fd5b83359250602084013567ffffffffffffffff81111561494c57600080fd5b614958868287016148d7565b9497909650939450505050565b600060c0828403121561443b57600080fd5b600060a0828403121561443b57600080fd5b60008060008060e0858703121561499f57600080fd5b843567ffffffffffffffff808211156149b757600080fd5b6149c388838901614965565b95506149d28860208901614977565b945060c08701359150808211156149e857600080fd5b506149f58782880161468b565b95989497509550505050565b600060208284031215614a1357600080fd5b5035919050565b600081518084526020808501945080840160005b83811015614a535781516001600160a01b031687529582019590820190600101614a2e565b509495945050505050565b6020815260006110076020830184614a1a565b60006080828403121561443b57600080fd5b600067ffffffffffffffff821115614a9d57614a9d614737565b5060051b60200190565b6000614ab561483484614a83565b8381529050602080820190600585901b840186811115614ad457600080fd5b845b81811015614b5657803567ffffffffffffffff80821115614af75760008081fd5b908701906040828b031215614b0c5760008081fd5b614b1461474d565b823582811115614b245760008081fd5b614b308c828601614815565b8252509185013591614b4183614712565b80860192909252508452928201928201614ad6565b505050509392505050565b600082601f830112614b7257600080fd5b61100783833560208501614aa7565b600080600060c08486031215614b9657600080fd5b833567ffffffffffffffff80821115614bae57600080fd5b614bba87838801614965565b9450614bc98760208801614a71565b935060a0860135915080821115614bdf57600080fd5b50614bec86828701614b61565b9150509250925092565b60005b83811015614c11578181015183820152602001614bf9565b50506000910152565b60008151808452614c32816020860160208601614bf6565b601f01601f19169290920160200192915050565b8215158152604060208201526000610c7d6040830184614c1a565b803560ff8116811461473257600080fd5b600080600060408486031215614c8757600080fd5b833567ffffffffffffffff811115614c9e57600080fd5b614caa8682870161468b565b9094509250614cbd905060208501614c61565b90509250925092565b60008060008060c08587031215614cdc57600080fd5b843567ffffffffffffffff80821115614cf457600080fd5b614d0088838901614965565b9550614d0f8860208901614a71565b945060a08701359150808211156149e857600080fd5b600080600060408486031215614d3a57600080fd5b8335614d4581614712565b9250602084013567ffffffffffffffff81111561494c57600080fd5b600060208284031215614d7357600080fd5b813561100781614712565b6020815260006110076020830184614c1a565b600080600080600080600080600060a08a8c031215614daf57600080fd5b893567ffffffffffffffff80821115614dc757600080fd5b614dd38d838e0161468b565b909b50995060208c0135915080821115614dec57600080fd5b614df88d838e0161468b565b909950975060408c0135915080821115614e1157600080fd5b614e1d8d838e0161468b565b909750955060608c01359150614e3282614712565b90935060808b01359080821115614e4857600080fd5b50614e558c828d016148d7565b915080935050809150509295985092959850929598565b600080600060e08486031215614e8157600080fd5b833567ffffffffffffffff80821115614e9957600080fd5b614ea587838801614965565b9450614eb48760208801614977565b935060c0860135915080821115614bdf57600080fd5b600080600060408486031215614edf57600080fd5b833567ffffffffffffffff811115614ef657600080fd5b614f028682870161468b565b909790965060209590950135949350505050565b6000614f2461483484614a83565b8381529050602080820190600585901b840186811115614f4357600080fd5b845b81811015614b5657803567ffffffffffffffff80821115614f665760008081fd5b908701906080828b031215614f7b5760008081fd5b614f83614776565b8235614f8e81614712565b81528286013582811115614fa25760008081fd5b614fae8c828601614815565b82880152506040838101359082015260609283013592810192909252508452928201928201614f45565b600082601f830112614fe957600080fd5b61100783833560208501614f16565b600060c0828403121561500a57600080fd5b615012614799565b9050813567ffffffffffffffff8082111561502c57600080fd5b61503885838601614fd8565b835260208401356020840152604084013560408401526060840135606084015261506460808501614727565b608084015260a084013591508082111561507d57600080fd5b5061508a84828501614815565b60a08301525092915050565b6000608082840312156150a857600080fd5b6150b0614776565b90508135815260208201356020820152604082013560408201526060820135606082015292915050565b60008060a083850312156150ed57600080fd5b823567ffffffffffffffff81111561510457600080fd5b61511085828601614ff8565b9250506151208460208501615096565b90509250929050565b600082601f83011261513a57600080fd5b8135602061514a61483483614a83565b82815260059290921b8401810191818101908684111561516957600080fd5b8286015b84811015615184578035835291830191830161516d565b509695505050505050565b600080600080600060a086880312156151a757600080fd5b85356151b281614712565b945060208601356151c281614712565b9350604086013567ffffffffffffffff808211156151df57600080fd5b6151eb89838a01615129565b9450606088013591508082111561520157600080fd5b61520d89838a01615129565b9350608088013591508082111561522357600080fd5b5061523088828901614815565b9150509295509295909350565b600060a0828403121561524f57600080fd5b60405160a0810181811067ffffffffffffffff8211171561527257615272614737565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b60008060c083850312156152c057600080fd5b823567ffffffffffffffff8111156152d757600080fd5b6152e385828601614ff8565b925050615120846020850161523d565b60006020828403121561530557600080fd5b61100782614c61565b600080600080600060a0868803121561532657600080fd5b853561533181614712565b9450602086013561534181614712565b93506040860135925060608601359150608086013567ffffffffffffffff81111561536b57600080fd5b61523088828901614815565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561539f57600080fd5b81356affffffffffffffffffffff8116811461100757600080fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016153e2576153e26153ba565b5060010190565b6000602082840312156153fb57600080fd5b815161100781614712565b6000808585111561541657600080fd5b8386111561542357600080fd5b5050820193919092039150565b77ffffffffffffffffffffffffffffffffffffffffffffffff1981358181169160088510156154695780818660080360031b1b83161692505b505092915050565b634e487b7160e01b600052601260045260246000fd5b60006020828403121561549957600080fd5b813567ffffffffffffffff8111156154b057600080fd5b610c7d84828501614b61565b6000602082840312156154ce57600080fd5b5051919050565b6000808335601e198436030181126154ec57600080fd5b83018035915067ffffffffffffffff82111561550757600080fd5b6020019150600581901b3603821315613eb957600080fd5b6000610af93683614ff8565b600060a0828403121561553d57600080fd5b611007838361523d565b6000611007368484614aa7565b60006080828403121561556657600080fd5b6110078383615096565b6000808335601e1984360301811261558757600080fd5b83018035915067ffffffffffffffff8211156155a257600080fd5b602001915036819003821315613eb957600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6040815260006155f36040830186614a1a565b8281036020840152613aab8185876155b7565b6060815260006156196060830187614a1a565b828103602084015261562b8187614c1a565b905082810360408401526156408185876155b7565b979650505050505050565b81810381811115610af957610af96153ba565b6001600160a01b03848116825260406020808401829052908301849052600091859160608501845b878110156156ad57843561569981614712565b841682529382019390820190600101615686565b5098975050505050505050565b600082516156cc818460208701614bf6565b9190910192915050565b6001600160a01b0384168152604060208201526000613e726040830184866155b7565b60608152600061570d6060830186886155b7565b6020830194909452506001600160e01b03199190911660409091015292915050565b60006020828403121561574157600080fd5b813567ffffffffffffffff81111561575857600080fd5b610c7d84828501614fd8565b81835260006020808501808196508560051b810191508460005b878110156158385782840389528135607e1988360301811261579f57600080fd5b8701608081356157ae81614712565b6001600160a01b031686528187013536839003601e190181126157d057600080fd5b8201878101903567ffffffffffffffff8111156157ec57600080fd5b8036038213156157fb57600080fd5b828989015261580d83890182846155b7565b604085810135908a01526060948501359490980193909352505050978401979084019060010161577e565b5091979650505050505050565b608081526000615859608083018789615764565b60208301959095525060408101929092526001600160e01b03191660609091015292915050565b6000611007368484614f16565b80820180821115610af957610af96153ba565b828152604060208201526000610c7d6040830184614c1a565b6000602082840312156158cb57600080fd5b815161100781614658565b600082601f8301126158e757600080fd5b815160206158f761483483614a83565b82815260059290921b8401810191818101908684111561591657600080fd5b8286015b8481101561518457805161592d81614712565b835291830191830161591a565b60006020828403121561594c57600080fd5b815167ffffffffffffffff81111561596357600080fd5b610c7d848285016158d6565b600069ffffffffffffffffffff80831681810361598e5761598e6153ba565b6001019392505050565b6040815260006159ac604083018587615764565b9050826020830152949350505050565b60006159ca614834846147ed565b90508281528383830111156159de57600080fd5b611007836020830184614bf6565b6000602082840312156159fe57600080fd5b815167ffffffffffffffff811115615a1557600080fd5b8201601f81018413615a2657600080fd5b610c7d848251602084016159bc565b60008060408385031215615a4857600080fd5b825191506020830151615a5a81614712565b809150509250929050565b6001600160a01b0383168152604060208201526000610c7d6040830184614a1a565b60008251615a99818460208701614bf6565b7f5f41564f5f5f4d4f4449464945445f53544f5241474500000000000000000000920191825250601601919050565b60008251615ada818460208701614bf6565b7f5f41564f5f5f4e4f5f464c4153484c4f414e5f494e5f464c4153484c4f414e00920191825250601f01919050565b600082601f830112615b1a57600080fd5b611007838351602085016159bc565b600080600080600060a08688031215615b4157600080fd5b855167ffffffffffffffff80821115615b5957600080fd5b615b6589838a016158d6565b9650602091508188015181811115615b7c57600080fd5b8801601f81018a13615b8d57600080fd5b8051615b9b61483482614a83565b81815260059190911b8201840190848101908c831115615bba57600080fd5b928501925b82841015615bd857835182529285019290850190615bbf565b60408c015160608d0151919a509850945050505080821115615bf957600080fd5b615c0589838a01615b09565b93506080880151915080821115615c1b57600080fd5b5061523088828901615b09565b606081526000615c3b6060830186614c1a565b6020830194909452506001600160e01b031991909116604090910152919050565b60008251615c6e818460208701614bf6565b7f5f41564f5f5f494e56414c49445f49445f4f525f4f5045524154494f4e000000920191825250601d01919050565b6000815260008251615cb6816001850160208701614bf6565b9190910160010192915050565b7f600b5981380380925939f3000000000000000000000000000000000000000000815260008251615cfb81600b850160208701614bf6565b91909101600b0192915050565b60008351615d1a818460208801614bf6565b835190830190615d2e818360208801614bf6565b01949350505050565b815160009082906020808601845b83811015615d6157815185529382019390820190600101615d45565b50929695505050505050565b634e487b7160e01b600052602160045260246000fd5b600060ff831680615d9657615d96615471565b8060ff84160491505092915050565b600060ff831680615db857615db8615471565b8060ff84160691505092915050565b7f5f5441524745545f50414e49434b45443a203078000000000000000000000000815260008251615dff816014850160208701614bf6565b9190910160140192915050565b605f60f81b815260008251615cb6816001850160208701614bf6565b7f5f435553544f4d5f4552524f523a203078000000000000000000000000000000815260008251615e60816011850160208701614bf6565b919091016011019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212206683cae62a86307e412c4a275e0e595b294d053575fae2765aa2c5dfeae8ca2d64736f6c63430008120033
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.