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 | |||
---|---|---|---|---|---|---|
411044 | 15 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:
AvocadoMultisigSecondary
Compiler Version
v0.8.18+commit.87f61d96
Optimization Enabled:
Yes with 10000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { SSTORE2 } from "solmate/src/utils/SSTORE2.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IAvoRegistry } from "../interfaces/IAvoRegistry.sol"; import { IAvoSignersList } from "../interfaces/IAvoSignersList.sol"; import { IAvocadoMultisigV1Secondary } from "../interfaces/IAvocadoMultisigV1Secondary.sol"; import { IAvocado } from "../Avocado.sol"; import { IAvoConfigV1 } from "../interfaces/IAvoConfigV1.sol"; import { AvocadoMultisigBase } from "./AvocadoMultisigCore.sol"; import { AvocadoMultisigProtected, AvocadoMultisigSelfUpgradeable } from "./AvocadoMultisig.sol"; // --------------------------- DEVELOPER NOTES ----------------------------------------- // @dev IMPORTANT: all storage variables go into AvocadoMultisigVariables.sol // ------------------------------------------------------------------------------------- // empty interface used for Natspec docs for nice layout in automatically generated docs: // /// @title AvocadoMultisigSecondary v1.1.0 /// @notice Extended core logic for `AvocadoMultisig.sol`, which calls this contract via delegateCall (or directly /// for view/pure methods). /// This contract is set as immutable on `AvocadoMultisig.sol` and is thus only upgradeable when that contract /// itself is newly deployed (and consecutively upgraded to by the Avocado owner). /// @dev All methods are restricted to be only callable via delegateCall. interface AvocadoMultisigSecondary_V1 {} abstract contract AvocadoMultisigSecondaryCore is AvocadoMultisigBase, IAvocadoMultisigV1Secondary { address private immutable _ADDRESS_THIS; /// @dev ensures the method can only be called by a delegate call. modifier onlyDelegateCall() { _requireDelegateCalled(); _; } /// @dev internal method for modifier logic to reduce bytecode size of contract. function _requireDelegateCalled() internal view { if (_ADDRESS_THIS == address(this)) { revert AvocadoMultisig__Unauthorized(); } } constructor() { _ADDRESS_THIS = address(this); } } abstract contract AvocadoMultisigUpgradeTo is AvocadoMultisigSecondaryCore { /// @notice upgrade the contract to a new implementation address. /// - Must be a valid version at the AvoRegistry. /// - Can only be self-called (authorization same as for `cast` methods). /// @param avoImplementation_ New contract address /// @param afterUpgradeHookData_ flexible bytes for custom usage in after upgrade hook logic // // Implementation must call `_afterUpgradeHook()` function upgradeTo(address avoImplementation_, bytes calldata afterUpgradeHookData_) public onlyDelegateCall { if (avoImplementation_ == _avoImpl) { return; } // checks that `avoImplementation_` is a valid version at registry. reverts if not. avoRegistry.requireValidAvoVersion(avoImplementation_); // store previous implementation address to pass to after upgrade hook, for version x > version y specific logic address fromImplementation_ = _avoImpl; _avoImpl = avoImplementation_; emit Upgraded(avoImplementation_); // Address.functionDelegateCall will revert if success = false Address.functionDelegateCall( avoImplementation_, abi.encodeCall( AvocadoMultisigSelfUpgradeable._afterUpgradeHook, (fromImplementation_, afterUpgradeHookData_) ) ); } } abstract contract AvocadoMultisigOccupyNonces is AvocadoMultisigSecondaryCore { /// @notice occupies the sequential `avoNonces_` in storage. This can be used to cancel / invalidate /// a previously signed request(s) because the nonce will be "used" up. /// - Can only be self-called (authorization same as for `cast` methods). /// @param avoNonces_ sequential ascending ordered nonces to be occupied in storage. /// E.g. if current AvoNonce is 77 and txs are queued with avoNonces 77, 78 and 79, /// then you would submit [78, 79] here because 77 will be occupied by the tx executing /// `occupyAvoNonces()` as an action itself. If executing via non-sequential nonces, you would /// submit [77, 78, 79]. /// - Maximum array length is 5. /// - gap from the current avoNonce will revert (e.g. [79, 80] if current one is 77) function occupyAvoNonces(uint88[] calldata avoNonces_) external onlyDelegateCall { uint256 avoNoncesLength_ = avoNonces_.length; if (avoNoncesLength_ == 0) { // in case to cancel just one nonce via normal sequential nonce execution itself return; } if (avoNoncesLength_ > 5) { revert AvocadoMultisig__InvalidParams(); } uint256 nextAvoNonce_ = _avoNonce; for (uint256 i; i < avoNoncesLength_; ) { if (avoNonces_[i] == nextAvoNonce_) { // nonce to occupy is valid -> must match the current avoNonce emit AvoNonceOccupied(nextAvoNonce_); nextAvoNonce_++; } else if (avoNonces_[i] > nextAvoNonce_) { // input nonce is not smaller or equal current nonce -> invalid sorted ascending input params revert AvocadoMultisig__InvalidParams(); } // else while nonce to occupy is < current nonce, skip ahead unchecked { ++i; } } _avoNonce = uint80(nextAvoNonce_); } /// @notice occupies the `nonSequentialNonces_` in storage. This can be used to cancel / invalidate /// previously signed request(s) because the nonce will be "used" up. /// - Can only be self-called (authorization same as for `cast` methods). /// @param nonSequentialNonces_ the non-sequential nonces to occupy function occupyNonSequentialNonces(bytes32[] calldata nonSequentialNonces_) external onlyDelegateCall { uint256 nonSequentialNoncesLength_ = nonSequentialNonces_.length; for (uint256 i; i < nonSequentialNoncesLength_; ) { nonSequentialNonces[nonSequentialNonces_[i]] = 1; emit NonSequentialNonceOccupied(nonSequentialNonces_[i]); unchecked { ++i; } } } } abstract contract AvocadoMultisigEIP1271 is AvocadoMultisigSecondaryCore { /// @dev length of a normal expected ECDSA signature uint256 private constant _SIGNATURE_LENGTH = 65; /// @dev signature must be 65 bytes or otherwise at least 90 bytes to be either a multiple /// of 85 bytes + prefix or a decodable `SignatureParams` struct array. uint256 private constant _MIN_SIGNATURE_LENGTH = 90; /// @dev prefix to signal decoding with multiple of 85 bytes is "0xDEC0DE6520" (appending 000000 to get to bytes8) bytes8 private constant _PREFIX_SIGNAL = bytes8(uint64(0xdec0de6520000000)); /// @dev prefix length to cut of is 5 bytes (DE_C0_DE_65_20) uint256 private constant _PREFIX_SIGNAL_LENGTH = 5; /// @notice decodes `signature` for EIP1271 into `signaturesParams_` function decodeEIP1271Signature( bytes calldata signature, address owner_ ) external pure returns (SignatureParams[] memory signaturesParams_) { // decode signaturesParams_ from bytes signature uint256 signatureLength_ = signature.length; if (signatureLength_ == _SIGNATURE_LENGTH) { // signature must be from owner for a Multisig with requiredSigners = 1, signers=[owner] signaturesParams_ = new SignatureParams[](1); signaturesParams_[0] = SignatureParams({ signature: signature, signer: owner_ }); } else if (signatureLength_ < _MIN_SIGNATURE_LENGTH) { revert AvocadoMultisig__InvalidEIP1271Signature(); } else if (bytes8(signature[0:_PREFIX_SIGNAL_LENGTH]) == _PREFIX_SIGNAL) { // if signature is prefixed with _PREFIX_SIGNAL ("0xDEC0DE6520") -> // signature after the prefix should be divisible by 85 // (65 bytes signature and 20 bytes signer address) each uint256 signaturesCount_; unchecked { // -_PREFIX_SIGNAL_LENGTH to not count prefix signaturesCount_ = (signatureLength_ - _PREFIX_SIGNAL_LENGTH) / 85; } signaturesParams_ = new SignatureParams[](signaturesCount_); for (uint256 i; i < signaturesCount_; ) { // used operations can not overflow / underflow unchecked { // +_PREFIX_SIGNAL_LENGTH to start after prefix uint256 signerOffset_ = (i * 85) + _SIGNATURE_LENGTH + _PREFIX_SIGNAL_LENGTH; bytes memory signerBytes_ = signature[signerOffset_:signerOffset_ + 20]; address signer_; // cast bytes to address in the easiest way via assembly assembly { signer_ := shr(96, mload(add(signerBytes_, 0x20))) } signaturesParams_[i] = SignatureParams({ signature: signature[(signerOffset_ - _SIGNATURE_LENGTH):signerOffset_], signer: signer_ }); ++i; } } } else { // multiple signatures are present that should form `SignatureParams[]` through abi.decode // @dev this will fail and revert if invalid typed data is passed in signaturesParams_ = abi.decode(signature, (SignatureParams[])); } return signaturesParams_; } /// @notice Marks a bytes32 `message_` (signature digest) as signed, making it verifiable by EIP-1271 `isValidSignature()`. /// - Can only be self-called (authorization same as for `cast` methods). /// @param message_ data hash to be allow-listed as signed /// @dev input `message_` is hashed with `domainSeparatorV4()` according to EIP712 typed data (`EIP1271_TYPE_HASH`) function signMessage(bytes32 message_) external onlyDelegateCall { // hashing with domain separator mitigates any potential replaying on other networks or other Avocados of the same owner message_ = ECDSA.toTypedDataHash( _domainSeparatorV4( DOMAIN_SEPARATOR_SALT_HASHED // includes block.chainid ), keccak256(abi.encode(EIP1271_TYPE_HASH, message_)) ); _signedMessages[message_] = 1; emit SignedMessage(message_); } /// @notice Removes a previously `signMessage()` signed bytes32 `message_` (signature digest). /// - Can only be self-called (authorization same as for `cast` methods). /// @param message_ data hash to be removed from allow-listed signatures function removeSignedMessage(bytes32 message_) external onlyDelegateCall { _signedMessages[message_] = 0; emit RemoveSignedMessage(message_); } } abstract contract AvocadoMultisigPayAuthorizedFee is AvocadoMultisigSecondaryCore { /// @notice pays the fee for `castAuthorized()` calls via the AvoRegistry (or fallback) /// @param gasUsedFrom_ `gasleft()` snapshot at gas measurement starting point /// @param maxFee_ maximum acceptable fee to be paid, revert if fee is bigger than this value function payAuthorizedFee(uint256 gasUsedFrom_, uint256 maxFee_) external payable onlyDelegateCall { // @dev part below costs ~24k gas for if `feeAmount_` and `maxFee_` is set uint256 feeAmount_; address payable feeCollector_; { uint256 gasUsed_; unchecked { // gas can not underflow // gasUsed already includes everything at this point except for paying fee logic gasUsed_ = gasUsedFrom_ - gasleft(); } // Using a low-level function call to prevent reverts (making sure the contract is truly non-custodial). // also limit gas, so that registry can not cause out of gas. (bool success_, bytes memory result_) = address(avoRegistry).staticcall{ gas: 15000 }( abi.encodeWithSignature("calcFee(uint256)", gasUsed_) ); // checks to ensure decoding does not fail, breaking non-custodial feature uint256 addressValue; assembly { addressValue := mload(add(result_, 0x40)) } if (success_ && result_.length > 63 && addressValue <= type(uint160).max) { // result bytes length < 64 or a too long address value would fail the abi.decode and cause revert (feeAmount_, feeCollector_) = abi.decode(result_, (uint256, address)); if (feeAmount_ > AUTHORIZED_MAX_FEE) { // make sure AvoRegistry fee is capped feeAmount_ = AUTHORIZED_MAX_FEE; } } else { // registry calcFee failed. Use local backup minimum fee feeCollector_ = AUTHORIZED_FEE_COLLECTOR; feeAmount_ = AUTHORIZED_MIN_FEE; } } // pay fee, if any if (feeAmount_ > 0) { if (maxFee_ > 0 && feeAmount_ > maxFee_) { revert AvocadoMultisig__MaxFee(feeAmount_, maxFee_); } // sending fee based on OZ Address.sendValue, but modified to properly act based on actual error case // (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/utils/Address.sol#L60) if (address(this).balance < feeAmount_) { revert AvocadoMultisig__InsufficientBalance(feeAmount_); } // Setting gas to very low 1000 because 2_300 gas is added automatically for a .call with a value amount. // This should be enough for any normal transfer to an EOA or an Avocado Multisig. (bool success_, ) = feeCollector_.call{ value: feeAmount_, gas: 1000 }(""); if (success_) { emit FeePaid(feeAmount_); } else { // do not revert, as an error on the feeCollector_ side should not be the "fault" of the Avo contract. // Letting this case pass ensures that the contract is truly non-custodial (not blockable by feeCollector) emit FeePayFailed(feeAmount_); } } else { emit FeePaid(feeAmount_); } } } abstract contract AvocadoMultisigSigners is AvocadoMultisigSecondaryCore { /// @notice adds `addSigners_` to allowed signers and sets required signers count to `requiredSigners_` /// Note the `addSigners_` to be added must: /// - NOT be duplicates (already present in current allowed signers) /// - NOT be the zero address /// - be sorted ascending function addSigners(address[] calldata addSigners_, uint8 requiredSigners_) external onlyDelegateCall { uint256 addSignersLength_ = addSigners_.length; // check array length and make sure signers can not be zero address // (only check for first elem needed, rest is checked through sort) if (addSignersLength_ == 0 || addSigners_[0] == address(0)) { revert AvocadoMultisig__InvalidParams(); } address[] memory currentSigners_ = _getSigners(); uint256 currentSignersLength_ = currentSigners_.length; uint256 newSignersLength_ = currentSignersLength_ + addSignersLength_; if (newSignersLength_ > MAX_SIGNERS_COUNT) { revert AvocadoMultisig__InvalidParams(); } address[] memory newSigners_ = new address[](newSignersLength_); uint256 currentSignersPos_ = 0; // index of position of loop in currentSigners_ array uint256 addedCount_ = 0; // keep track of number of added signers of current signers array for (uint256 i; i < newSignersLength_; ) { unchecked { currentSignersPos_ = i - addedCount_; } if ( addedCount_ == addSignersLength_ || (currentSignersPos_ < currentSignersLength_ && currentSigners_[currentSignersPos_] < addSigners_[addedCount_]) ) { // if already added all signers or if current signer is < next signer, keep the current one newSigners_[i] = currentSigners_[currentSignersPos_]; } else { // add signer newSigners_[i] = addSigners_[addedCount_]; emit SignerAdded(addSigners_[addedCount_]); unchecked { ++addedCount_; } } if (i > 0 && newSigners_[i] <= newSigners_[i - 1]) { // make sure input signers are ordered ascending and no duplicate signers are added revert AvocadoMultisig__InvalidParams(); } unchecked { ++i; } } // update values in storage _setSigners(newSigners_, requiredSigners_); // updates `signersCount`, checks and sets `requiredSigners_` // sync mappings at AvoSignersList -> must happen *after* storage write update // use call with success_ here to not block users transaction if the helper contract fails. // in case of failure, only emit event ListSyncFailed() so off-chain tracking is informed to react. (bool success_, ) = address(avoSignersList).call( abi.encodeCall(IAvoSignersList.syncAddAvoSignerMappings, (address(this), addSigners_)) ); if (!success_) { emit ListSyncFailed(); } } /// @notice removes `removeSigners_` from allowed signers and sets required signers count to `requiredSigners_` /// Note the `removeSigners_` to be removed must: /// - NOT be the owner /// - be sorted ascending /// - be present in current allowed signers function removeSigners(address[] calldata removeSigners_, uint8 requiredSigners_) external onlyDelegateCall { uint256 removeSignersLength_ = removeSigners_.length; if (removeSignersLength_ == 0) { revert AvocadoMultisig__InvalidParams(); } address[] memory currentSigners_ = _getSigners(); uint256 currentSignersLength_ = currentSigners_.length; uint256 newSignersLength_ = currentSignersLength_ - removeSignersLength_; address owner_ = IAvocado(address(this))._owner(); address[] memory newSigners_ = new address[](newSignersLength_); uint256 currentInsertPos_ = 0; // index of position of loop in `newSigners_` array uint256 removedCount_ = 0; // keep track of number of removed signers of current signers array for (uint256 i; i < currentSignersLength_; ) { unchecked { currentInsertPos_ = i - removedCount_; } if (removedCount_ == removeSignersLength_ || currentSigners_[i] != removeSigners_[removedCount_]) { // if already removed all signers or if current signer is not a signer to be removed, keep the current one if (currentInsertPos_ < newSignersLength_) { // make sure index to insert is within bounds of newSigners_ array newSigners_[currentInsertPos_] = currentSigners_[i]; } else { // a signer has been passed in that was not found and thus we would be inserting at a position // in newSigners_ array that overflows its length revert AvocadoMultisig__InvalidParams(); } } else { // remove signer, i.e. do not insert the current signer in the newSigners_ array // make sure signer to be removed is not the owner if (removeSigners_[removedCount_] == owner_) { revert AvocadoMultisig__InvalidParams(); } emit SignerRemoved(removeSigners_[removedCount_]); unchecked { ++removedCount_; } } unchecked { ++i; } } if (removedCount_ != removeSignersLength_) { // this case should not be possible but it is a good cheap extra check to make sure nothing goes wrong // and the contract does not end up in an invalid signers state revert AvocadoMultisig__InvalidParams(); } // update values in storage _setSigners(newSigners_, requiredSigners_); // updates `signersCount`, checks and sets `requiredSigners_` // sync mappings at AvoSignersList -> must happen *after* storage write update // use call with success_ here to not block users transaction if the helper contract fails. // in case of failure, only emit event ListSyncFailed() so off-chain tracking is informed to react. (bool success_, ) = address(avoSignersList).call( abi.encodeCall(IAvoSignersList.syncRemoveAvoSignerMappings, (address(this), removeSigners_)) ); if (!success_) { emit ListSyncFailed(); } } /// @notice sets number of required signers for a valid request to `requiredSigners_` function setRequiredSigners(uint8 requiredSigners_) external onlyDelegateCall { _setRequiredSigners(requiredSigners_); } /***********************************| | INTERNAL | |__________________________________*/ /// @dev writes `signers_` to storage with SSTORE2 and updates `signersCount`. uses `requiredSigners_` for sanity checks function _setSigners(address[] memory signers_, uint8 requiredSigners_) internal { uint256 signersCount_ = signers_.length; if (signersCount_ > MAX_SIGNERS_COUNT || signersCount_ == 0) { revert AvocadoMultisig__InvalidParams(); } if (signersCount_ == 1) { // if signersCount is 1, owner must be the only signer (checked in `removeSigners`) // can reset to empty "uninitialized" signer vars state, making subsequent interactions cheaper // and even giving a gas refund for clearing out the slot 1 if (requiredSigners_ != 1) { revert AvocadoMultisig__InvalidParams(); } if (_requiredSigners > 1) { emit RequiredSignersSet(1); } assembly { sstore(1, 0) // Reset slot 1 (signers related vars) to 0 } } else { _signersCount = uint8(signersCount_); _signersPointer = SSTORE2.write(abi.encode(signers_)); // required signers vs signersCount is checked in _setRequiredSigners _setRequiredSigners(requiredSigners_); } } /// @dev sets number of required signers to `requiredSigners_` and emits event RequiredSignersSet, if valid function _setRequiredSigners(uint8 requiredSigners_) internal { // check if number of actual signers is > `requiredSigners_` because otherwise // the multisig would end up in a broken state where no execution is possible anymore if (requiredSigners_ == 0 || requiredSigners_ > _getSignersCount()) { revert AvocadoMultisig__InvalidParams(); } if (_requiredSigners != requiredSigners_) { _requiredSigners = requiredSigners_; emit RequiredSignersSet(requiredSigners_); } } } abstract contract AvocadoMultisigInitialize is AvocadoMultisigSecondaryCore { /// @notice sets the initial state of the Multisig for `owner_` as owner and first and only required signer function initialize() external onlyDelegateCall { address owner_ = IAvocado(address(this))._owner(); // owner must be EOA if (Address.isContract(owner_) || owner_ == address(0)) { revert AvocadoMultisig__InvalidParams(); } // set _transientAllowHash so refund behaviour is already active for first tx and this cost is applied to deployment _resetTransientStorage(); // emit events emit SignerAdded(owner_); emit RequiredSignersSet(1); // add owner as signer at AvoSignersList address[] memory signers_ = new address[](1); signers_[0] = owner_; // use call with success_ here to not block users transaction if the helper contract fails. // in case of failure, only emit event ListSyncFailed() so off-chain tracking is informed to react. (bool success_, ) = address(avoSignersList).call( abi.encodeCall(IAvoSignersList.syncAddAvoSignerMappings, (address(this), signers_)) ); if (!success_) { emit ListSyncFailed(); } } } abstract contract AvocadoMultisigRevertReason is AvocadoMultisigSecondaryCore { /// @notice Get the revert reason from the returnedData (supports Panic, Error & Custom Errors). /// @param returnedData_ revert data of the call /// @return reason_ revert reason // // Based on https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/libs/CallUtils.sol // This is needed in order to provide some human-readable revert message from a call. function getRevertReasonFromReturnedData(bytes memory returnedData_) public pure returns (string memory reason_) { if (returnedData_.length < 4) { // case 1: catch all return "_REASON_NOT_DEFINED"; } bytes4 errorSelector_; assembly { errorSelector_ := mload(add(returnedData_, 0x20)) } if (errorSelector_ == bytes4(0x4e487b71)) { // case 2: Panic(uint256), selector 0x4e487b71 (Defined since 0.8.0) // ref: https://docs.soliditylang.org/en/v0.8.0/control-structures.html#panic-via-assert-and-error-via-require) // convert last byte to hex digits -> string to decode the panic code assembly { returnedData_ := add(returnedData_, 0x04) // skip error selector } reason_ = string.concat("_TARGET_PANICKED: ", Strings.toHexString(uint256(bytes32(returnedData_)))); } else if (errorSelector_ == bytes4(0x08c379a0)) { // case 3: Error(string), selector 0x08c379a0 (Defined at least since 0.7.0) // based on https://ethereum.stackexchange.com/a/83577 assembly { returnedData_ := add(returnedData_, 0x04) // skip error selector } reason_ = string.concat("_", abi.decode(returnedData_, (string))); } else { // case 4: Custom errors (Defined since 0.8.0) // convert bytes4 selector to string reason_ = string.concat("_CUSTOM_ERROR: ", Strings.toHexString(uint256(uint32(errorSelector_)))); // decode custom error params if there are any, they are returned raw as hex string, // up to a length of `REVERT_REASON_MAX_LENGTH` uint256 paramsLength_ = (returnedData_.length - 4); if (paramsLength_ * 2 > REVERT_REASON_MAX_LENGTH + 8) { paramsLength_ = REVERT_REASON_MAX_LENGTH + 4; } bytes memory result_ = new bytes(paramsLength_ * 2); for (uint256 i; i < paramsLength_; ) { // use unchecked as i is < 4 and division. unchecked { result_[2 * i] = _toHexDigit(uint8(returnedData_[i + 4]) / 16); result_[2 * i + 1] = _toHexDigit(uint8(returnedData_[i + 4]) % 16); ++i; } } reason_ = string.concat(reason_, ". PARAMS_RAW: "); reason_ = string.concat(reason_, string(result_)); } { // truncate reason_ string to REVERT_REASON_MAX_LENGTH for reserveGas used to ensure Cast event is emitted if (bytes(reason_).length > REVERT_REASON_MAX_LENGTH) { bytes memory reasonBytes_ = bytes(reason_); uint256 maxLength_ = REVERT_REASON_MAX_LENGTH + 1; // cheaper than <= in each loop bytes memory truncatedRevertReason_ = new bytes(maxLength_); for (uint256 i; i < maxLength_; ) { truncatedRevertReason_[i] = reasonBytes_[i]; unchecked { ++i; } } reason_ = string(truncatedRevertReason_); } } } /// @dev used to convert bytes4 selector to string function _toHexDigit(uint8 d) internal pure returns (bytes1) { // use unchecked as the operations with d can not over / underflow unchecked { if (d < 10) { return bytes1(uint8(bytes1("0")) + d); } if (d < 16) { return bytes1(uint8(bytes1("a")) + d - 10); } } revert AvocadoMultisig__ToHexDigit(); } } abstract contract AvocadoMultisigSimulate is AvocadoMultisigSecondaryCore, AvocadoMultisigRevertReason { /// @notice executes a SIMULATE cast process for `cast()` or `castChainAgnostic()` for gas estimations. /// @dev - set `signaturesParams_` to empty to automatically simulate with required signers length. /// - if `signaturesParams_` first element signature is not set, or if first signer is set to /// 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF, then gas usage burn is simulated /// for verify signature functionality. DO NOT set signature to non-empty for subsequent /// elements then; set all signatures to empty! /// - if `signaturesParams_` is set normally, signatures are verified as in actual execute /// - buffer amounts for mock smart contract signers signature verification must be added /// off-chain as this varies on a case per case basis. /// @param params_ Cast params such as id, avoNonce and actions to execute /// @param forwardParams_ Cast params related to validity of forwarding as instructed and signed /// @param signaturesParams_ SignatureParams structs array for signature and signer: /// - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA. /// For smart contract signatures it must fulfill the requirements for the relevant /// smart contract `.isValidSignature()` EIP1271 logic /// - signer: address of the signature signer. /// Must match the actual signature signer or refer to the smart contract /// that must be an allowed signer and validates signature via EIP1271 /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` struct for all `CastChainAgnosticParams` /// struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`. /// must be set in the same order as when creating the signature. /// @return success_ true if all actions were executed succesfully, false otherwise. /// @return revertReason_ revert reason if one of the actions fails in the following format: /// The revert reason will be prefixed with the index of the action. /// e.g. if action 1 fails, then the reason will be "1_reason". /// if an action in the flashloan callback fails (or an otherwise nested action), /// it will be prefixed with with two numbers: "1_2_reason". /// e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails, /// the reason will be 1_2_reason. function simulateCast( CastParams calldata params_, CastForwardParams calldata forwardParams_, SignatureParams[] memory signaturesParams_, ChainAgnosticHash[] memory chainAgnosticHashes_ ) public onlyDelegateCall returns (bool success_, string memory revertReason_) { _simulateValidateParams( params_.actions.length, params_.avoNonce, forwardParams_.validAfter, forwardParams_.validUntil, forwardParams_.value ); if (chainAgnosticHashes_.length > 0) { // validate that input `CastParams` and `CastForwardParams` are present in `chainAgnosticHashes_` _simulateValidateChainAgnostic( _castChainAgnosticParamsHash(params_, forwardParams_, block.chainid), chainAgnosticHashes_ ); } bytes32 digest_ = chainAgnosticHashes_.length > 0 ? _getSigDigestChainAgnostic(chainAgnosticHashes_) : _getSigDigest(params_, forwardParams_); // if signaturesParams is not set, we use required signers to simulate gas usage address[] memory signers_ = new address[]( signaturesParams_.length > 0 ? signaturesParams_.length : _getRequiredSigners() ); if ( signaturesParams_.length == 0 || signaturesParams_[0].signature.length == 0 || signaturesParams_[0].signer == 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF ) { // simulate verify sig by burning gas according to the measurements as listed in comments in // _verifySig` in AvocadoMultisigCore. uint256 signersCount_ = uint256(_getSignersCount()); // Avoado signersCount == 1 ? -> 11_000 gas. If only owner is signer then it is cheaper -> 8_500 gas. uint256 verifySigGas_ = signersCount_ == 1 ? 8500 : 11_000; // Avoado signersCount > 1 ? -> 6400 + allowedSignersCount * 160 + signersLength * 6900 if (signers_.length > 1) { verifySigGas_ = 6_400 + (signersCount_ * 160) + (signers_.length * 6_900); } // is non Sequential nonce? + 2200 if (params_.avoNonce == -1) { verifySigGas_ += 2_200; } if (signaturesParams_.length == 0) { // calldata etc. cost per signaturesParams array element is ~3.300 gas verifySigGas_ += (signers_.length * 3_300); } // is smart contract signer buffer amount is external contract dependent, must be added off-chain // waste the gas `verifySigGas_` to correctly account for gas usage in estimateGas calls uint256 gasLeft_ = gasleft(); uint256 wasteGasCounter_; while (gasLeft_ - gasleft() < verifySigGas_) wasteGasCounter_++; } else { signers_ = _verifySigWithRevert(digest_, signaturesParams_, params_.avoNonce == -1); } { (success_, revertReason_) = _simulateExecuteCast( params_, _dynamicReserveGas(CAST_EVENTS_RESERVE_GAS, signers_.length, params_.metadata.length), params_.avoNonce == -1 ? digest_ : bytes32(0) ); } if (success_) { emit CastExecuted(params_.source, msg.sender, signers_, params_.metadata); } else { emit CastFailed(params_.source, msg.sender, signers_, revertReason_, params_.metadata); } } /// @dev SIMULATES: executes `actions_` with respective target, calldata, operation etc. // // this is called by _callTargets() and executeOperation() in AvocadoMultisig.sol when the tx.origin is dead address. // making this public and calling via delegateCall reduces code duplication and simplifies simulation logic. function _simulateExecuteActions( Action[] memory actions_, uint256 id_, bool isFlashloanCallback_ ) external onlyDelegateCall { // reset _transientAllowHash immediately to avert reentrancy etc. & get the gas refund (4800) _resetTransientStorage(); uint256 storageSlot0Snapshot_; // avoImpl, nonce, initialized vars uint256 storageSlot1Snapshot_; // signers related variables // delegate call = ids 1 and 21 bool isDelegateCallId_ = id_ == 1 || id_ == 21; if (isDelegateCallId_) { // store values before execution to make sure core storage vars are not modified by a delegatecall. // this ensures the smart wallet does not end up in a corrupted state. // for mappings etc. it is hard to protect against storage changes, so we must rely on the owner / signer // to know what is being triggered and the effects of a tx assembly { storageSlot0Snapshot_ := sload(0x0) // avoImpl, nonce & initialized vars storageSlot1Snapshot_ := sload(0x1) // signers related variables } } uint256 actionsLength_ = actions_.length; for (uint256 i; i < actionsLength_; ) { Action memory action_ = actions_[i]; // execute action bool success_; bytes memory result_; uint256 actionMinGasLeft_; if (action_.operation == 0 && (id_ < 2 || id_ == 20 || id_ == 21)) { // call (operation = 0 & id = call(0 / 20) or mixed(1 / 21)) // low-level call will return success true also if action target is not even a contract. // we do not explicitly check for this, default interaction is via UI which can check and handle this. // Also applies to delegatecall etc. (success_, result_) = action_.target.call{ value: action_.value }(action_.data); // handle action failure right after external call to better detect out of gas errors if (!success_) { _simulateHandleActionFailure(actionMinGasLeft_, i, result_); } } else if (action_.operation == 1 && isDelegateCallId_) { // delegatecall (operation = 1 & id = mixed(1 / 21)) (success_, result_) = action_.target.delegatecall(action_.data); // handle action failure right after external call to better detect out of gas errors if (!success_) { _simulateHandleActionFailure(actionMinGasLeft_, i, result_); } // reset _transientAllowHash to make sure it can not be set up in any way for reentrancy _resetTransientStorage(); // for delegatecall, make sure storage was not modified. After every action, to also defend reentrancy uint256 storageSlot0_; uint256 storageSlot1_; assembly { storageSlot0_ := sload(0x0) // avoImpl, nonce & initialized vars storageSlot1_ := sload(0x1) // signers related variables } if (!(storageSlot0_ == storageSlot0Snapshot_ && storageSlot1_ == storageSlot1Snapshot_)) { revert(string.concat(Strings.toString(i), "_AVO__MODIFIED_STORAGE")); } } else if (action_.operation == 2 && (id_ == 20 || id_ == 21)) { // flashloan (operation = 2 & id = flashloan(20 / 21)) if (isFlashloanCallback_) { revert(string.concat(Strings.toString(i), "_AVO__NO_FLASHLOAN_IN_FLASHLOAN")); } // flashloan is always executed via .call, flashloan aggregator uses `msg.sender`, so .delegatecall // wouldn't send funds to this contract but rather to the original sender. bytes memory data_ = action_.data; assembly { data_ := add(data_, 4) // Skip function selector (4 bytes) } // get actions data from calldata action_.data. Only supports InstaFlashAggregatorInterface (, , , data_, ) = abi.decode(data_, (address[], uint256[], uint256, bytes, bytes)); // set allowHash to signal allowed entry into executeOperation() _transientAllowHash = bytes31( keccak256(abi.encode(data_, block.timestamp, EXECUTE_OPERATION_SELECTOR)) ); // store id_ in transient storage slot _transientId = uint8(id_); // handle action failure right after external call to better detect out of gas errors (success_, result_) = action_.target.call{ value: action_.value }(action_.data); if (!success_) { _simulateHandleActionFailure(actionMinGasLeft_, i, result_); } // reset _transientAllowHash to prevent reentrancy during actions execution _resetTransientStorage(); } else { // either operation does not exist or the id was not set according to what the action wants to execute revert(string.concat(Strings.toString(i), "_AVO__INVALID_ID_OR_OPERATION")); } unchecked { ++i; } } } /***********************************| | INTERNAL | |__________________________________*/ /// @dev SIMULATES: executes multiple cast actions according to CastParams `params_`, reserving `reserveGas_` in this contract. /// Uses a sequential nonce unless `nonSequentialNonce_` is set. /// @return success_ boolean flag indicating whether all actions have been executed successfully. /// @return revertReason_ if `success_` is false, then revert reason is returned as string here. function _simulateExecuteCast( CastParams calldata params_, uint256 /** reserveGas_ */, bytes32 nonSequentialNonce_ ) internal returns (bool success_, string memory revertReason_) { // set allowHash to signal allowed entry into _callTargets with actions in current block only _transientAllowHash = bytes31( keccak256(abi.encode(params_.actions, params_.id, block.timestamp, _CALL_TARGETS_SELECTOR)) ); // nonce must be used *always* if signature is valid if (nonSequentialNonce_ == bytes32(0)) { // use sequential nonce, already validated in `_validateParams()` _avoNonce++; } else { // use non-sequential nonce, already validated in `_verifySig()` nonSequentialNonces[nonSequentialNonce_] = 1; } // execute _callTargets via a low-level call to create a separate execution frame // this is used to revert all the actions if one action fails without reverting the whole transaction bytes memory calldata_ = abi.encodeCall(AvocadoMultisigProtected._callTargets, (params_.actions, params_.id)); bytes memory result_; // using inline assembly for delegatecall to define custom gas amount that should stay here in caller assembly { success_ := delegatecall( gas(), // send all remaining gas for simulate // load _avoImpl from slot 0 and explicitly convert to address with bit mask and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff), add(calldata_, 0x20), mload(calldata_), 0, 0 ) let size := returndatasize() result_ := mload(0x40) mstore(0x40, add(result_, and(add(add(size, 0x20), 0x1f), not(0x1f)))) mstore(result_, size) returndatacopy(add(result_, 0x20), 0, size) } if (!success_) { if (result_.length == 0) { revertReason_ = "AVO__REASON_NOT_DEFINED"; } else { assembly { result_ := add(result_, 0x04) } revertReason_ = abi.decode(result_, (string)); } } // reset all transient variables to get the gas refund (4800) _resetTransientStorage(); } /// @dev SIMULATES: handles failure of an action execution depending on error cause, /// decoding and reverting with `result_` as reason string. function _simulateHandleActionFailure( uint256 /** actionMinGasLeft_ */, uint256 i_, bytes memory result_ ) internal pure { revert(string.concat(Strings.toString(i_), getRevertReasonFromReturnedData(result_))); } /// @dev SIMULATES: Validates input params, reverts on invalid values. /// @param actionsLength_ the length of the actions array to execute /// @param avoNonce_ the avoNonce from input CastParams /// @param validAfter_ timestamp after which the request is valid /// @param validUntil_ timestamp before which the request is valid /// @param value_ the msg.value expected to be sent along function _simulateValidateParams( uint256 actionsLength_, int256 avoNonce_, uint256 validAfter_, uint256 validUntil_, uint256 value_ ) internal view { // make sure actions are defined and nonce is valid: // must be -1 to use a non-sequential nonce or otherwise it must match the avoNonce if (!(actionsLength_ > 0 && (avoNonce_ == -1 || uint256(avoNonce_) == _avoNonce))) { // revert AvocadoMultisig__InvalidParams(); // no revert on simulate } // make sure request is within valid timeframe if ((validAfter_ > block.timestamp) || (validUntil_ > 0 && validUntil_ < block.timestamp)) { // revert AvocadoMultisig__InvalidTiming(); // no revert on simulate } // make sure msg.value matches `value_` (if set) if (value_ > 0 && msg.value != value_) { // revert AvocadoMultisig__InvalidParams(); // no revert on simulate } } /// @dev SIMULATES: Validates input params for `castChainAgnostic`: verifies that the `curCastChainAgnosticHash_` is present in /// the `castChainAgnosticHashes_` array of hashes. Reverts with `AvocadoMultisig__InvalidParams` if not. /// Reverts with `AvocadoMultisig__ChainAgnosticChainMismatch` if the hash is present but valid for another chain. function _simulateValidateChainAgnostic( bytes32 curCastChainAgnosticHash_, ChainAgnosticHash[] memory castChainAgnosticHashes_ ) internal view { uint256 length_ = castChainAgnosticHashes_.length; // chain agnostic must be at least 2 hashes if (length_ > 1) { for (uint256 i; i < length_; ) { if ( curCastChainAgnosticHash_ == castChainAgnosticHashes_[i].hash && block.chainid == castChainAgnosticHashes_[i].chainId ) { // hash must be found, and must be for the current chain to be valid return; } unchecked { ++i; } } } // `_castChainAgnosticParamsHash()` of current input params is not present in castChainAgnosticHashes_ -> revert // revert AvocadoMultisig__InvalidParams(); // no revert on simulate } } contract AvocadoMultisigSecondary is AvocadoMultisigBase, AvocadoMultisigSecondaryCore, AvocadoMultisigUpgradeTo, AvocadoMultisigOccupyNonces, AvocadoMultisigEIP1271, AvocadoMultisigPayAuthorizedFee, AvocadoMultisigSigners, AvocadoMultisigInitialize, AvocadoMultisigRevertReason, AvocadoMultisigSimulate { /***********************************| | CONSTRUCTOR / INITIALIZERS | |__________________________________*/ /// @notice constructor sets multiple immutable values for contracts and payFee fallback logic. /// @param avoRegistry_ address of the avoRegistry (proxy) contract /// @param avoForwarder_ address of the avoForwarder (proxy) contract /// to forward tx with valid signatures. must be valid version in AvoRegistry. /// @param avoSignersList_ address of the AvoSignersList (proxy) contract /// @param avoConfigV1_ AvoConfigV1 contract holding values for authorizedFee values constructor( IAvoRegistry avoRegistry_, address avoForwarder_, IAvoSignersList avoSignersList_, IAvoConfigV1 avoConfigV1_ ) AvocadoMultisigBase(avoRegistry_, avoForwarder_, avoSignersList_, avoConfigV1_) {} }
// 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 { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import { IAvoRegistry } from "../interfaces/IAvoRegistry.sol"; import { IAvoSignersList } from "../interfaces/IAvoSignersList.sol"; import { IAvocadoMultisigV1Base } from "../interfaces/IAvocadoMultisigV1.sol"; import { IAvocadoMultisigV1Secondary, IAvocadoMultisigV1SecondaryConstants } from "../interfaces/IAvocadoMultisigV1Secondary.sol"; import { IAvocado } from "../Avocado.sol"; import { IAvoConfigV1 } from "../interfaces/IAvoConfigV1.sol"; import { AvocadoMultisigBase, AvocadoMultisigCore } from "./AvocadoMultisigCore.sol"; // --------------------------- DEVELOPER NOTES ----------------------------------------- // @dev IMPORTANT: all storage variables go into AvocadoMultisigVariables.sol // ------------------------------------------------------------------------------------- // empty interface used for Natspec docs for nice layout in automatically generated docs: // /// @title AvocadoMultisig v1.1.0 /// @notice Smart wallet enabling meta transactions through multiple EIP712 signatures (Multisig n out of m). /// /// Supports: /// - Executing arbitrary actions /// - Receiving NFTs (ERC721) /// - Receiving ERC1155 tokens /// - ERC1271 smart contract signatures /// - Instadapp Flashloan callbacks /// - chain-agnostic signatures, user can sign once for execution of actions on different chains. /// /// The `cast` method allows the AvoForwarder (relayer) to execute multiple arbitrary actions authorized by signature. /// /// Broadcasters are expected to call the AvoForwarder contract `execute()` method, which also automatically /// deploys an AvocadoMultisig if necessary first. /// /// Upgradeable by calling `upgradeTo` through a `cast` / `castAuthorized` call. /// /// The `castAuthorized` method allows the signers of the wallet to execute multiple arbitrary actions with signatures /// without the AvoForwarder in between, to guarantee the smart wallet is truly non-custodial. /// /// _@dev Notes:_ /// - This contract implements parts of EIP-2770 in a minimized form. E.g. domainSeparator is immutable etc. /// - This contract does not implement ERC2771, because trusting an upgradeable "forwarder" bears a security /// risk for this non-custodial wallet. /// - Signature related logic is based off of OpenZeppelin EIP712Upgradeable. /// - All signatures are validated for defaultChainId of `634` instead of `block.chainid` from opcode (EIP-1344). /// - For replay protection, the current `block.chainid` instead is used in the EIP-712 salt. interface AvocadoMultisig_V1 {} /// @dev Simple contract to upgrade the implementation address stored at storage slot 0x0. /// Mostly based on OpenZeppelin ERC1967Upgrade contract, adapted with onlySelf etc. /// IMPORTANT: For any new implementation, the upgrade method MUST be in the implementation itself, /// otherwise it can not be upgraded anymore! abstract contract AvocadoMultisigSelfUpgradeable is AvocadoMultisigCore { /// @notice upgrade the contract to a new implementation address. /// - Must be a valid version at the AvoRegistry. /// - Can only be self-called (authorization same as for `cast` methods). /// @param avoImplementation_ New contract address /// @param afterUpgradeHookData_ flexible bytes for custom usage in after upgrade hook logic // // Implementation must call `_afterUpgradeHook()` function upgradeTo(address avoImplementation_, bytes calldata afterUpgradeHookData_) public onlySelf { _spell(address(avoSecondary), msg.data); } /// @notice hook called after executing an upgrade from previous `fromImplementation_`, with flexible bytes `data_` function _afterUpgradeHook(address fromImplementation_, bytes calldata data_) public virtual onlySelf {} } abstract contract AvocadoMultisigProtected is AvocadoMultisigCore { /***********************************| | ONLY SELF | |__________________________________*/ /// @notice occupies the sequential `avoNonces_` in storage. This can be used to cancel / invalidate /// a previously signed request(s) because the nonce will be "used" up. /// - Can only be self-called (authorization same as for `cast` methods). /// @param avoNonces_ sequential ascending ordered nonces to be occupied in storage. /// E.g. if current AvoNonce is 77 and txs are queued with avoNonces 77, 78 and 79, /// then you would submit [78, 79] here because 77 will be occupied by the tx executing /// `occupyAvoNonces()` as an action itself. If executing via non-sequential nonces, you would /// submit [77, 78, 79]. /// - Maximum array length is 5. /// - gap from the current avoNonce will revert (e.g. [79, 80] if current one is 77) function occupyAvoNonces(uint88[] calldata avoNonces_) external onlySelf { _spell(address(avoSecondary), msg.data); } /// @notice occupies the `nonSequentialNonces_` in storage. This can be used to cancel / invalidate /// previously signed request(s) because the nonce will be "used" up. /// - Can only be self-called (authorization same as for `cast` methods). /// @param nonSequentialNonces_ the non-sequential nonces to occupy function occupyNonSequentialNonces(bytes32[] calldata nonSequentialNonces_) external onlySelf { _spell(address(avoSecondary), msg.data); } /***********************************| | FLASHLOAN CALLBACK | |__________________________________*/ /// @dev callback used by Instadapp Flashloan Aggregator, executes operations while owning /// the flashloaned amounts. `data_` must contain actions, one of them must pay back flashloan // /// @param assets_ assets_ received a flashloan for // /// @param amounts_ flashloaned amounts for each asset // /// @param premiums_ fees to pay for the flashloan /// @param initiator_ flashloan initiator -> must be this contract /// @param data_ data bytes containing the `abi.encoded()` actions that are executed like in `CastParams.actions` function executeOperation( address[] calldata /* assets_ */, uint256[] calldata /* amounts_ */, uint256[] calldata /* premiums_ */, address initiator_, bytes calldata data_ ) external returns (bool) { // @dev using the valid case inverted via one ! to optimize gas usage // data_ includes id and actions if ( !(_transientAllowHash == bytes31(keccak256(abi.encode(data_, block.timestamp, EXECUTE_OPERATION_SELECTOR))) && initiator_ == address(this)) ) { revert AvocadoMultisig__Unauthorized(); } // get and reset transient id uint256 id_ = uint256(_transientId); _transientId = 0; if (tx.origin == 0x000000000000000000000000000000000000dEaD) { // tx origin 0x000000000000000000000000000000000000dEaD used for backend gas estimations -> forward to simulate _spell( address(avoSecondary), abi.encodeCall(avoSecondary._simulateExecuteActions, (abi.decode(data_, (Action[])), id_, true)) ); } else { // decode actions to be executed after getting the flashloan and id_ packed into the data_ _executeActions(abi.decode(data_, (Action[])), id_, true); } return true; } /***********************************| | INDIRECT INTERNAL | |__________________________________*/ /// @dev executes a low-level .call or .delegateCall on all `actions_`. /// Can only be self-called by this contract under certain conditions, essentially internal method. /// This is called like an external call to create a separate execution frame. /// This way we can revert all the `actions_` if one fails without reverting the whole transaction. /// @param actions_ the actions to execute (target, data, value, operation) /// @param id_ id for `actions_`, see `CastParams.id` function _callTargets(Action[] calldata actions_, uint256 id_) external payable { if (tx.origin == 0x000000000000000000000000000000000000dEaD) { // tx origin 0x000000000000000000000000000000000000dEaD used for backend gas estimations -> forward to simulate _spell(address(avoSecondary), abi.encodeCall(avoSecondary._simulateExecuteActions, (actions_, id_, false))); } else { // _transientAllowHash must be set if ( (_transientAllowHash != bytes31(keccak256(abi.encode(actions_, id_, block.timestamp, _CALL_TARGETS_SELECTOR)))) ) { revert AvocadoMultisig__Unauthorized(); } _executeActions(actions_, id_, false); } } } abstract contract AvocadoMultisigEIP1271 is AvocadoMultisigCore { /// @inheritdoc IERC1271 /// @param signature This can be one of the following: /// - empty: `hash` must be a previously signed message in storage then. /// - 65 bytes: owner signature for a Multisig with only owner as signer (requiredSigners = 1, signers=[owner]). /// - a multiple of 85 bytes, through grouping of 65 bytes signature + 20 bytes signer address each. /// To signal decoding this way, the signature bytes must be prefixed with `0xDEC0DE6520`. /// - the `abi.encode` result for `SignatureParams` struct array. /// @dev reverts with `AvocadoMultisig__InvalidEIP1271Signature` or `AvocadoMultisig__InvalidParams` if signature is invalid. /// @dev input `message_` is hashed with `domainSeparatorV4()` according to EIP712 typed data (`EIP1271_TYPE_HASH`) function isValidSignature( bytes32 hash, bytes calldata signature ) external view override returns (bytes4 magicValue) { // hashing with domain separator mitigates any potential replaying on other networks or other Avocados of the same owner hash = ECDSA.toTypedDataHash( _domainSeparatorV4( DOMAIN_SEPARATOR_SALT_HASHED // includes block.chainid ), keccak256(abi.encode(EIP1271_TYPE_HASH, hash)) ); // @dev function params without _ for inheritdoc if (signature.length == 0) { // must be pre-allow-listed via `signMessage` method if (_signedMessages[hash] != 1) { revert AvocadoMultisig__InvalidEIP1271Signature(); } } else { (bool validSignature_, ) = _verifySig( hash, // decode signaturesParams_ from bytes signature avoSecondary.decodeEIP1271Signature(signature, IAvocado(address(this))._owner()), // we have no way to know nonce type, so make sure validity test covers everything. // setting this flag true will check that the digest is not a used non-sequential nonce. // unfortunately, for sequential nonces it adds unneeded verification and gas cost, // because the check will always pass, but there is no way around it. true ); if (!validSignature_) { revert AvocadoMultisig__InvalidEIP1271Signature(); } } return EIP1271_MAGIC_VALUE; } /// @notice Marks a bytes32 `message_` (signature digest) as signed, making it verifiable by EIP-1271 `isValidSignature()`. /// - Can only be self-called (authorization same as for `cast` methods). /// @param message_ data hash to be allow-listed as signed /// @dev input `message_` is hashed with `domainSeparatorV4()` according to EIP712 typed data (`EIP1271_TYPE_HASH`) function signMessage(bytes32 message_) external onlySelf { _spell(address(avoSecondary), msg.data); } /// @notice Removes a previously `signMessage()` signed bytes32 `message_` (signature digest). /// - Can only be self-called (authorization same as for `cast` methods). /// @param message_ data hash to be removed from allow-listed signatures function removeSignedMessage(bytes32 message_) external onlySelf { _spell(address(avoSecondary), msg.data); } } abstract contract AvocadoMultisigSigners is AvocadoMultisigCore { /// @notice adds `addSigners_` to allowed signers and sets required signers count to `requiredSigners_` /// Note the `addSigners_` to be added must: /// - NOT be duplicates (already present in current allowed signers) /// - NOT be the zero address /// - be sorted ascending function addSigners(address[] calldata addSigners_, uint8 requiredSigners_) external onlySelf { _spell(address(avoSecondary), msg.data); } /// @notice removes `removeSigners_` from allowed signers and sets required signers count to `requiredSigners_` /// Note the `removeSigners_` to be removed must: /// - NOT be the owner /// - be sorted ascending /// - be present in current allowed signers function removeSigners(address[] calldata removeSigners_, uint8 requiredSigners_) external onlySelf { _spell(address(avoSecondary), msg.data); } /// @notice sets number of required signers for a valid request to `requiredSigners_` function setRequiredSigners(uint8 requiredSigners_) external onlySelf { _spell(address(avoSecondary), msg.data); } } abstract contract AvocadoMultisigCast is AvocadoMultisigCore { /// @inheritdoc IAvocadoMultisigV1Base function getSigDigest( CastParams memory params_, CastForwardParams memory forwardParams_ ) public view returns (bytes32) { return _getSigDigest(params_, forwardParams_); } /// @inheritdoc IAvocadoMultisigV1Base function verify( CastParams calldata params_, CastForwardParams calldata forwardParams_, SignatureParams[] calldata signaturesParams_ ) external view returns (bool) { _validateParams( params_.actions.length, params_.avoNonce, forwardParams_.validAfter, forwardParams_.validUntil, forwardParams_.value ); _verifySigWithRevert(_getSigDigest(params_, forwardParams_), signaturesParams_, params_.avoNonce == -1); return true; } /// @inheritdoc IAvocadoMultisigV1Base function cast( CastParams calldata params_, CastForwardParams calldata forwardParams_, SignatureParams[] memory signaturesParams_ ) external payable returns (bool success_, string memory revertReason_) { return _cast(params_, forwardParams_, signaturesParams_, new ChainAgnosticHash[](0)); } /// @inheritdoc IAvocadoMultisigV1Base function simulateCast( CastParams calldata params_, CastForwardParams calldata forwardParams_, SignatureParams[] memory signaturesParams_ ) external payable returns (bool success_, string memory revertReason_) { return _simulateCast(params_, forwardParams_, signaturesParams_, new ChainAgnosticHash[](0), false); } /// @inheritdoc IAvocadoMultisigV1Base function estimateCast( CastParams calldata params_, CastForwardParams calldata forwardParams_, SignatureParams[] memory signaturesParams_ ) external payable returns (bool success_, string memory revertReason_) { return _simulateCast(params_, forwardParams_, signaturesParams_, new ChainAgnosticHash[](0), true); } } abstract contract AvocadoMultisigCastChainAgnostic is AvocadoMultisigCore { /// @inheritdoc IAvocadoMultisigV1Base function castChainAgnostic( CastChainAgnosticParams calldata params_, SignatureParams[] memory signaturesParams_, ChainAgnosticHash[] calldata chainAgnosticHashes_ ) external payable returns (bool success_, string memory revertReason_) { if (params_.chainId != block.chainid) { revert AvocadoMultisig__InvalidParams(); } return _cast(params_.params, params_.forwardParams, signaturesParams_, chainAgnosticHashes_); } /// @inheritdoc IAvocadoMultisigV1Base function getChainAgnosticHashes( CastChainAgnosticParams[] calldata params_ ) public pure returns (ChainAgnosticHash[] memory chainAgnosticHashes_) { uint256 length_ = params_.length; if (length_ < 2) { revert AvocadoMultisig__InvalidParams(); } chainAgnosticHashes_ = new ChainAgnosticHash[](length_); for (uint256 i; i < length_; ) { chainAgnosticHashes_[i] = ChainAgnosticHash( _castChainAgnosticParamsHash(params_[i].params, params_[i].forwardParams, params_[i].chainId), params_[i].chainId ); unchecked { ++i; } } } /// @inheritdoc IAvocadoMultisigV1Base function getSigDigestChainAgnostic(CastChainAgnosticParams[] calldata params_) public view returns (bytes32) { return _getSigDigestChainAgnostic(getChainAgnosticHashes(params_)); } /// @inheritdoc IAvocadoMultisigV1Base function getSigDigestChainAgnosticFromHashes( ChainAgnosticHash[] calldata chainAgnosticHashes_ ) public view returns (bytes32) { return _getSigDigestChainAgnostic(chainAgnosticHashes_); } /// @inheritdoc IAvocadoMultisigV1Base function verifyChainAgnostic( CastChainAgnosticParams calldata params_, SignatureParams[] calldata signaturesParams_, ChainAgnosticHash[] calldata chainAgnosticHashes_ ) public view returns (bool) { if (params_.chainId != block.chainid) { revert AvocadoMultisig__InvalidParams(); } _validateParams( params_.params.actions.length, params_.params.avoNonce, params_.forwardParams.validAfter, params_.forwardParams.validUntil, params_.forwardParams.value ); _validateChainAgnostic( _castChainAgnosticParamsHash(params_.params, params_.forwardParams, block.chainid), chainAgnosticHashes_ ); _verifySigWithRevert( _getSigDigestChainAgnostic(chainAgnosticHashes_), signaturesParams_, params_.params.avoNonce == -1 ); return true; } /// @inheritdoc IAvocadoMultisigV1Base function simulateCastChainAgnostic( CastChainAgnosticParams calldata params_, SignatureParams[] memory signaturesParams_, ChainAgnosticHash[] calldata chainAgnosticHashes_ ) external payable returns (bool success_, string memory revertReason_) { if (params_.chainId != block.chainid) { revert AvocadoMultisig__InvalidParams(); } return _simulateCast(params_.params, params_.forwardParams, signaturesParams_, chainAgnosticHashes_, false); } /// @inheritdoc IAvocadoMultisigV1Base function estimateCastChainAgnostic( CastChainAgnosticParams calldata params_, SignatureParams[] memory signaturesParams_, ChainAgnosticHash[] calldata chainAgnosticHashes_ ) external payable returns (bool success_, string memory revertReason_) { if (params_.chainId != block.chainid) { revert AvocadoMultisig__InvalidParams(); } return _simulateCast(params_.params, params_.forwardParams, signaturesParams_, chainAgnosticHashes_, true); } } abstract contract AvocadoMultisigCastAuthorized is AvocadoMultisigCore { /// @inheritdoc IAvocadoMultisigV1Base function getSigDigestAuthorized( CastParams memory params_, CastAuthorizedParams memory authorizedParams_ ) public view returns (bytes32) { return _getSigDigestAuthorized(params_, authorizedParams_); } /// @inheritdoc IAvocadoMultisigV1Base function verifyAuthorized( CastParams calldata params_, CastAuthorizedParams calldata authorizedParams_, SignatureParams[] calldata signaturesParams_ ) external view returns (bool) { // make sure actions are defined and nonce is valid _validateParams( params_.actions.length, params_.avoNonce, authorizedParams_.validAfter, authorizedParams_.validUntil, 0 // no value param in authorized interaction ); _verifySigWithRevert( _getSigDigestAuthorized(params_, authorizedParams_), signaturesParams_, params_.avoNonce == -1 ); return true; } /// @inheritdoc IAvocadoMultisigV1Base function castAuthorized( CastParams calldata params_, CastAuthorizedParams calldata authorizedParams_, SignatureParams[] memory signaturesParams_ ) external payable returns (bool success_, string memory revertReason_) { uint256 gasSnapshot_ = gasleft(); // make sure actions are defined and nonce is valid _validateParams( params_.actions.length, params_.avoNonce, authorizedParams_.validAfter, authorizedParams_.validUntil, 0 // no value param in authorized interaction ); bytes32 digest_ = _getSigDigestAuthorized(params_, authorizedParams_); address[] memory signers_ = _verifySigWithRevert(digest_, signaturesParams_, params_.avoNonce == -1); (success_, revertReason_) = _executeCast( params_, _dynamicReserveGas(CAST_AUTHORIZED_RESERVE_GAS, signers_.length, params_.metadata.length), params_.avoNonce == -1 ? digest_ : bytes32(0) ); // @dev on changes in the code below this point, measure the needed reserve gas via `gasleft()` anew // and update reserve gas constant amounts if (success_) { emit CastExecuted(params_.source, msg.sender, signers_, params_.metadata); } else { emit CastFailed(params_.source, msg.sender, signers_, revertReason_, params_.metadata); } // @dev `_payAuthorizedFee()` costs ~24.5k gas for if a fee is configured and maxFee is set _spell( address(avoSecondary), abi.encodeCall(avoSecondary.payAuthorizedFee, (gasSnapshot_, authorizedParams_.maxFee)) ); // @dev ending point for measuring reserve gas should be here. Also see comment in `AvocadoMultisigCore._executeCast()` } } contract AvocadoMultisig is AvocadoMultisigBase, AvocadoMultisigCore, AvocadoMultisigSelfUpgradeable, AvocadoMultisigProtected, AvocadoMultisigEIP1271, AvocadoMultisigSigners, AvocadoMultisigCast, AvocadoMultisigCastAuthorized, AvocadoMultisigCastChainAgnostic { /***********************************| | CONSTRUCTOR / INITIALIZERS | |__________________________________*/ /// @notice constructor sets multiple immutable values for contracts and payFee fallback logic. /// @param avoRegistry_ address of the avoRegistry (proxy) contract /// @param avoForwarder_ address of the avoForwarder (proxy) contract /// to forward tx with valid signatures. must be valid version in AvoRegistry. /// @param avoSignersList_ address of the AvoSignersList (proxy) contract /// @param avoConfigV1_ AvoConfigV1 contract holding values for authorizedFee values /// @param secondary_ AvocadoMultisigSecondary contract for extended logic constructor( IAvoRegistry avoRegistry_, address avoForwarder_, IAvoSignersList avoSignersList_, IAvoConfigV1 avoConfigV1_, IAvocadoMultisigV1Secondary secondary_ ) AvocadoMultisigBase(avoRegistry_, avoForwarder_, avoSignersList_, avoConfigV1_) AvocadoMultisigCore(secondary_) { // sanity checks to ensure all immutables are configured with same values on AvoSecondary if ( address(IAvocadoMultisigV1SecondaryConstants(address(secondary_)).avoRegistry()) != address(avoRegistry_) || IAvocadoMultisigV1SecondaryConstants(address(secondary_)).avoForwarder() != avoForwarder_ || address(IAvocadoMultisigV1SecondaryConstants(address(secondary_)).avoSignersList()) != address(avoSignersList_) || IAvocadoMultisigV1SecondaryConstants(address(secondary_)).AUTHORIZED_MIN_FEE() != AUTHORIZED_MIN_FEE || IAvocadoMultisigV1SecondaryConstants(address(secondary_)).AUTHORIZED_MAX_FEE() != AUTHORIZED_MAX_FEE || IAvocadoMultisigV1SecondaryConstants(address(secondary_)).AUTHORIZED_FEE_COLLECTOR() != AUTHORIZED_FEE_COLLECTOR ) { revert AvocadoMultisig__InvalidParams(); } } /// @inheritdoc IAvocadoMultisigV1Base function initialize() public initializer { _spell(address(avoSecondary), msg.data); } /***********************************| | PUBLIC API | |__________________________________*/ receive() external payable {} /// @inheritdoc IAvocadoMultisigV1Base function domainSeparatorV4() public view returns (bytes32) { return _domainSeparatorV4( DOMAIN_SEPARATOR_SALT_HASHED // includes block.chainid ); } /// @inheritdoc IAvocadoMultisigV1Base function domainSeparatorV4ChainAgnostic() public view returns (bytes32) { return _domainSeparatorV4( DOMAIN_SEPARATOR_CHAIN_AGNOSTIC_SALT_HASHED // includes default chain id (634) ); } /// @inheritdoc IAvocadoMultisigV1Base function isSigner(address signer_) public view returns (bool) { address[] memory allowedSigners_ = _getSigners(); // includes owner uint256 allowedSignersLength_ = allowedSigners_.length; for (uint256 i; i < allowedSignersLength_; ) { if (allowedSigners_[i] == signer_) { return true; } unchecked { ++i; } } return false; } /// @inheritdoc IAvocadoMultisigV1Base function signers() public view returns (address[] memory signers_) { return _getSigners(); } /// @inheritdoc IAvocadoMultisigV1Base function requiredSigners() public view returns (uint8) { return _getRequiredSigners(); } /// @inheritdoc IAvocadoMultisigV1Base function signersCount() public view returns (uint8) { return _getSignersCount(); } /// @inheritdoc IAvocadoMultisigV1Base function owner() public view returns (address) { return IAvocado(address(this))._owner(); } /// @inheritdoc IAvocadoMultisigV1Base function index() public view returns (uint32) { return uint32(IAvocado(address(this))._data() >> 160); } /// @notice incrementing nonce for each valid tx executed (to ensure uniqueness) function avoNonce() public view returns (uint256) { return uint256(_avoNonce); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import { ERC1155Holder } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; import { InstaFlashReceiverInterface } from "../external/InstaFlashReceiverInterface.sol"; import { IAvoRegistry } from "../interfaces/IAvoRegistry.sol"; import { IAvoSignersList } from "../interfaces/IAvoSignersList.sol"; import { IAvocadoMultisigV1Base } from "../interfaces/IAvocadoMultisigV1.sol"; import { IAvocadoMultisigV1Secondary } from "../interfaces/IAvocadoMultisigV1Secondary.sol"; import { IAvoConfigV1 } from "../interfaces/IAvoConfigV1.sol"; import { IAvocado } from "../Avocado.sol"; import { AvocadoMultisigErrors } from "./AvocadoMultisigErrors.sol"; import { AvocadoMultisigEvents } from "./AvocadoMultisigEvents.sol"; import { AvocadoMultisigVariables } from "./AvocadoMultisigVariables.sol"; import { AvocadoMultisigInitializable } from "./lib/AvocadoMultisigInitializable.sol"; import { AvocadoMultisigStructs } from "./AvocadoMultisigStructs.sol"; import { AvocadoMultisigProtected } from "./AvocadoMultisig.sol"; /// @dev AvocadoMultisigBase contains all internal helper and base state needed for AvocadoMultisig main AND /// secondary contract logic. abstract contract AvocadoMultisigBase is AvocadoMultisigErrors, AvocadoMultisigEvents, AvocadoMultisigVariables, AvocadoMultisigStructs, AvocadoMultisigInitializable { /***********************************| | CONSTRUCTOR / INITIALIZERS | |__________________________________*/ constructor( IAvoRegistry avoRegistry_, address avoForwarder_, IAvoSignersList avoSignersList_, IAvoConfigV1 avoConfigV1_ ) AvocadoMultisigVariables(avoRegistry_, avoForwarder_, avoSignersList_, avoConfigV1_) { // Ensure logic contract initializer is not abused by disabling initializing // see https://forum.openzeppelin.com/t/security-advisory-initialize-uups-implementation-contracts/15301 // and https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract _disableInitializers(); } /***********************************| | INTERNAL | |__________________________________*/ /// @dev returns the dynamic reserve gas to be kept back for emitting the CastExecuted or CastFailed event function _dynamicReserveGas( uint256 fixedReserveGas_, uint256 signersCount_, uint256 metadataLength_ ) internal pure returns (uint256 reserveGas_) { unchecked { // the gas usage for the emitting the CastExecuted/CastFailed events depends on the signers count // the cost per signer is PER_SIGNER_RESERVE_GAS. We calculate this dynamically to ensure // enough reserve gas is reserved in Multisigs with a higher signersCount. // same for metadata bytes length, dynamically calculated with cost per byte for emit event reserveGas_ = fixedReserveGas_ + (PER_SIGNER_RESERVE_GAS * signersCount_) + (EMIT_EVENT_COST_PER_BYTE * metadataLength_); } } /// @dev Returns the domain separator for the chain with id `DEFAULT_CHAIN_ID` and `salt_` function _domainSeparatorV4(bytes32 salt_) internal view returns (bytes32) { return keccak256( abi.encode( TYPE_HASH, DOMAIN_SEPARATOR_NAME_HASHED, DOMAIN_SEPARATOR_VERSION_HASHED, DEFAULT_CHAIN_ID, address(this), salt_ ) ); } /// @dev returns the EIP712 `CAST_PARAMS_TYPE_HASH` hash for `params_`. function _castParamsHash(CastParams memory params_) internal pure returns (bytes32) { // get keccak256s for actions uint256 actionsLength_ = params_.actions.length; bytes memory actionsAbiEncodePacked_; for (uint256 i; i < actionsLength_; ) { actionsAbiEncodePacked_ = abi.encodePacked( actionsAbiEncodePacked_, keccak256( abi.encode( ACTION_TYPE_HASH, params_.actions[i].target, keccak256(params_.actions[i].data), params_.actions[i].value, params_.actions[i].operation ) ) ); unchecked { ++i; } } return keccak256( abi.encode( CAST_PARAMS_TYPE_HASH, // actions[] keccak256(actionsAbiEncodePacked_), params_.id, params_.avoNonce, params_.salt, params_.source, keccak256(params_.metadata) ) ); } /// @dev returns the EIP712 `CAST_FORWARD_PARAMS_TYPE_HASH` hash for `forwardParams_`. function _castForwardParamsHash(CastForwardParams memory forwardParams_) internal pure returns (bytes32) { return keccak256( abi.encode( CAST_FORWARD_PARAMS_TYPE_HASH, forwardParams_.gas, forwardParams_.gasPrice, forwardParams_.validAfter, forwardParams_.validUntil, forwardParams_.value ) ); } /// @dev returns the EIP712 `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` hash for `params_`, `forwardParams_`, `chainId_`. function _castChainAgnosticParamsHash( CastParams memory params_, CastForwardParams memory forwardParams_, uint256 chainId_ ) internal pure returns (bytes32) { return keccak256( abi.encode( CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH, _castParamsHash(params_), _castForwardParamsHash(forwardParams_), chainId_ ) ); } /// @dev gets the digest (hash) used to verify an EIP712 signature for `chainAgnosticHashes_`. /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` struct for all `CastChainAgnosticParams` /// struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`. /// must be set in the same order as when creating the signature. /// @return bytes32 digest e.g. for signature or non-sequential nonce function _getSigDigestChainAgnostic( ChainAgnosticHash[] memory chainAgnosticHashes_ ) internal view returns (bytes32) { uint256 length_ = chainAgnosticHashes_.length; bytes memory hashesAbiEncodePacked_; bytes memory chainIdsAbiEncodePacked_; for (uint256 i; i < length_; ) { hashesAbiEncodePacked_ = abi.encodePacked(hashesAbiEncodePacked_, chainAgnosticHashes_[i].hash); chainIdsAbiEncodePacked_ = abi.encodePacked(chainIdsAbiEncodePacked_, chainAgnosticHashes_[i].chainId); unchecked { ++i; } } return ECDSA.toTypedDataHash( // domain separator without chain id as salt for chain agnofstic actions (chain id is in signed params instead) _domainSeparatorV4( DOMAIN_SEPARATOR_CHAIN_AGNOSTIC_SALT_HASHED // includes default chain id (634) ), // structHash according to CAST_CHAIN_AGNOSTIC_TYPE_HASH keccak256( abi.encode( CAST_CHAIN_AGNOSTIC_TYPE_HASH, // hash for castChainAgnostic() params[] keccak256(hashesAbiEncodePacked_), // Note: chain ids must be included in this hash here to guarantee input params for the ChainAgnosticHashes // struct array at e.g. castChainAgnostic() are valid. Otherwise, chainIds for the non-current chain could // be passed in wrongly there. keccak256(chainIdsAbiEncodePacked_) ) ) ); } /// @dev gets the digest (hash) used to verify an EIP712 signature for `forwardParams_` /// @param params_ Cast params such as id, avoNonce and actions to execute /// @param forwardParams_ Cast params related to validity of forwarding as instructed and signed /// @return bytes32 digest e.g. for signature or non-sequential nonce function _getSigDigest( CastParams memory params_, CastForwardParams memory forwardParams_ ) internal view returns (bytes32) { return ECDSA.toTypedDataHash( // domain separator _domainSeparatorV4( DOMAIN_SEPARATOR_SALT_HASHED // includes block.chainid ), // structHash according to CAST_TYPE_HASH keccak256(abi.encode(CAST_TYPE_HASH, _castParamsHash(params_), _castForwardParamsHash(forwardParams_))) ); } /// @dev Verifies a EIP712 signature, returning valid status in `isValid_` or reverting /// in case the params for the signatures / digest are wrong /// @param digest_ the EIP712 digest for the signature /// @param signaturesParams_ SignatureParams structs array for signature and signer: /// - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA. /// For smart contract signatures it must fulfill the requirements for the relevant /// smart contract `.isValidSignature()` EIP1271 logic /// - signer: address of the signature signer. /// Must match the actual signature signer or refer to the smart contract /// that must be an allowed signer and validates signature via EIP1271 /// @param isNonSequentialNonce_ flag to signal verify with non sequential nonce or not /// @return isValid_ true if the signature is valid, false otherwise /// @return recoveredSigners_ recovered valid signer addresses of the signatures. In case that `isValid_` is /// false, the last element in the array with a value is the invalid signer function _verifySig( bytes32 digest_, SignatureParams[] memory signaturesParams_, bool isNonSequentialNonce_ ) internal view returns (bool isValid_, address[] memory recoveredSigners_) { // gas measurements: // cost until the for loop in verify signature is: // 1 signer 3374 (_getSigners() with only owner is cheaper) // 2 signers 6473 // every additional allowedSigner (!) + 160 gas (additional SSTORE2 load cost) // For non-sequential nonce additional cold SLOAD + check cost is ~2200 // dynamic cost for verifying any additional signer ~6900 // So formula: // Avoado signersCount == 1 ? -> 11_000 gas // Avoado signersCount > 1 ? -> 6400 + allowedSignersCount * 160 + signersLength * 6900 // is non Sequential nonce? + 2200 // is smart contract signer? + buffer amount. A very basic ECDSA verify call like with e.g. MockSigner costs ~9k. uint256 signaturesLength_ = signaturesParams_.length; if ( // enough signatures must be submitted to reach quorom of `requiredSigners` signaturesLength_ < _getRequiredSigners() || // for non sequential nonce, if nonce is already used, the signature has already been used and is invalid (isNonSequentialNonce_ && nonSequentialNonces[digest_] == 1) ) { revert AvocadoMultisig__InvalidParams(); } // fill recovered signers array for use in event emit recoveredSigners_ = new address[](signaturesLength_); // get current signers from storage address[] memory allowedSigners_ = _getSigners(); // includes owner uint256 allowedSignersLength_ = allowedSigners_.length; // track last allowed signer index for loop performance improvements uint256 lastAllowedSignerIndex_ = 0; bool isContract_ = false; // keeping this variable outside the loop so it is not re-initialized in each loop -> cheaper bool isAllowedSigner_ = false; for (uint256 i; i < signaturesLength_; ) { if (Address.isContract(signaturesParams_[i].signer)) { recoveredSigners_[i] = signaturesParams_[i].signer; // set flag that the signer is a contract so we don't have to check again in code below isContract_ = true; } else { // recover signer from signature recoveredSigners_[i] = ECDSA.recover(digest_, signaturesParams_[i].signature); if (signaturesParams_[i].signer != recoveredSigners_[i]) { // signer does not match recovered signer. Either signer param is wrong or params used to // build digest are not the same as for the signature revert AvocadoMultisig__InvalidParams(); } } // because signers in storage and signers from signatures input params must be ordered ascending, // the for loop can be optimized each new cycle to start from the position where the last signer // has been found. // this also ensures that input params signers must be ordered ascending off-chain // (which again is used to improve performance and simplifies ensuring unique signers) for (uint256 j = lastAllowedSignerIndex_; j < allowedSignersLength_; ) { if (allowedSigners_[j] == recoveredSigners_[i]) { isAllowedSigner_ = true; unchecked { lastAllowedSignerIndex_ = j + 1; // set to j+1 so that next cycle starts at next array position } break; } // could be optimized by checking if allowedSigners_[j] > recoveredSigners_[i] // and immediately skipping with a `break;` if so. Because that implies that the recoveredSigners_[i] // can not be present in allowedSigners_ due to ascending sort. // But that would optimize the failing invalid case and increase cost for the default case where // the input data is valid -> skip. unchecked { ++j; } } // validate if signer is allowed if (!isAllowedSigner_) { return (false, recoveredSigners_); } else { // reset `isAllowedSigner_` for next loop isAllowedSigner_ = false; } if (isContract_) { // validate as smart contract signature if ( IERC1271(signaturesParams_[i].signer).isValidSignature(digest_, signaturesParams_[i].signature) != EIP1271_MAGIC_VALUE ) { // return value is not EIP1271_MAGIC_VALUE -> smart contract returned signature is invalid return (false, recoveredSigners_); } // reset isContract for next loop (because defined outside of the loop to save gas) isContract_ = false; } // else already everything validated through recovered signer must be an allowed signer etc. in logic above unchecked { ++i; } } return (true, recoveredSigners_); } /// @dev Verifies a EIP712 signature, reverting if it is not valid. /// @param digest_ the EIP712 digest for the signature /// @param signaturesParams_ SignatureParams structs array for signature and signer: /// - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA. /// For smart contract signatures it must fulfill the requirements for the relevant /// smart contract `.isValidSignature()` EIP1271 logic /// - signer: address of the signature signer. /// Must match the actual signature signer or refer to the smart contract /// that must be an allowed signer and validates signature via EIP1271 /// @param isNonSequentialNonce_ flag to signal verify with non sequential nonce or not /// @return recoveredSigners_ recovered valid signer addresses of the signatures. In case that `isValid_` is /// false, the last element in the array with a value is the invalid signer function _verifySigWithRevert( bytes32 digest_, SignatureParams[] memory signaturesParams_, bool isNonSequentialNonce_ ) internal view returns (address[] memory recoveredSigners_) { bool validSignature_; (validSignature_, recoveredSigners_) = _verifySig(digest_, signaturesParams_, isNonSequentialNonce_); // signature must be valid if (!validSignature_) { revert AvocadoMultisig__InvalidSignature(); } } } /// @dev AvocadoMultisigCore contains all internal helper and base state needed for AvocadoMultisig.sol main logic abstract contract AvocadoMultisigCore is AvocadoMultisigBase, ERC721Holder, ERC1155Holder, InstaFlashReceiverInterface, IAvocadoMultisigV1Base, IERC1271 { IAvocadoMultisigV1Secondary public immutable avoSecondary; constructor(IAvocadoMultisigV1Secondary secondary_) { if (address(secondary_) == address(0)) { revert AvocadoMultisig__InvalidParams(); } avoSecondary = secondary_; } /// @dev ensures the method can only be called by the same contract itself. modifier onlySelf() { _requireSelfCalled(); _; } /// @dev internal method for modifier logic to reduce bytecode size of contract. function _requireSelfCalled() internal view { if (msg.sender != address(this)) { revert AvocadoMultisig__Unauthorized(); } } /// @dev method used to trigger a delegatecall with `data_` to `target_`. function _spell(address target_, bytes memory data_) internal returns (bytes memory response_) { assembly { let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0) let size := returndatasize() response_ := mload(0x40) mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f)))) mstore(response_, size) returndatacopy(add(response_, 0x20), 0, size) switch iszero(succeeded) case 1 { // throw if delegatecall failed returndatacopy(0x00, 0x00, size) revert(0x00, size) } } } /// @dev executes multiple cast actions according to CastParams `params_`, reserving `reserveGas_` in this contract. /// Uses a sequential nonce unless `nonSequentialNonce_` is set. /// @return success_ boolean flag indicating whether all actions have been executed successfully. /// @return revertReason_ if `success_` is false, then revert reason is returned as string here. function _executeCast( CastParams calldata params_, uint256 reserveGas_, bytes32 nonSequentialNonce_ ) internal returns (bool success_, string memory revertReason_) { // set allowHash to signal allowed entry into _callTargets with actions in current block only _transientAllowHash = bytes31( keccak256(abi.encode(params_.actions, params_.id, block.timestamp, _CALL_TARGETS_SELECTOR)) ); // nonce must be used *always* if signature is valid if (nonSequentialNonce_ == bytes32(0)) { // use sequential nonce, already validated in `_validateParams()` _avoNonce++; } else { // use non-sequential nonce, already validated in `_verifySig()` nonSequentialNonces[nonSequentialNonce_] = 1; } // execute _callTargets via a low-level call to create a separate execution frame // this is used to revert all the actions if one action fails without reverting the whole transaction bytes memory calldata_ = abi.encodeCall(AvocadoMultisigProtected._callTargets, (params_.actions, params_.id)); bytes memory result_; unchecked { if (gasleft() < reserveGas_ + 150) { // catch out of gas issues when available gas does not even cover reserveGas // -> immediately return with out of gas. + 150 to cover sload, sub etc. _resetTransientStorage(); return (false, "AVO__OUT_OF_GAS"); } } // using inline assembly for delegatecall to define custom gas amount that should stay here in caller assembly { success_ := delegatecall( // reserve some gas to make sure we can emit CastFailed event even for out of gas cases // and execute fee paying logic for `castAuthorized()`. // if gasleft() is less than the amount wanted to be sent along, sub would overflow and send all gas // that's why there is the explicit check a few lines up. sub(gas(), reserveGas_), // load _avoImpl from slot 0 and explicitly convert to address with bit mask and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff), add(calldata_, 0x20), mload(calldata_), 0, 0 ) let size := returndatasize() result_ := mload(0x40) mstore(0x40, add(result_, and(add(add(size, 0x20), 0x1f), not(0x1f)))) mstore(result_, size) returndatacopy(add(result_, 0x20), 0, size) } // @dev starting point for measuring reserve gas should be here right after actions execution. // on changes in code after execution (below here or below `_executeCast()` call in calling method), // measure the needed reserve gas via `gasleft()` anew and update `CAST_AUTHORIZED_RESERVE_GAS` // and `CAST_EVENTS_RESERVE_GAS` accordingly. use a method that forces maximum logic execution, // e.g. `castAuthorized()` with failing action. // gas measurement currently: ~1400 gas for logic in this method below if (!success_) { if (result_.length == 0) { if (gasleft() < reserveGas_ - 150) { // catch out of gas errors where not the action ran out of gas but the logic around execution // of the action itself. -150 to cover gas cost until here revertReason_ = "AVO__OUT_OF_GAS"; } else { // @dev this case might be caused by edge-case out of gas errors that we were unable to catch, // but could potentially also have other reasons revertReason_ = "AVO__REASON_NOT_DEFINED"; } } else { assembly { result_ := add(result_, 0x04) } revertReason_ = abi.decode(result_, (string)); } } // reset all transient variables to get the gas refund (4800) _resetTransientStorage(); } /// @dev handles failure of an action execution depending on error cause, /// decoding and reverting with `result_` as reason string. function _handleActionFailure(uint256 actionMinGasLeft_, uint256 i_, bytes memory result_) internal view { if (gasleft() < actionMinGasLeft_) { // action ran out of gas. can not add action index as that again might run out of gas. keep revert minimal revert("AVO__OUT_OF_GAS"); } revert(string.concat(Strings.toString(i_), avoSecondary.getRevertReasonFromReturnedData(result_))); } function _simulateCast( CastParams calldata params_, CastForwardParams calldata forwardParams_, SignatureParams[] memory signaturesParams_, ChainAgnosticHash[] memory chainAgnosticHashes_, bool estimate_ ) internal returns (bool success_, string memory revertReason_) { if ( !(msg.sender == 0x000000000000000000000000000000000000dEaD || (msg.sender == avoForwarder && tx.origin == 0x000000000000000000000000000000000000dEaD)) ) { // sender must be the allowed AvoForwarder and tx origin must be dead address, // or msg.sender must be 0x000000000000000000000000000000000000dEaD directly. revert AvocadoMultisig__Unauthorized(); } (success_, revertReason_) = abi.decode( _spell( address(avoSecondary), abi.encodeCall( avoSecondary.simulateCast, (params_, forwardParams_, signaturesParams_, chainAgnosticHashes_) ) ), (bool, string) ); if (estimate_ && !success_) { // on estimate, revert to get a more accurate gas estimation result. revert(revertReason_); } } /// @dev executes a cast process for `cast()` or `castChainAgnostic()` /// @param params_ Cast params such as id, avoNonce and actions to execute /// @param forwardParams_ Cast params related to validity of forwarding as instructed and signed /// @param signaturesParams_ SignatureParams structs array for signature and signer: /// - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA. /// For smart contract signatures it must fulfill the requirements for the relevant /// smart contract `.isValidSignature()` EIP1271 logic /// - signer: address of the signature signer. /// Must match the actual signature signer or refer to the smart contract /// that must be an allowed signer and validates signature via EIP1271 /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` struct for all `CastChainAgnosticParams` /// struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`. /// must be set in the same order as when creating the signature. /// @return success_ true if all actions were executed succesfully, false otherwise. /// @return revertReason_ revert reason if one of the actions fails in the following format: /// The revert reason will be prefixed with the index of the action. /// e.g. if action 1 fails, then the reason will be "1_reason". /// if an action in the flashloan callback fails (or an otherwise nested action), /// it will be prefixed with with two numbers: "1_2_reason". /// e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails, /// the reason will be 1_2_reason. function _cast( CastParams calldata params_, CastForwardParams calldata forwardParams_, SignatureParams[] memory signaturesParams_, ChainAgnosticHash[] memory chainAgnosticHashes_ ) internal returns (bool success_, string memory revertReason_) { if (msg.sender != avoForwarder) { // sender must be the allowed AvoForwarder revert AvocadoMultisig__Unauthorized(); } unchecked { // compare actual sent gas to user instructed gas, adding 500 to `gasleft()` for approx. already used gas if ((gasleft() + 500) < forwardParams_.gas) { // relayer has not sent enough gas to cover gas limit as user instructed. // this error should not be blamed on the user but rather on the relayer revert AvocadoMultisig__InsufficientGasSent(); } } // @dev gas measurement: uses maximum 685 gas when all params must be validated _validateParams( params_.actions.length, params_.avoNonce, forwardParams_.validAfter, forwardParams_.validUntil, forwardParams_.value ); bytes32 digest_; if (chainAgnosticHashes_.length > 0) { // validate that input `CastParams` and `CastForwardParams` are present in `chainAgnosticHashes_` _validateChainAgnostic( _castChainAgnosticParamsHash(params_, forwardParams_, block.chainid), chainAgnosticHashes_ ); digest_ = _getSigDigestChainAgnostic(chainAgnosticHashes_); } else { digest_ = _getSigDigest(params_, forwardParams_); } address[] memory signers_ = _verifySigWithRevert(digest_, signaturesParams_, params_.avoNonce == -1); (success_, revertReason_) = _executeCast( params_, _dynamicReserveGas(CAST_EVENTS_RESERVE_GAS, signers_.length, params_.metadata.length), params_.avoNonce == -1 ? digest_ : bytes32(0) ); // @dev on changes in the code below this point, measure the needed reserve gas via `gasleft()` anew // and update the reserve gas constant amounts. // gas measurement currently: ~7500 gas for emit event with max revertReason length if (success_) { emit CastExecuted(params_.source, msg.sender, signers_, params_.metadata); } else { emit CastFailed(params_.source, msg.sender, signers_, revertReason_, params_.metadata); } // @dev ending point for measuring reserve gas should be here. Also see comment in `AvocadoMultisigCore._executeCast()` } /// @dev executes `actions_` with respective target, calldata, operation etc. /// IMPORTANT: Validation of `id_` and `_transientAllowHash` is expected to happen in `executeOperation()` and `_callTargets()`. /// catches out of gas errors (as well as possible), reverting with `AVO__OUT_OF_GAS`. /// reverts with action index + error code in case of failure (e.g. "1_SOME_ERROR"). function _executeActions(Action[] memory actions_, uint256 id_, bool isFlashloanCallback_) internal { // reset _transientAllowHash immediately to avert reentrancy etc. & get the gas refund (4800) _resetTransientStorage(); uint256 storageSlot0Snapshot_; // avoImpl, nonce, initialized vars uint256 storageSlot1Snapshot_; // signers related variables // delegate call = ids 1 and 21 bool isDelegateCallId_ = id_ == 1 || id_ == 21; if (isDelegateCallId_) { // store values before execution to make sure core storage vars are not modified by a delegatecall. // this ensures the smart wallet does not end up in a corrupted state. // for mappings etc. it is hard to protect against storage changes, so we must rely on the owner / signer // to know what is being triggered and the effects of a tx assembly { storageSlot0Snapshot_ := sload(0x0) // avoImpl, nonce & initialized vars storageSlot1Snapshot_ := sload(0x1) // signers related variables } } uint256 actionsLength_ = actions_.length; for (uint256 i; i < actionsLength_; ) { Action memory action_ = actions_[i]; // execute action bool success_; bytes memory result_; uint256 actionMinGasLeft_; if (action_.operation == 0 && (id_ < 2 || id_ == 20 || id_ == 21)) { // call (operation = 0 & id = call(0 / 20) or mixed(1 / 21)) unchecked { // store amount of gas that stays with caller, according to EIP150 to detect out of gas errors // -> as close as possible to actual call actionMinGasLeft_ = gasleft() / 64; } // low-level call will return success true also if action target is not even a contract. // we do not explicitly check for this, default interaction is via UI which can check and handle this. // Also applies to delegatecall etc. (success_, result_) = action_.target.call{ value: action_.value }(action_.data); // handle action failure right after external call to better detect out of gas errors if (!success_) { _handleActionFailure(actionMinGasLeft_, i, result_); } } else if (action_.operation == 1 && isDelegateCallId_) { // delegatecall (operation = 1 & id = mixed(1 / 21)) unchecked { // store amount of gas that stays with caller, according to EIP150 to detect out of gas errors // -> as close as possible to actual call actionMinGasLeft_ = gasleft() / 64; } (success_, result_) = action_.target.delegatecall(action_.data); // handle action failure right after external call to better detect out of gas errors if (!success_) { _handleActionFailure(actionMinGasLeft_, i, result_); } // reset _transientAllowHash to make sure it can not be set up in any way for reentrancy _resetTransientStorage(); // for delegatecall, make sure storage was not modified. After every action, to also defend reentrancy uint256 storageSlot0_; uint256 storageSlot1_; assembly { storageSlot0_ := sload(0x0) // avoImpl, nonce & initialized vars storageSlot1_ := sload(0x1) // signers related variables } if (!(storageSlot0_ == storageSlot0Snapshot_ && storageSlot1_ == storageSlot1Snapshot_)) { revert(string.concat(Strings.toString(i), "_AVO__MODIFIED_STORAGE")); } } else if (action_.operation == 2 && (id_ == 20 || id_ == 21)) { // flashloan (operation = 2 & id = flashloan(20 / 21)) if (isFlashloanCallback_) { revert(string.concat(Strings.toString(i), "_AVO__NO_FLASHLOAN_IN_FLASHLOAN")); } // flashloan is always executed via .call, flashloan aggregator uses `msg.sender`, so .delegatecall // wouldn't send funds to this contract but rather to the original sender. bytes memory data_ = action_.data; assembly { data_ := add(data_, 4) // Skip function selector (4 bytes) } // get actions data from calldata action_.data. Only supports InstaFlashAggregatorInterface (, , , data_, ) = abi.decode(data_, (address[], uint256[], uint256, bytes, bytes)); // set allowHash to signal allowed entry into executeOperation() _transientAllowHash = bytes31( keccak256(abi.encode(data_, block.timestamp, EXECUTE_OPERATION_SELECTOR)) ); // store id_ in transient storage slot _transientId = uint8(id_); unchecked { // store amount of gas that stays with caller, according to EIP150 to detect out of gas errors // -> as close as possible to actual call actionMinGasLeft_ = gasleft() / 64; } // handle action failure right after external call to better detect out of gas errors (success_, result_) = action_.target.call{ value: action_.value }(action_.data); if (!success_) { _handleActionFailure(actionMinGasLeft_, i, result_); } // reset _transientAllowHash to prevent reentrancy during actions execution _resetTransientStorage(); } else { // either operation does not exist or the id was not set according to what the action wants to execute revert(string.concat(Strings.toString(i), "_AVO__INVALID_ID_OR_OPERATION")); } unchecked { ++i; } } } /// @dev Validates input params, reverts on invalid values. /// @param actionsLength_ the length of the actions array to execute /// @param avoNonce_ the avoNonce from input CastParams /// @param validAfter_ timestamp after which the request is valid /// @param validUntil_ timestamp before which the request is valid /// @param value_ the msg.value expected to be sent along function _validateParams( uint256 actionsLength_, int256 avoNonce_, uint256 validAfter_, uint256 validUntil_, uint256 value_ ) internal view { // make sure actions are defined and nonce is valid: // must be -1 to use a non-sequential nonce or otherwise it must match the avoNonce if (!(actionsLength_ > 0 && (avoNonce_ == -1 || uint256(avoNonce_) == _avoNonce))) { revert AvocadoMultisig__InvalidParams(); } // make sure request is within valid timeframe if ((validAfter_ > block.timestamp) || (validUntil_ > 0 && validUntil_ < block.timestamp)) { revert AvocadoMultisig__InvalidTiming(); } // make sure msg.value matches `value_` (if set) if (value_ > 0 && msg.value != value_) { revert AvocadoMultisig__InvalidParams(); } } /// @dev Validates input params for `castChainAgnostic`: verifies that the `curCastChainAgnosticHash_` is present in /// the `castChainAgnosticHashes_` array of hashes. Reverts with `AvocadoMultisig__InvalidParams` if not. /// Reverts with `AvocadoMultisig__ChainAgnosticChainMismatch` if the hash is present but valid for another chain. function _validateChainAgnostic( bytes32 curCastChainAgnosticHash_, ChainAgnosticHash[] memory castChainAgnosticHashes_ ) internal view { uint256 length_ = castChainAgnosticHashes_.length; // chain agnostic must be at least 2 hashes if (length_ > 1) { for (uint256 i; i < length_; ) { if ( curCastChainAgnosticHash_ == castChainAgnosticHashes_[i].hash && block.chainid == castChainAgnosticHashes_[i].chainId ) { // hash must be found, and must be for the current chain to be valid return; } unchecked { ++i; } } } // `_castChainAgnosticParamsHash()` of current input params is not present in castChainAgnosticHashes_ -> revert revert AvocadoMultisig__InvalidParams(); } /// @dev gets the digest (hash) used to verify an EIP712 signature for `authorizedParams_` /// @param params_ Cast params such as id, avoNonce and actions to execute /// @param authorizedParams_ Cast params related to execution through owner such as maxFee /// @return bytes32 digest e.g. for signature or non-sequential nonce function _getSigDigestAuthorized( CastParams memory params_, CastAuthorizedParams memory authorizedParams_ ) internal view returns (bytes32) { return ECDSA.toTypedDataHash( // domain separator _domainSeparatorV4( DOMAIN_SEPARATOR_SALT_HASHED // includes block.chainid ), // structHash according to CAST_AUTHORIZED_TYPE_HASH keccak256( abi.encode( CAST_AUTHORIZED_TYPE_HASH, _castParamsHash(params_), // CastAuthorizedParams hash keccak256( abi.encode( CAST_AUTHORIZED_PARAMS_TYPE_HASH, authorizedParams_.maxFee, authorizedParams_.gasPrice, authorizedParams_.validAfter, authorizedParams_.validUntil ) ) ) ) ); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; abstract contract AvocadoMultisigErrors { /// @notice thrown when a method is called with invalid params (e.g. a zero address as input param) error AvocadoMultisig__InvalidParams(); /// @notice thrown when a signature is not valid (e.g. not signed by enough allowed signers) error AvocadoMultisig__InvalidSignature(); /// @notice thrown when someone is trying to execute a in some way auth protected logic error AvocadoMultisig__Unauthorized(); /// @notice thrown when forwarder/relayer does not send enough gas as the user has defined. /// this error should not be blamed on the user but rather on the relayer error AvocadoMultisig__InsufficientGasSent(); /// @notice thrown when a signature has expired or when a request isn't valid yet error AvocadoMultisig__InvalidTiming(); /// @notice thrown when _toHexDigit() fails error AvocadoMultisig__ToHexDigit(); /// @notice thrown when an EIP1271 signature is invalid error AvocadoMultisig__InvalidEIP1271Signature(); /// @notice thrown when a `castAuthorized()` `fee` is bigger than the `maxFee` given through the input param error AvocadoMultisig__MaxFee(uint256 fee, uint256 maxFee); /// @notice thrown when `castAuthorized()` fee can not be covered by available contract funds error AvocadoMultisig__InsufficientBalance(uint256 fee); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; abstract contract AvocadoMultisigEvents { /// @notice Emitted when the implementation is upgraded to a new logic contract event Upgraded(address indexed newImplementation); /// @notice Emitted when a message is marked as allowed smart contract signature event SignedMessage(bytes32 indexed messageHash); /// @notice Emitted when a previously allowed signed message is removed event RemoveSignedMessage(bytes32 indexed messageHash); /// @notice emitted when the avoNonce in storage is increased through an authorized call to /// `occupyAvoNonces()`, which can be used to cancel a previously signed request event AvoNonceOccupied(uint256 indexed occupiedAvoNonce); /// @notice emitted when a non-sequential nonce is occupied in storage through an authorized call to /// `useNonSequentialNonces()`, which can be used to cancel a previously signed request event NonSequentialNonceOccupied(bytes32 indexed occupiedNonSequentialNonce); /// @notice Emitted when a fee is paid through use of the `castAuthorized()` method event FeePaid(uint256 indexed fee); /// @notice Emitted when paying a fee reverts at the recipient event FeePayFailed(uint256 indexed fee); /// @notice emitted when syncing to the AvoSignersList fails event ListSyncFailed(); /// @notice emitted when all actions are executed successfully. /// caller = owner / AvoForwarder address. signers = addresses that triggered this execution event CastExecuted(address indexed source, address indexed caller, address[] signers, bytes metadata); /// @notice emitted if one of the executed actions fails. The reason will be prefixed with the index of the action. /// e.g. if action 1 fails, then the reason will be 1_reason /// if an action in the flashloan callback fails, it will be prefixed with with two numbers: /// e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails, the reason will be 1_2_reason. /// caller = owner / AvoForwarder address. signers = addresses that triggered this execution /// Note If the signature was invalid, the `signers` array last set element is the signer that caused the revert event CastFailed(address indexed source, address indexed caller, address[] signers, string reason, bytes metadata); /// @notice emitted when a signer is added as Multisig signer event SignerAdded(address indexed signer); /// @notice emitted when a signer is removed as Multisig signer event SignerRemoved(address indexed signer); /// @notice emitted when the required signers count is updated event RequiredSignersSet(uint8 indexed requiredSigners); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; interface AvocadoMultisigStructs { /// @notice a combination of a bytes signature and its signer. struct SignatureParams { /// /// @param signature ECDSA signature of `getSigDigest()` for default flow or EIP1271 smart contract signature bytes signature; /// /// @param signer signer of the signature. Can be set to smart contract address that supports EIP1271 address signer; } /// @notice an arbitrary executable action struct Action { /// /// @param target the target address to execute the action on address target; /// /// @param data the calldata to be passed to the call for each target bytes data; /// /// @param value the msg.value to be passed to the call for each target. set to 0 if none uint256 value; /// /// @param operation type of operation to execute: /// 0 -> .call; 1 -> .delegateCall, 2 -> flashloan (via .call) uint256 operation; } /// @notice common params for both `cast()` and `castAuthorized()` struct CastParams { Action[] actions; /// /// @param id Required: /// id for actions, e.g. 0 = CALL, 1 = MIXED (call and delegatecall), /// 20 = FLASHLOAN_CALL, 21 = FLASHLOAN_MIXED uint256 id; /// /// @param avoNonce Required: /// avoNonce to be used for this tx. Must equal the avoNonce value on smart /// wallet or alternatively it must be set to -1 to use a non-sequential nonce instead int256 avoNonce; /// /// @param salt Optional: /// Salt to customize non-sequential nonce (if `avoNonce` is set to -1) bytes32 salt; /// /// @param source Optional: /// Source / referral for this tx address source; /// /// @param metadata Optional: /// metadata for any potential additional data to be tracked in the tx bytes metadata; } /// @notice `cast()` input params related to forwarding validity struct CastForwardParams { /// /// @param gas Optional: /// As EIP-2770: user instructed minimum amount of gas that the relayer (AvoForwarder) /// must send for the execution. Sending less gas will fail the tx at the cost of the relayer. /// Also protects against potential gas griefing attacks /// See https://ronan.eth.limo/blog/ethereum-gas-dangers/ uint256 gas; /// /// @param gasPrice Optional: /// Not implemented / used yet uint256 gasPrice; /// /// @param validAfter Optional: /// the earliest block timestamp that the request can be forwarded in, /// or 0 if the request is not time-limited to occur after a certain time. /// Protects against relayers executing a certain transaction at an earlier moment /// not intended by the user, where it might have a completely different effect. uint256 validAfter; /// /// @param validUntil Optional: /// Similar to EIP-2770: the latest block timestamp (instead of block number) the request /// can be forwarded, or 0 if request should be valid forever. /// Protects against relayers executing a certain transaction at a later moment /// not intended by the user, where it might have a completely different effect. uint256 validUntil; /// /// @param value Optional: /// Not implemented / used yet (`msg.value` amount the broadcaster should send along) uint256 value; } /// @notice `castAuthorized()` input params struct CastAuthorizedParams { /// /// @param maxFee Optional: /// the maximum Avocado charge-up allowed to be paid for tx execution uint256 maxFee; /// /// @param gasPrice Optional: /// Not implemented / used yet uint256 gasPrice; /// /// @param validAfter Optional: /// the earliest block timestamp that the request can be forwarded in, /// or 0 if the request is not time-limited to occur after a certain time. /// Protects against relayers executing a certain transaction at an earlier moment /// not intended by the user, where it might have a completely different effect. uint256 validAfter; /// /// @param validUntil Optional: /// Similar to EIP-2770: the latest block timestamp (instead of block number) the request /// can be forwarded, or 0 if request should be valid forever. /// Protects against relayers executing a certain transaction at a later moment /// not intended by the user, where it might have a completely different effect. uint256 validUntil; } /// @notice params for `castChainAgnostic()` to be used when casting txs on multiple chains with one signature struct CastChainAgnosticParams { /// /// @param params cast params containing actions to be executed etc. CastParams params; /// /// @param forwardParams params related to forwarding validity CastForwardParams forwardParams; /// /// @param chainId chainId where these actions are valid uint256 chainId; } /// @notice unique chain agnostic hash with chain id to be used for chain agnostic interactions struct ChainAgnosticHash { /// /// @param hash EIP712 type `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` hash for one specific `CastChainAgnosticParams` struct bytes32 hash; /// /// @param chainId chainId where this `hash` is for uint256 chainId; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; import { SSTORE2 } from "solmate/src/utils/SSTORE2.sol"; import { IAvoRegistry } from "../interfaces/IAvoRegistry.sol"; import { IAvoSignersList } from "../interfaces/IAvoSignersList.sol"; import { IAvoConfigV1 } from "../interfaces/IAvoConfigV1.sol"; import { IAvocado } from "../Avocado.sol"; import { AvocadoMultisigErrors } from "./AvocadoMultisigErrors.sol"; import { AvocadoMultisigEvents } from "./AvocadoMultisigEvents.sol"; abstract contract AvocadoMultisigConstants is AvocadoMultisigErrors { /************************************| | CONSTANTS | |___________________________________*/ /// @notice overwrite chain id for EIP712 is always set to 634 for the Avocado RPC / network uint256 public constant DEFAULT_CHAIN_ID = 634; // constants for EIP712 values string public constant DOMAIN_SEPARATOR_NAME = "Avocado-Multisig"; string public constant DOMAIN_SEPARATOR_VERSION = "1.1.0"; // hashed EIP712 values bytes32 internal constant DOMAIN_SEPARATOR_NAME_HASHED = keccak256(bytes(DOMAIN_SEPARATOR_NAME)); bytes32 internal constant DOMAIN_SEPARATOR_VERSION_HASHED = keccak256(bytes(DOMAIN_SEPARATOR_VERSION)); /// @notice _TYPE_HASH is copied from OpenZeppelin EIP712 but with added salt as last param (we use it for `block.chainid`) bytes32 public constant TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"); /// @notice EIP712 typehash for `cast()` calls, including structs bytes32 public constant CAST_TYPE_HASH = keccak256( "Cast(CastParams params,CastForwardParams forwardParams)Action(address target,bytes data,uint256 value,uint256 operation)CastForwardParams(uint256 gas,uint256 gasPrice,uint256 validAfter,uint256 validUntil,uint256 value)CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)" ); /// @notice EIP712 typehash for Action struct bytes32 public constant ACTION_TYPE_HASH = keccak256("Action(address target,bytes data,uint256 value,uint256 operation)"); /// @notice EIP712 typehash for CastParams struct bytes32 public constant CAST_PARAMS_TYPE_HASH = keccak256( "CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)Action(address target,bytes data,uint256 value,uint256 operation)" ); /// @notice EIP712 typehash for CastForwardParams struct bytes32 public constant CAST_FORWARD_PARAMS_TYPE_HASH = keccak256( "CastForwardParams(uint256 gas,uint256 gasPrice,uint256 validAfter,uint256 validUntil,uint256 value)" ); /// @notice EIP712 typehash for `castAuthorized()` calls, including structs bytes32 public constant CAST_AUTHORIZED_TYPE_HASH = keccak256( "CastAuthorized(CastParams params,CastAuthorizedParams authorizedParams)Action(address target,bytes data,uint256 value,uint256 operation)CastAuthorizedParams(uint256 maxFee,uint256 gasPrice,uint256 validAfter,uint256 validUntil)CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)" ); /// @notice EIP712 typehash for CastAuthorizedParams struct bytes32 public constant CAST_AUTHORIZED_PARAMS_TYPE_HASH = keccak256("CastAuthorizedParams(uint256 maxFee,uint256 gasPrice,uint256 validAfter,uint256 validUntil)"); /// @notice EIP712 typehash for `castChainAgnostic()` calls, bytes32 hashes array + chainId, made up of `getSigDigest()` for /// action on the respective chain. bytes32 public constant CAST_CHAIN_AGNOSTIC_TYPE_HASH = keccak256( "CastChainAgnostic(CastChainAgnosticParams[] params,uint256[] chainIds)Action(address target,bytes data,uint256 value,uint256 operation)CastChainAgnosticParams(CastParams params,CastForwardParams forwardParams,uint256 chainId)CastForwardParams(uint256 gas,uint256 gasPrice,uint256 validAfter,uint256 validUntil,uint256 value)CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)" ); /// @notice EIP712 typehash for CastChainAgnosticParams struct bytes32 public constant CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH = keccak256( "CastChainAgnosticParams(CastParams params,CastForwardParams forwardParams,uint256 chainId)Action(address target,bytes data,uint256 value,uint256 operation)CastForwardParams(uint256 gas,uint256 gasPrice,uint256 validAfter,uint256 validUntil,uint256 value)CastParams(Action[] actions,uint256 id,int256 avoNonce,bytes32 salt,address source,bytes metadata)" ); /// @notice EIP712 typehash for signed hashes used for EIP1271 (`isValidSignature()`) bytes32 public constant EIP1271_TYPE_HASH = keccak256("AvocadoHash(bytes32 hash)"); /// @notice defines the max signers count for the Multisig. This is chosen deliberately very high, as there shouldn't /// really be a limit on signers count in practice. It is extremely unlikely that anyone runs into this very high /// limit but it helps to implement test coverage within this given limit uint256 public constant MAX_SIGNERS_COUNT = 90; /// @dev "magic value" according to EIP1271 https://eips.ethereum.org/EIPS/eip-1271#specification bytes4 internal constant EIP1271_MAGIC_VALUE = 0x1626ba7e; /// @dev constants for _transientAllowHash functionality: function selectors bytes4 internal constant _CALL_TARGETS_SELECTOR = bytes4(keccak256(bytes("_callTargets()"))); bytes4 internal constant EXECUTE_OPERATION_SELECTOR = bytes4(keccak256(bytes("executeOpeartion()"))); /// @dev amount of gas to keep in castAuthorized caller method as reserve for emitting event + paying fee. /// the dynamic part is covered with PER_SIGNER_RESERVE_GAS. /// use 48_500 as reserve gas for `castAuthorized()`. Usually it will cost less but 48_500 is the maximum amount /// with buffer (~32_000 + 1_400 base) pay fee logic etc. could cost on maximum logic execution. uint256 internal constant CAST_AUTHORIZED_RESERVE_GAS = 48_500; /// @dev amount of gas to keep in cast caller method as reserve for emitting CastFailed / CastExecuted event. /// ~7500 gas + ~1400 gas + buffer. the dynamic part is covered with PER_SIGNER_RESERVE_GAS and EMIT_EVENT_COST_PER_BYTE. uint256 internal constant CAST_EVENTS_RESERVE_GAS = 10_000; /// @dev emitting one byte in an event costs 8 byte see https://github.com/wolflo/evm-opcodes/blob/main/gas.md#a8-log-operations uint256 internal constant EMIT_EVENT_COST_PER_BYTE = 8; /// @dev maximum length of revert reason, longer will be truncated. necessary to reserve enugh gas for event emit uint256 internal constant REVERT_REASON_MAX_LENGTH = 250; /// @dev each additional signer costs ~358 gas to emit in the CastFailed / CastExecuted event. this amount must be /// factored in dynamically depending on the number of signers (PER_SIGNER_RESERVE_GAS * number of signers) uint256 internal constant PER_SIGNER_RESERVE_GAS = 370; /************************************| | IMMUTABLES | |___________________________________*/ // hashed EIP712 value to reduce gas usage bytes32 internal immutable DOMAIN_SEPARATOR_SALT_HASHED = keccak256(abi.encodePacked(block.chainid)); // hashed EIP712 value to reduce gas usage bytes32 internal immutable DOMAIN_SEPARATOR_CHAIN_AGNOSTIC_SALT_HASHED = keccak256(abi.encodePacked(DEFAULT_CHAIN_ID)); /// @notice registry holding the valid versions (addresses) for Avocado smart wallet implementation contracts /// The registry is used to verify a valid version before upgrading & to pay fees for `castAuthorized()` IAvoRegistry public immutable avoRegistry; /// @notice address of the AvoForwarder (proxy) that is allowed to forward tx with valid signatures address public immutable avoForwarder; /// @notice Signers <> Avocados mapping list contract for easy on-chain tracking IAvoSignersList public immutable avoSignersList; // @dev Note that AvocadoMultisig.sol (main) also has an immutable for avoSecondary in AvocadoMultisigCore.sol // backup fee logic /// @dev minimum fee for fee charged via `castAuthorized()` to charge if `AvoRegistry.calcFee()` would fail uint256 public immutable AUTHORIZED_MIN_FEE; /// @dev global maximum for fee charged via `castAuthorized()`. If AvoRegistry returns a fee higher than this, /// then MAX_AUTHORIZED_FEE is charged as fee instead (capping) uint256 public immutable AUTHORIZED_MAX_FEE; /// @dev address that the fee charged via `castAuthorized()` is sent to in the fallback case address payable public immutable AUTHORIZED_FEE_COLLECTOR; /***********************************| | CONSTRUCTOR | |__________________________________*/ constructor( IAvoRegistry avoRegistry_, address avoForwarder_, IAvoSignersList avoSignersList_, IAvoConfigV1 avoConfigV1_ ) { if ( address(avoRegistry_) == address(0) || avoForwarder_ == address(0) || address(avoSignersList_) == address(0) || address(avoConfigV1_) == address(0) ) { revert AvocadoMultisig__InvalidParams(); } avoRegistry = avoRegistry_; avoForwarder = avoForwarder_; avoSignersList = avoSignersList_; // get values from AvoConfigV1 contract IAvoConfigV1.AvocadoMultisigConfig memory avoConfig_ = avoConfigV1_.avocadoMultisigConfig(); // min & max fee settings, fee collector address are required if ( avoConfig_.authorizedMinFee == 0 || avoConfig_.authorizedMaxFee == 0 || avoConfig_.authorizedFeeCollector == address(0) || avoConfig_.authorizedMinFee > avoConfig_.authorizedMaxFee ) { revert AvocadoMultisig__InvalidParams(); } AUTHORIZED_MIN_FEE = avoConfig_.authorizedMinFee; AUTHORIZED_MAX_FEE = avoConfig_.authorizedMaxFee; AUTHORIZED_FEE_COLLECTOR = payable(avoConfig_.authorizedFeeCollector); } } abstract contract AvocadoMultisigVariablesSlot0 { /// @notice address of the smart wallet logic / implementation contract. // @dev IMPORTANT: SAME STORAGE SLOT AS FOR PROXY (`Avocado`). DO NOT MOVE THIS VARIABLE. // _avoImpl MUST ALWAYS be the first declared variable here in the logic contract and in the proxy! // When upgrading, the storage at memory address 0x0 is upgraded (first slot). // Note immutable and constants do not take up storage slots so they can come before. address internal _avoImpl; /// @dev nonce that is incremented for every `cast` / `castAuthorized` transaction (unless it uses a non-sequential nonce) uint80 internal _avoNonce; /// @dev AvocadoMultisigInitializable.sol variables (modified from OpenZeppelin), see ./lib folder /// @dev Indicates that the contract has been initialized. uint8 internal _initialized; /// @dev Indicates that the contract is in the process of being initialized. bool internal _initializing; } abstract contract AvocadoMultisigVariablesSlot1 is AvocadoMultisigConstants, AvocadoMultisigEvents { /// @dev signers are stored with SSTORE2 to save gas, especially for storage checks at delegateCalls. /// getter and setter are implemented below address internal _signersPointer; /// @notice signers count required to reach quorom and be able to execute actions uint8 internal _requiredSigners; /// @notice number of signers currently listed as allowed signers // // @dev should be updated directly via `_setSigners()` in AvocadoMultisigSecondary uint8 internal _signersCount; // 10 bytes empty /***********************************| | SIGNERS GETTERS | |__________________________________*/ /// @dev reads signers from storage with SSTORE2 function _getSigners() internal view returns (address[] memory signers_) { address pointer_ = _signersPointer; if (pointer_ == address(0)) { // signers not set yet -> only owner is signer currently. signers_ = new address[](1); signers_[0] = IAvocado(address(this))._owner(); return signers_; } return abi.decode(SSTORE2.read(pointer_), (address[])); } /// @dev reads required signers (and returns 1 if it is not set) function _getRequiredSigners() internal view returns (uint8 requiredSigners_) { requiredSigners_ = _requiredSigners; if (requiredSigners_ == 0) { requiredSigners_ = 1; } } /// @dev reads signers count (and returns 1 if it is not set) function _getSignersCount() internal view returns (uint8 signersCount_) { signersCount_ = _signersCount; if (signersCount_ == 0) { signersCount_ = 1; } } } abstract contract AvocadoMultisigVariablesSlot2 { /// @dev allow-listed signed messages, e.g. for Permit2 Uniswap interaction. /// mappings are not in sequential storage slot, thus not influenced by previous storage variables /// (but consider the slot number in calculating the hash of the key to store). mapping(bytes32 => uint256) internal _signedMessages; } abstract contract AvocadoMultisigVariablesSlot3 { /// @notice occupied non-sequential nonces (which can not be used again) mapping(bytes32 => uint256) public nonSequentialNonces; } abstract contract AvocadoMultisigSlotGaps { // slots 4 to 53 // create some storage slot gaps for future expansion before the transient storage slot uint256[50] private __gaps; } abstract contract AvocadoMultisigTransient { // slot 54 /// @dev transient allow hash used to signal allowing certain entry into methods such as _callTargets etc. bytes31 internal _transientAllowHash; /// @dev transient id used for passing id to flashloan callback uint8 internal _transientId; } /// @notice Defines storage variables for AvocadoMultisig abstract contract AvocadoMultisigVariables is AvocadoMultisigConstants, AvocadoMultisigVariablesSlot0, AvocadoMultisigVariablesSlot1, AvocadoMultisigVariablesSlot2, AvocadoMultisigVariablesSlot3, AvocadoMultisigSlotGaps, AvocadoMultisigTransient { constructor( IAvoRegistry avoRegistry_, address avoForwarder_, IAvoSignersList avoSignersList_, IAvoConfigV1 avoConfigV1_ ) AvocadoMultisigConstants(avoRegistry_, avoForwarder_, avoSignersList_, avoConfigV1_) {} /// @dev resets transient storage to default value (1). 1 is better than 0 for optimizing gas refunds function _resetTransientStorage() internal { assembly { sstore(54, 1) // Store 1 in the transient storage 54 } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { AvocadoMultisigVariables } from "../AvocadoMultisigVariables.sol"; /// @dev contract copied from OpenZeppelin Initializable but with storage vars moved to AvocadoMultisigVariables.sol /// from OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol) /// see https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.1/contracts/proxy/utils/Initializable.sol /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract AvocadoMultisigInitializable is AvocadoMultisigVariables { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ // uint8 private _initialized; // -> in AvocadoMultisigVariables /** * @dev Indicates that the contract is in the process of being initialized. */ // bool private _initializing; // -> in AvocadoMultisigVariables /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; interface InstaFlashReceiverInterface { function executeOperation( address[] calldata assets, uint256[] calldata amounts, uint256[] calldata premiums, address initiator, bytes calldata _data ) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; import { AvocadoMultisigStructs } from "../AvocadoMultisig/AvocadoMultisigStructs.sol"; // @dev base interface without getters for storage variables (to avoid overloads issues) interface IAvocadoMultisigV1Base is AvocadoMultisigStructs { /// @notice initializer called by AvoFactory after deployment, sets the `owner_` as the only signer function initialize() external; /// @notice returns the domainSeparator for EIP712 signature function domainSeparatorV4() external view returns (bytes32); /// @notice returns the domainSeparator for EIP712 signature for `castChainAgnostic` function domainSeparatorV4ChainAgnostic() external view returns (bytes32); /// @notice gets the digest (hash) used to verify an EIP712 signature for `cast()`. /// /// This is also used as the non-sequential nonce that will be marked as used when the /// request with the matching `params_` and `forwardParams_` is executed via `cast()`. /// @param params_ Cast params such as id, avoNonce and actions to execute /// @param forwardParams_ Cast params related to validity of forwarding as instructed and signed /// @return bytes32 digest to verify signature (or used as non-sequential nonce) function getSigDigest( CastParams calldata params_, CastForwardParams calldata forwardParams_ ) external view returns (bytes32); /// @notice gets the digest (hash) used to verify an EIP712 signature for `castAuthorized()`. /// /// This is also the non-sequential nonce that will be marked as used when the request /// with the matching `params_` and `authorizedParams_` is executed via `castAuthorized()`. /// @param params_ Cast params such as id, avoNonce and actions to execute /// @param authorizedParams_ Cast params related to authorized execution such as maxFee, as signed /// @return bytes32 digest to verify signature (or used as non-sequential nonce) function getSigDigestAuthorized( CastParams calldata params_, CastAuthorizedParams calldata authorizedParams_ ) external view returns (bytes32); /// @notice Verify the signatures for a `cast()' call are valid and can be executed. /// This does not guarantuee that the tx will not revert, simply that the params are valid. /// Does not revert and returns successfully if the input is valid. /// Reverts if input params, signature or avoNonce etc. are invalid. /// @param params_ Cast params such as id, avoNonce and actions to execute /// @param forwardParams_ Cast params related to validity of forwarding as instructed and signed /// @param signaturesParams_ SignatureParams structs array for signature and signer: /// - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA. /// For smart contract signatures it must fulfill the requirements for the relevant /// smart contract `.isValidSignature()` EIP1271 logic /// - signer: address of the signature signer. /// Must match the actual signature signer or refer to the smart contract /// that must be an allowed signer and validates signature via EIP1271 /// @return returns true if everything is valid, otherwise reverts function verify( CastParams calldata params_, CastForwardParams calldata forwardParams_, SignatureParams[] calldata signaturesParams_ ) external view returns (bool); /// @notice Verify the signatures for a `castAuthorized()' call are valid and can be executed. /// This does not guarantuee that the tx will not revert, simply that the params are valid. /// Does not revert and returns successfully if the input is valid. /// Reverts if input params, signature or avoNonce etc. are invalid. /// @param params_ Cast params such as id, avoNonce and actions to execute /// @param authorizedParams_ Cast params related to authorized execution such as maxFee, as signed /// @param signaturesParams_ SignatureParams structs array for signature and signer: /// - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA. /// For smart contract signatures it must fulfill the requirements for the relevant /// smart contract `.isValidSignature()` EIP1271 logic /// - signer: address of the signature signer. /// Must match the actual signature signer or refer to the smart contract /// that must be an allowed signer and validates signature via EIP1271 /// @return returns true if everything is valid, otherwise reverts function verifyAuthorized( CastParams calldata params_, CastAuthorizedParams calldata authorizedParams_, SignatureParams[] calldata signaturesParams_ ) external view returns (bool); /// @notice Executes arbitrary actions with valid signatures. Only executable by AvoForwarder. /// If one action fails, the transaction doesn't revert, instead emits the `CastFailed` event. /// In that case, all previous actions are reverted. /// On success, emits CastExecuted event. /// @dev validates EIP712 signature then executes each action via .call or .delegatecall /// @param params_ Cast params such as id, avoNonce and actions to execute /// @param forwardParams_ Cast params related to validity of forwarding as instructed and signed /// @param signaturesParams_ SignatureParams structs array for signature and signer: /// - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA. /// For smart contract signatures it must fulfill the requirements for the relevant /// smart contract `.isValidSignature()` EIP1271 logic /// - signer: address of the signature signer. /// Must match the actual signature signer or refer to the smart contract /// that must be an allowed signer and validates signature via EIP1271 /// @return success true if all actions were executed succesfully, false otherwise. /// @return revertReason revert reason if one of the actions fails in the following format: /// The revert reason will be prefixed with the index of the action. /// e.g. if action 1 fails, then the reason will be "1_reason". /// if an action in the flashloan callback fails (or an otherwise nested action), /// it will be prefixed with with two numbers: "1_2_reason". /// e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails, /// the reason will be 1_2_reason. function cast( CastParams calldata params_, CastForwardParams calldata forwardParams_, SignatureParams[] calldata signaturesParams_ ) external payable returns (bool success, string memory revertReason); /// @notice Simulates a `cast()` call with exact same params and execution logic except for: /// - any `gasleft()` use removed to remove potential problems when estimating gas. /// - reverts on param validations removed (verify validity with `verify` instead). /// - signature validation is skipped (must be manually added to gas estimations). /// @dev tx.origin must be dead address, msg.sender must be AvoForwarder. /// @dev - set `signaturesParams_` to empty to automatically simulate with required signers length. /// - if `signaturesParams_` first element signature is not set, or if first signer is set to /// 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF, then gas usage burn is simulated /// for verify signature functionality. DO NOT set signature to non-empty for subsequent /// elements then; set all signatures to empty! /// - if `signaturesParams_` is set normally, signatures are verified as in actual execute /// - buffer amounts for mock smart contract signers signature verification must be added /// off-chain as this varies on a case per case basis. function simulateCast( CastParams calldata params_, CastForwardParams calldata forwardParams_, SignatureParams[] memory signaturesParams_ ) external payable returns (bool success_, string memory revertReason_); /// @notice Exact same as `simulateCast`, just reverts in case of `success_` = false to optimize /// for use with .estimateGas(). function estimateCast( CastParams calldata params_, CastForwardParams calldata forwardParams_, SignatureParams[] memory signaturesParams_ ) external payable returns (bool success_, string memory revertReason_); /// @notice Executes arbitrary actions through authorized transaction sent with valid signatures. /// Includes a fee in native network gas token, amount depends on registry `calcFee()`. /// If one action fails, the transaction doesn't revert, instead emits the `CastFailed` event. /// In that case, all previous actions are reverted. /// On success, emits CastExecuted event. /// @dev executes a .call or .delegateCall for every action (depending on params) /// @param params_ Cast params such as id, avoNonce and actions to execute /// @param authorizedParams_ Cast params related to authorized execution such as maxFee, as signed /// @param signaturesParams_ SignatureParams structs array for signature and signer: /// - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA. /// For smart contract signatures it must fulfill the requirements for the relevant /// smart contract `.isValidSignature()` EIP1271 logic /// - signer: address of the signature signer. /// Must match the actual signature signer or refer to the smart contract /// that must be an allowed signer and validates signature via EIP1271 /// @return success true if all actions were executed succesfully, false otherwise. /// @return revertReason revert reason if one of the actions fails in the following format: /// The revert reason will be prefixed with the index of the action. /// e.g. if action 1 fails, then the reason will be "1_reason". /// if an action in the flashloan callback fails (or an otherwise nested action), /// it will be prefixed with with two numbers: "1_2_reason". /// e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails, /// the reason will be 1_2_reason. function castAuthorized( CastParams calldata params_, CastAuthorizedParams calldata authorizedParams_, SignatureParams[] calldata signaturesParams_ ) external payable returns (bool success, string memory revertReason); /// @notice returns the hashes struct for each `CastChainAgnosticParams` element of `params_`. The returned array must be /// passed into `castChainAgnostic()` as the param `chainAgnosticHashes_` there (order must be the same). /// The returned hash for each element is the EIP712 type hash for `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH`, /// as used when the signature digest is built. function getChainAgnosticHashes( CastChainAgnosticParams[] calldata params_ ) external pure returns (ChainAgnosticHash[] memory chainAgnosticHashes_); /// @notice gets the digest (hash) used to verify an EIP712 signature for `castChainAgnostic()`, /// built from the `CastChainAgnosticParams`. /// /// This is also the non-sequential nonce that will be marked as used when the request /// with the matching `params_` is executed via `castChainAgnostic()`. /// @param params_ Cast params such as id, avoNonce and actions to execute /// @return bytes32 digest to verify signature (or used as non-sequential nonce) function getSigDigestChainAgnostic(CastChainAgnosticParams[] calldata params_) external view returns (bytes32); /// @notice gets the digest (hash) used to verify an EIP712 signature for `castChainAgnostic()`, /// built from the chain agnostic hashes (result of `getChainAgnosticHashes()`). /// /// This is also the non-sequential nonce that will be marked as used when the request /// with the matching `params_` is executed via `castChainAgnostic()`. /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` for all `CastChainAgnosticParams` /// struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`. /// must be set in the same order as when creating the signature. /// @return bytes32 digest to verify signature (or used as non-sequential nonce) function getSigDigestChainAgnosticFromHashes( ChainAgnosticHash[] calldata chainAgnosticHashes_ ) external view returns (bytes32); /// @notice Executes arbitrary actions with valid signatures. Only executable by AvoForwarder. /// If one action fails, the transaction doesn't revert, instead emits the `CastFailed` event. /// In that case, all previous actions are reverted. /// On success, emits CastExecuted event. /// @dev validates EIP712 signature then executes each action via .call or .delegatecall /// @param params_ params containing info and intents regarding actions to be executed. Made up of /// same params as for `cast()` plus chain id. /// @param signaturesParams_ SignatureParams structs array for signature and signer: /// - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA. /// For smart contract signatures it must fulfill the requirements for the relevant /// smart contract `.isValidSignature()` EIP1271 logic /// - signer: address of the signature signer. /// Must match the actual signature signer or refer to the smart contract /// that must be an allowed signer and validates signature via EIP1271 /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` for all `CastChainAgnosticParams` /// struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`. /// must be set in the same order as when creating the signature. /// @return success true if all actions were executed succesfully, false otherwise. /// @return revertReason revert reason if one of the actions fails in the following format: /// The revert reason will be prefixed with the index of the action. /// e.g. if action 1 fails, then the reason will be "1_reason". /// if an action in the flashloan callback fails (or an otherwise nested action), /// it will be prefixed with with two numbers: "1_2_reason". /// e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails, /// the reason will be 1_2_reason. function castChainAgnostic( CastChainAgnosticParams calldata params_, SignatureParams[] memory signaturesParams_, ChainAgnosticHash[] calldata chainAgnosticHashes_ ) external payable returns (bool success, string memory revertReason); /// @notice Simulates a `castChainAgnostic()` call with exact same params and execution logic except for: /// - any `gasleft()` use removed to remove potential problems when estimating gas. /// - reverts on param validations removed (verify validity with `verify` instead). /// - signature validation is skipped (must be manually added to gas estimations). /// @dev tx.origin must be dead address, msg.sender must be AvoForwarder. /// @dev - set `signaturesParams_` to empty to automatically simulate with required signers length. /// - if `signaturesParams_` first element signature is not set, or if first signer is set to /// 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF, then gas usage burn is simulated /// for verify signature functionality. DO NOT set signature to non-empty for subsequent /// elements then; set all signatures to empty! /// - if `signaturesParams_` is set normally, signatures are verified as in actual execute /// - buffer amounts for mock smart contract signers signature verification must be added /// off-chain as this varies on a case per case basis. function simulateCastChainAgnostic( CastChainAgnosticParams calldata params_, SignatureParams[] memory signaturesParams_, ChainAgnosticHash[] calldata chainAgnosticHashes_ ) external payable returns (bool success_, string memory revertReason_); /// @notice Exact same as `simulateCastChainAgnostic`, just reverts in case of `success_` = false to /// optimize for use with .estimateGas(). function estimateCastChainAgnostic( CastChainAgnosticParams calldata params_, SignatureParams[] memory signaturesParams_, ChainAgnosticHash[] calldata chainAgnosticHashes_ ) external payable returns (bool success_, string memory revertReason_); /// @notice Verify the signatures for a `castChainAgnostic()' call are valid and can be executed. /// This does not guarantuee that the tx will not revert, simply that the params are valid. /// Does not revert and returns successfully if the input is valid. /// Reverts if input params, signature or avoNonce etc. are invalid. /// @param params_ params containing info and intents regarding actions to be executed. Made up of /// same params as for `cast()` plus chain id. /// @param signaturesParams_ SignatureParams structs array for signature and signer: /// - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA. /// For smart contract signatures it must fulfill the requirements for the relevant /// smart contract `.isValidSignature()` EIP1271 logic /// - signer: address of the signature signer. /// Must match the actual signature signer or refer to the smart contract /// that must be an allowed signer and validates signature via EIP1271 /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` for all `CastChainAgnosticParams` /// struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`. /// must be set in the same order as when creating the signature. /// @return returns true if everything is valid, otherwise reverts function verifyChainAgnostic( CastChainAgnosticParams calldata params_, SignatureParams[] calldata signaturesParams_, ChainAgnosticHash[] calldata chainAgnosticHashes_ ) external view returns (bool); /// @notice checks if an address `signer_` is an allowed signer (returns true if allowed) function isSigner(address signer_) external view returns (bool); /// @notice returns allowed signers on Avocado wich can trigger actions if reaching quorum `requiredSigners`. /// signers automatically include owner. function signers() external view returns (address[] memory signers_); /// @notice returns the number of required signers function requiredSigners() external view returns (uint8); /// @notice returns the number of allowed signers function signersCount() external view returns (uint8); /// @notice Avocado owner function owner() external view returns (address); /// @notice Avocado index (number of Avocado for EOA owner) function index() external view returns (uint32); } // @dev full interface with some getters for storage variables interface IAvocadoMultisigV1 is IAvocadoMultisigV1Base { /// @notice Domain separator name for signatures function DOMAIN_SEPARATOR_NAME() external view returns (string memory); /// @notice Domain separator version for signatures function DOMAIN_SEPARATOR_VERSION() external view returns (string memory); /// @notice incrementing nonce for each valid tx executed (to ensure uniqueness) function avoNonce() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; import { IAvoRegistry } from "./IAvoRegistry.sol"; import { IAvoSignersList } from "./IAvoSignersList.sol"; import { AvocadoMultisigStructs } from "../AvocadoMultisig/AvocadoMultisigStructs.sol"; interface IAvocadoMultisigV1Secondary { /// @notice pays the fee for `castAuthorized()` calls via the AvoRegistry (or fallback) /// @param gasUsedFrom_ `gasleft()` snapshot at gas measurement starting point /// @param maxFee_ maximum acceptable fee to be paid, revert if fee is bigger than this value function payAuthorizedFee(uint256 gasUsedFrom_, uint256 maxFee_) external payable; /// @notice decodes `signature` for EIP1271 into `signaturesParams_` function decodeEIP1271Signature( bytes calldata signature, address owner_ ) external pure returns (AvocadoMultisigStructs.SignatureParams[] memory signaturesParams_); /// @notice Get the revert reason from the returnedData (supports Panic, Error & Custom Errors). /// @param returnedData_ revert data of the call /// @return reason_ revert reason function getRevertReasonFromReturnedData(bytes memory returnedData_) external pure returns (string memory reason_); /// @notice upgrade the contract to a new implementation address. /// - Must be a valid version at the AvoRegistry. /// - Can only be self-called (authorization same as for `cast` methods). /// @param avoImplementation_ New contract address /// @param afterUpgradeHookData_ flexible bytes for custom usage in after upgrade hook logic // // Implementation must call `_afterUpgradeHook()` function upgradeTo(address avoImplementation_, bytes calldata afterUpgradeHookData_) external; /// @notice executes a SIMULATE cast process for `cast()` or `castChainAgnostic()` for gas estimations. /// @param params_ Cast params such as id, avoNonce and actions to execute /// @param forwardParams_ Cast params related to validity of forwarding as instructed and signed /// @param signaturesParams_ SignatureParams structs array for signature and signer: /// - signature: the EIP712 signature, 65 bytes ECDSA signature for a default EOA. /// For smart contract signatures it must fulfill the requirements for the relevant /// smart contract `.isValidSignature()` EIP1271 logic /// - signer: address of the signature signer. /// Must match the actual signature signer or refer to the smart contract /// that must be an allowed signer and validates signature via EIP1271 /// @param chainAgnosticHashes_ EIP712 type hashes of `CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH` struct for all `CastChainAgnosticParams` /// struct array elements as used when creating the signature. Result of `getChainAgnosticHashes()`. /// must be set in the same order as when creating the signature. /// @return success_ true if all actions were executed succesfully, false otherwise. /// @return revertReason_ revert reason if one of the actions fails in the following format: /// The revert reason will be prefixed with the index of the action. /// e.g. if action 1 fails, then the reason will be "1_reason". /// if an action in the flashloan callback fails (or an otherwise nested action), /// it will be prefixed with with two numbers: "1_2_reason". /// e.g. if action 1 is the flashloan, and action 2 of flashloan actions fails, /// the reason will be 1_2_reason. function simulateCast( AvocadoMultisigStructs.CastParams calldata params_, AvocadoMultisigStructs.CastForwardParams calldata forwardParams_, AvocadoMultisigStructs.SignatureParams[] memory signaturesParams_, AvocadoMultisigStructs.ChainAgnosticHash[] memory chainAgnosticHashes_ ) external returns (bool success_, string memory revertReason_); /// @notice SIMULATES: executes `actions_` with respective target, calldata, operation etc. function _simulateExecuteActions( AvocadoMultisigStructs.Action[] memory actions_, uint256 id_, bool isFlashloanCallback_ ) external; } interface IAvocadoMultisigV1SecondaryConstants { function avoRegistry() external view returns (IAvoRegistry); function avoForwarder() external view returns (address); function avoSignersList() external view returns (IAvoSignersList); function AUTHORIZED_MIN_FEE() external view returns (uint256); function AUTHORIZED_MAX_FEE() external view returns (uint256); function AUTHORIZED_FEE_COLLECTOR() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; interface IAvoConfigV1 { struct AvocadoMultisigConfig { /// /// @param authorizedMinFee_ minimum for fee charged via `castAuthorized()` to charge if /// `AvoRegistry.calcFee()` would fail. uint256 authorizedMinFee; /// /// @param authorizedMaxFee_ maximum for fee charged via `castAuthorized()`. If AvoRegistry /// returns a fee higher than this, then `authorizedMaxFee_` is charged as fee instead. uint256 authorizedMaxFee; /// /// @param authorizedFeeCollector_ address that the fee charged via `castAuthorized()` is sent to in the fallback case. address authorizedFeeCollector; } struct AvoDepositManagerConfig { address depositToken; } struct AvoSignersListConfig { bool trackInStorage; } /// @notice config for AvocadoMultisig function avocadoMultisigConfig() external view returns (AvocadoMultisigConfig memory); /// @notice config for AvoDepositManager function avoDepositManagerConfig() external view returns (AvoDepositManagerConfig memory); /// @notice config for AvoSignersList function avoSignersListConfig() external view returns (AvoSignersListConfig memory); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; interface IAvoFeeCollector { /// @notice fee config params used to determine the fee for Avocado smart wallet `castAuthorized()` calls struct FeeConfig { /// /// @param feeCollector address that the fee should be paid to address payable feeCollector; /// /// @param mode current fee mode: 0 = percentage fee (gas cost markup); 1 = static fee (better for L2) uint8 mode; /// /// @param fee current fee amount: /// - for mode percentage: fee in 1e6 percentage (1e8 = 100%, 1e6 = 1%) /// - for static mode: absolute amount in native gas token to charge /// (max value 30_9485_009,821345068724781055 in 1e18) uint88 fee; } /// @notice calculates the `feeAmount_` for an Avocado (`msg.sender`) transaction `gasUsed_` based on /// fee configuration present on the contract /// @param gasUsed_ amount of gas used, required if mode is percentage. not used if mode is static fee. /// @return feeAmount_ calculated fee amount to be paid /// @return feeCollector_ address to send the fee to function calcFee(uint256 gasUsed_) external view returns (uint256 feeAmount_, address payable feeCollector_); } interface IAvoRegistry is IAvoFeeCollector { /// @notice checks if an address is listed as allowed AvoForwarder version, reverts if not. /// @param avoForwarderVersion_ address of the AvoForwarder logic contract to check function requireValidAvoForwarderVersion(address avoForwarderVersion_) external view; /// @notice checks if an address is listed as allowed Avocado version, reverts if not. /// @param avoVersion_ address of the Avocado logic contract to check function requireValidAvoVersion(address avoVersion_) external view; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.18; interface IAvoSignersList { /// @notice adds mappings of `addSigners_` to an Avocado `avocado_`. /// checks the data present at the Avocado to validate input data. /// /// If `trackInStorage` flag is set to false, then only an event will be emitted for off-chain tracking. /// The contract itself will not track avocados per signer on-chain! /// /// Silently ignores `addSigners_` that are already added /// /// There is expectedly no need for this method to be called by anyone other than the Avocado itself. function syncAddAvoSignerMappings(address avocado_, address[] calldata addSigners_) external; /// @notice removes mappings of `removeSigners_` from an Avocado `avocado_`. /// checks the data present at the Avocado to validate input data. /// /// If `trackInStorage` flag is set to false, then only an event will be emitted for off-chain tracking. /// The contract itself will not track avocados per signer on-chain! /// /// Silently ignores `removeSigners_` that are already removed /// /// There is expectedly no need for this method to be called by anyone other than the Avocado itself. function syncRemoveAvoSignerMappings(address avocado_, address[] calldata removeSigners_) external; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Read and write to persistent storage at a fraction of the cost. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol) /// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol) library SSTORE2 { uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called. /*////////////////////////////////////////////////////////////// WRITE LOGIC //////////////////////////////////////////////////////////////*/ function write(bytes memory data) internal returns (address pointer) { // Prefix the bytecode with a STOP opcode to ensure it cannot be called. bytes memory runtimeCode = abi.encodePacked(hex"00", data); bytes memory creationCode = abi.encodePacked( //---------------------------------------------------------------------------------------------------------------// // Opcode | Opcode + Arguments | Description | Stack View // //---------------------------------------------------------------------------------------------------------------// // 0x60 | 0x600B | PUSH1 11 | codeOffset // // 0x59 | 0x59 | MSIZE | 0 codeOffset // // 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset // // 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset // // 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset // // 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset // // 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // // 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // // 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) // // 0xf3 | 0xf3 | RETURN | // //---------------------------------------------------------------------------------------------------------------// hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes. runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit. ); /// @solidity memory-safe-assembly assembly { // Deploy a new contract with the generated creation code. // We start 32 bytes into the code to avoid copying the byte length. pointer := create(0, add(creationCode, 32), mload(creationCode)) } require(pointer != address(0), "DEPLOYMENT_FAILED"); } /*////////////////////////////////////////////////////////////// READ LOGIC //////////////////////////////////////////////////////////////*/ function read(address pointer) internal view returns (bytes memory) { return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET); } function read(address pointer, uint256 start) internal view returns (bytes memory) { start += DATA_OFFSET; return readBytecode(pointer, start, pointer.code.length - start); } function read( address pointer, uint256 start, uint256 end ) internal view returns (bytes memory) { start += DATA_OFFSET; end += DATA_OFFSET; require(pointer.code.length >= end, "OUT_OF_BOUNDS"); return readBytecode(pointer, start, end - start); } /*////////////////////////////////////////////////////////////// INTERNAL HELPER LOGIC //////////////////////////////////////////////////////////////*/ function readBytecode( address pointer, uint256 start, uint256 size ) private view returns (bytes memory data) { /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. data := mload(0x40) // Update the free memory pointer to prevent overriding our data. // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)). // Adding 31 to size and running the result through the logic above ensures // the memory pointer remains word-aligned, following the Solidity convention. mstore(0x40, add(data, and(add(add(size, 32), 31), not(31)))) // Store the size of the data in the first 32 byte chunk of free memory. mstore(data, size) // Copy the code into memory right after the 32 bytes we used to store the size. extcodecopy(pointer, add(data, 32), start, size) } } }
{ "optimizer": { "enabled": true, "runs": 10000 }, "libraries": {}, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract IAvoRegistry","name":"avoRegistry_","type":"address"},{"internalType":"address","name":"avoForwarder_","type":"address"},{"internalType":"contract IAvoSignersList","name":"avoSignersList_","type":"address"},{"internalType":"contract IAvoConfigV1","name":"avoConfigV1_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"AvocadoMultisig__InsufficientBalance","type":"error"},{"inputs":[],"name":"AvocadoMultisig__InsufficientGasSent","type":"error"},{"inputs":[],"name":"AvocadoMultisig__InvalidEIP1271Signature","type":"error"},{"inputs":[],"name":"AvocadoMultisig__InvalidParams","type":"error"},{"inputs":[],"name":"AvocadoMultisig__InvalidSignature","type":"error"},{"inputs":[],"name":"AvocadoMultisig__InvalidTiming","type":"error"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"maxFee","type":"uint256"}],"name":"AvocadoMultisig__MaxFee","type":"error"},{"inputs":[],"name":"AvocadoMultisig__ToHexDigit","type":"error"},{"inputs":[],"name":"AvocadoMultisig__Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"occupiedAvoNonce","type":"uint256"}],"name":"AvoNonceOccupied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"source","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address[]","name":"signers","type":"address[]"},{"indexed":false,"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"CastExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"source","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address[]","name":"signers","type":"address[]"},{"indexed":false,"internalType":"string","name":"reason","type":"string"},{"indexed":false,"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"CastFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FeePaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FeePayFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[],"name":"ListSyncFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"occupiedNonSequentialNonce","type":"bytes32"}],"name":"NonSequentialNonceOccupied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"name":"RemoveSignedMessage","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"requiredSigners","type":"uint8"}],"name":"RequiredSignersSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"name":"SignedMessage","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"signer","type":"address"}],"name":"SignerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"signer","type":"address"}],"name":"SignerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"ACTION_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"AUTHORIZED_FEE_COLLECTOR","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"AUTHORIZED_MAX_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"AUTHORIZED_MIN_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_AUTHORIZED_PARAMS_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_AUTHORIZED_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_CHAIN_AGNOSTIC_PARAMS_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_CHAIN_AGNOSTIC_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_FORWARD_PARAMS_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_PARAMS_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CAST_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_CHAIN_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR_NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP1271_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SIGNERS_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"operation","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.Action[]","name":"actions_","type":"tuple[]"},{"internalType":"uint256","name":"id_","type":"uint256"},{"internalType":"bool","name":"isFlashloanCallback_","type":"bool"}],"name":"_simulateExecuteActions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addSigners_","type":"address[]"},{"internalType":"uint8","name":"requiredSigners_","type":"uint8"}],"name":"addSigners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"avoForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"avoRegistry","outputs":[{"internalType":"contract IAvoRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"avoSignersList","outputs":[{"internalType":"contract IAvoSignersList","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"owner_","type":"address"}],"name":"decodeEIP1271Signature","outputs":[{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"signer","type":"address"}],"internalType":"struct AvocadoMultisigStructs.SignatureParams[]","name":"signaturesParams_","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"returnedData_","type":"bytes"}],"name":"getRevertReasonFromReturnedData","outputs":[{"internalType":"string","name":"reason_","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"nonSequentialNonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint88[]","name":"avoNonces_","type":"uint88[]"}],"name":"occupyAvoNonces","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"nonSequentialNonces_","type":"bytes32[]"}],"name":"occupyNonSequentialNonces","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gasUsedFrom_","type":"uint256"},{"internalType":"uint256","name":"maxFee_","type":"uint256"}],"name":"payAuthorizedFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"message_","type":"bytes32"}],"name":"removeSignedMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"removeSigners_","type":"address[]"},{"internalType":"uint8","name":"requiredSigners_","type":"uint8"}],"name":"removeSigners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"requiredSigners_","type":"uint8"}],"name":"setRequiredSigners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"message_","type":"bytes32"}],"name":"signMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"operation","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.Action[]","name":"actions","type":"tuple[]"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"int256","name":"avoNonce","type":"int256"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"source","type":"address"},{"internalType":"bytes","name":"metadata","type":"bytes"}],"internalType":"struct AvocadoMultisigStructs.CastParams","name":"params_","type":"tuple"},{"components":[{"internalType":"uint256","name":"gas","type":"uint256"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"validAfter","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.CastForwardParams","name":"forwardParams_","type":"tuple"},{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"signer","type":"address"}],"internalType":"struct AvocadoMultisigStructs.SignatureParams[]","name":"signaturesParams_","type":"tuple[]"},{"components":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"internalType":"struct AvocadoMultisigStructs.ChainAgnosticHash[]","name":"chainAgnosticHashes_","type":"tuple[]"}],"name":"simulateCast","outputs":[{"internalType":"bool","name":"success_","type":"bool"},{"internalType":"string","name":"revertReason_","type":"string"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"avoImplementation_","type":"address"},{"internalType":"bytes","name":"afterUpgradeHookData_","type":"bytes"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
466101c090815260206101a08190529081902060805261027a610200526101e0526102206040527f56de5bbd3725e9c80073ed00b9767413d9ddf7d1ec1d602a8f924f15a9c7568560a0523480156200005757600080fd5b5060405162005ff638038062005ff68339810160408190526200007a91620002f5565b8383838383838383838383836001600160a01b0384161580620000a457506001600160a01b038316155b80620000b757506001600160a01b038216155b80620000ca57506001600160a01b038116155b15620000e95760405163ec92598560e01b815260040160405180910390fd5b6001600160a01b0380851660c05283811660e0528281166101005260408051632271f8db60e01b81529051600092841691632271f8db9160048083019260609291908290030181865afa15801562000145573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200016b91906200035d565b805190915015806200017f57506020810151155b8062000196575060408101516001600160a01b0316155b80620001a6575060208101518151115b15620001c55760405163ec92598560e01b815260040160405180910390fd5b805161012052602081015161014052604001516001600160a01b03166101605250620001f99650506200020d945050505050565b5050306101805250620003d0945050505050565b600054600160f81b900460ff16156200027c5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff600160f01b90910481161015620002da576000805460ff60f01b191660ff60f01b17905560405160ff81527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6001600160a01b0381168114620002f257600080fd5b50565b600080600080608085870312156200030c57600080fd5b84516200031981620002dc565b60208601519094506200032c81620002dc565b60408601519093506200033f81620002dc565b60608601519092506200035281620002dc565b939692955090935050565b6000606082840312156200037057600080fd5b604051606081016001600160401b0381118282101715620003a157634e487b7160e01b600052604160045260246000fd5b806040525082518152602083015160208201526040830151620003c481620002dc565b60408201529392505050565b60805160a05160c05160e0516101005161012051610140516101605161018051615b6f620004876000396000612a890152600081816107420152611f4401526000818161083e01528181611ef30152611f1b0152600081816107960152611f6701526000818161026d0152818161188801528181611ca10152612532015260006105790152600081816105c0015281816119ff0152611dc501526000612de501526000818161212c0152612cc90152615b6f6000f3fe6080604052600436106102345760003560e01c806387265c9511610138578063b4907ddc116100b0578063ec5904831161007f578063ed969f0a11610064578063ed969f0a146107d8578063f7b14fac1461080c578063fe6bfed31461082c57600080fd5b8063ec59048314610784578063ec80dcc2146107b857600080fd5b8063b4907ddc146106b3578063bb1c006b146106fc578063c7fe944f14610730578063eb2856e91461076457600080fd5b80639ee3f88311610107578063a0d06193116100ec578063a0d0619314610617578063a755ecfc1461064b578063a80ebaf11461067f57600080fd5b80639ee3f883146105e2578063a08519b5146105f757600080fd5b806387265c951461051e57806394f0320e14610567578063984cee411461059b5780639cae7aa8146105ae57600080fd5b806343ea1996116101cb5780635b502b401161019a57806364d4c8191161017f57806364d4c819146104b55780636fbc15e9146104e95780638129fc1c1461050957600080fd5b80635b502b40146104615780636043848a1461049557600080fd5b806343ea1996146103c3578063447f537f146103d957806349926a661461040d5780634cf5de191461042d57600080fd5b80632b537875116102075780632b537875146103145780633a06cc8c146103415780633e0cd897146103755780633edd9187146103a357600080fd5b8063044d3cd21461023957806316914ac01461025b5780631bbce23d146102ac5780632a5d1d66146102d9575b600080fd5b34801561024557600080fd5b506102596102543660046145d3565b610860565b005b34801561026757600080fd5b5061028f7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102b857600080fd5b506102cc6102c736600461467c565b6109f7565b6040516102a39190614723565b3480156102e557600080fd5b506103066102f43660046147c0565b60036020526000908152604090205481565b6040519081526020016102a3565b34801561032057600080fd5b5061033461032f366004614926565b610d24565b6040516102a3919061495b565b34801561034d57600080fd5b506103067f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e81565b34801561038157600080fd5b50610395610390366004614a6c565b611158565b6040516102a3929190614b97565b3480156103af57600080fd5b506102596103be3660046145d3565b6114dd565b3480156103cf57600080fd5b5061030661027a81565b3480156103e557600080fd5b506103067f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2081565b34801561041957600080fd5b506102596104283660046147c0565b611575565b34801561043957600080fd5b506103067fc84a2c176321157bd55b70feb5871c4304d99c870656d2fc420998eff645e20781565b34801561046d57600080fd5b506103067f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c081565b3480156104a157600080fd5b506102596104b0366004614bc3565b6115b8565b3480156104c157600080fd5b506103067fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647281565b3480156104f557600080fd5b50610259610504366004614c17565b6119ab565b34801561051557600080fd5b50610259611b38565b34801561052a57600080fd5b506103346040518060400160405280601081526020017f41766f6361646f2d4d756c74697369670000000000000000000000000000000081525081565b34801561057357600080fd5b5061028f7f000000000000000000000000000000000000000000000000000000000000000081565b6102596105a9366004614c6c565b611dae565b3480156105ba57600080fd5b5061028f7f000000000000000000000000000000000000000000000000000000000000000081565b3480156105ee57600080fd5b50610306605a81565b34801561060357600080fd5b506102596106123660046147c0565b61211c565b34801561062357600080fd5b506103067fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b81565b34801561065757600080fd5b506103067fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4781565b34801561068b57600080fd5b506103067f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6581565b3480156106bf57600080fd5b506103346040518060400160405280600581526020017f312e312e3000000000000000000000000000000000000000000000000000000081525081565b34801561070857600080fd5b506103067fb7c77dbcd01eff35f637803daf7abe3f0e3b86b5102459be5665dc7d9a85ac5e81565b34801561073c57600080fd5b5061028f7f000000000000000000000000000000000000000000000000000000000000000081565b34801561077057600080fd5b5061025961077f366004614bc3565b61222b565b34801561079057600080fd5b506103067f000000000000000000000000000000000000000000000000000000000000000081565b3480156107c457600080fd5b506102596107d3366004614c8e565b612654565b3480156107e457600080fd5b506103067f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb81565b34801561081857600080fd5b50610259610827366004614d8b565b612668565b34801561083857600080fd5b506103067f000000000000000000000000000000000000000000000000000000000000000081565b610868612a86565b80600081900361087757505050565b60058111156108995760405163ec92598560e01b815260040160405180910390fd5b6000805474010000000000000000000000000000000000000000900469ffffffffffffffffffff16905b8281101561099e57818585838181106108de576108de614de0565b90506020020160208101906108f39190614e0f565b6affffffffffffffffffffff16036109425760405182907f5e8592bbef3f468eddd77c711ee57100f76bbb79500d8871a700fb6108929d9290600090a28161093a81614e6b565b925050610996565b8185858381811061095557610955614de0565b905060200201602081019061096a9190614e0f565b6affffffffffffffffffffff1611156109965760405163ec92598560e01b815260040160405180910390fd5b6001016108c3565b506000805469ffffffffffffffffffff90921674010000000000000000000000000000000000000000027fffff00000000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055505050565b6060827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf8101610ad05760408051600180825281830190925290816020015b604080518082019091526060815260006020820152815260200190600190039081610a36579050506040805160606020601f890181900402820181018352918101878152929450918291889088908190850183828082843760009201829052509385525050506001600160a01b0386166020909201919091528351849190610ac057610ac0614de0565b6020026020010181905250610d1b565b605a811015610b0b576040517fa8398eb900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fdec0de6520000000000000000000000000000000000000000000000000000000610b3a600560008789614e85565b610b4391614eaf565b7fffffffffffffffff0000000000000000000000000000000000000000000000001603610d0c5760557ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8201048067ffffffffffffffff811115610ba957610ba96147d9565b604051908082528060200260200182016040528015610bef57816020015b604080518082019091526060815260006020820152815260200190600190039081610bc75790505b50925060005b81811015610d0557605581026046810190600090610c1890605a01838a8c614e85565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050505060208101516040805180820190915291925060601c9080610c91857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf81018d8f614e85565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506001600160a01b0383166020909101528751889086908110610cec57610cec614de0565b6020026020010181905250836001019350505050610bf5565b5050610d1b565b610d1884860186614f26565b91505b505b9392505050565b6060600482511015610d6957505060408051808201909152601381527f5f524541534f4e5f4e4f545f444546494e454400000000000000000000000000602082015290565b60208201517fb1b7848f000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821601610df557600483019250610dcf83610dca90614f5b565b612aea565b604051602001610ddf9190614f7f565b6040516020818303038152906040529150611078565b7ff73c8660000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821601610e685760048301925082806020019051810190610e589190614ff4565b604051602001610ddf919061503d565b610e7560e082901c612aea565b604051602001610e859190615082565b6040516020818303038152906040529150600060048451610ea691906150c7565b9050610eb460fa60086150da565b610ebf8260026150ed565b1115610ed457610ed160fa60046150da565b90505b6000610ee18260026150ed565b67ffffffffffffffff811115610ef957610ef96147d9565b6040519080825280601f01601f191660200182016040528015610f23576020820181803683370190505b50905060005b8281101561102e57610f666010878360040181518110610f4b57610f4b614de0565b016020015160f81c81610f6057610f60614ef7565b04612b07565b828260020281518110610f7b57610f7b614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610fdf6010878360040181518110610fc457610fc4614de0565b016020015160f81c81610fd957610fd9614ef7565b06612b07565b828260020260010181518110610ff757610ff7614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101610f29565b50836040516020016110409190615104565b60405160208183030381529060405293508381604051602001611064929190615145565b604051602081830303815290604052935050505b60fa825111156111525781600061109160fa60016150da565b905060008167ffffffffffffffff8111156110ae576110ae6147d9565b6040519080825280601f01601f1916602001820160405280156110d8576020820181803683370190505b50905060005b8281101561114c578381815181106110f8576110f8614de0565b602001015160f81c60f81b82828151811061111557611115614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506001016110de565b50935050505b50919050565b60006060611164612a86565b61118c6111718780615174565b90508760400135876040013588606001358960800135612b67565b8251156111bd576111bd6111b76111a2886151dc565b6111b136899003890189615278565b46612bd9565b84612c4e565b6000808451116111ec576111e76111d3886151dc565b6111e236899003890189615278565b612cbf565b6111f5565b6111f584612d3f565b905060008086511161121157611209612e62565b60ff16611214565b85515b67ffffffffffffffff81111561122c5761122c6147d9565b604051908082528060200260200182016040528015611255578160200160208202803683370190505b50905085516000148061128757508560008151811061127657611276614de0565b602002602001015160000151516000145b806112c15750856000815181106112a0576112a0614de0565b6020026020010151602001516001600160a01b03166001600160a01b038016145b1561139f5760006112d0612e90565b60ff1690506000816001146112e757612af86112eb565b6121345b61ffff16905060018351111561132d57825161130990611af46150ed565b6113148360a06150ed565b611320906119006150da565b61132a91906150da565b90505b60408a01351961134657611343610898826150da565b90505b875160000361136a57825161135d90610ce46150ed565b61136790826150da565b90505b60005a905060005b825a61137e90846150c7565b1015611396578061138e81614e6b565b915050611372565b505050506113b5565b6113b282878a6040013560001914612ebe565b90505b6113ff886113e561271084518c8060a001906113d191906152e8565b600802610172929092029290920101919050565b8a60400135600019146113f9576000612f08565b84612f08565b9094509250831561146f573361141b60a08a0160808b0161534d565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b8361145360a08d018d6152e8565b604051611462939291906153d9565b60405180910390a36114d2565b3361148060a08a0160808b0161534d565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f83866114b960a08e018e6152e8565b6040516114c994939291906153ff565b60405180910390a35b505094509492505050565b6114e5612a86565b8060005b8181101561156f5760016003600086868581811061150957611509614de0565b9050602002013581526020019081526020016000208190555083838281811061153457611534614de0565b905060200201357f3749b30151183a20a5e6b93dce343f6010bde0e057441cca9548231f5727b70360405160405180910390a26001016114e9565b50505050565b61157d612a86565b6000818152600260205260408082208290555182917fc93ef95774a42ae5f9ca1e82cc762d17a6c2424041b91bf34dcf23aacc28cf1291a250565b6115c0612a86565b8160008190036115e35760405163ec92598560e01b815260040160405180910390fd5b60006115ed61317e565b805190915060006115fe84836150c7565b90506000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611640573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116649190615444565b905060008267ffffffffffffffff811115611681576116816147d9565b6040519080825280602002602001820160405280156116aa578160200160208202803683370190505b50905060008060005b868110156118595781810392508882148061171f57508b8b838181106116db576116db614de0565b90506020020160208101906116f0919061534d565b6001600160a01b031688828151811061170b5761170b614de0565b60200260200101516001600160a01b031614155b15611796578583101561177d5787818151811061173e5761173e614de0565b602002602001015184848151811061175857611758614de0565b60200260200101906001600160a01b031690816001600160a01b031681525050611851565b60405163ec92598560e01b815260040160405180910390fd5b846001600160a01b03168c8c848181106117b2576117b2614de0565b90506020020160208101906117c7919061534d565b6001600160a01b0316036117ee5760405163ec92598560e01b815260040160405180910390fd5b8b8b8381811061180057611800614de0565b9050602002016020810190611815919061534d565b6001600160a01b03167f3525e22824a8a7df2c9a6029941c824cf95b6447f1e13d5128fd3826d35afe8b60405160405180910390a28160010191505b6001016116b3565b5087811461187a5760405163ec92598560e01b815260040160405180910390fd5b611884838a61326d565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316308d8d6040516024016118c593929190615461565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa1153e7f000000000000000000000000000000000000000000000000000000001790525161192891906154bd565b6000604051808303816000865af19150503d8060008114611965576040519150601f19603f3d011682016040523d82523d6000602084013e61196a565b606091505b505090508061199d576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b505050505050505050505050565b6119b3612a86565b6000546001600160a01b03848116911614611b33576040517efe90e30000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301527f0000000000000000000000000000000000000000000000000000000000000000169062fe90e39060240160006040518083038186803b158015611a4057600080fd5b505afa158015611a54573d6000803e3d6000fd5b5050600080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b0388811691821784556040519216945092507fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b9190a2611b3084828585604051602401611ad3939291906154d9565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fe7e38cd3000000000000000000000000000000000000000000000000000000001790526133d1565b50505b505050565b611b40612a86565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba49190615444565b90506001600160a01b0381163b151580611bc557506001600160a01b038116155b15611be35760405163ec92598560e01b815260040160405180910390fd5b611bed6001603655565b6040516001600160a01b038216907f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2490600090a26040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a2604080516001808252818301909252600091602080830190803683370190505090508181600081518110611c8357611c83614de0565b6001600160a01b0392831660209182029290920101526040516000917f00000000000000000000000000000000000000000000000000000000000000001690611cd290309085906024016154fc565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f863deb110000000000000000000000000000000000000000000000000000000017905251611d3591906154bd565b6000604051808303816000865af19150503d8060008114611d72576040519150601f19603f3d011682016040523d82523d6000602084013e611d77565b606091505b5050905080611b33576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a1505050565b611db6612a86565b60008060005a850390506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316613a9884604051602401611e0391815260200190565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f75dc7d8c0000000000000000000000000000000000000000000000000000000017905251611e6691906154bd565b6000604051808303818686fa925050503d8060008114611ea2576040519150601f19603f3d011682016040523d82523d6000602084013e611ea7565b606091505b5060408101519193509150828015611ec05750603f8251115b8015611ed357506001600160a01b038111155b15611f425781806020019051810190611eec919061551e565b90965094507f0000000000000000000000000000000000000000000000000000000000000000861115611f3d577f000000000000000000000000000000000000000000000000000000000000000095505b611f89565b7f000000000000000000000000000000000000000000000000000000000000000094507f000000000000000000000000000000000000000000000000000000000000000095505b5050505060008211156120eb57600083118015611fa557508282115b15611feb576040517f360ceed500000000000000000000000000000000000000000000000000000000815260048101839052602481018490526044015b60405180910390fd5b81471015612028576040517ffae5708700000000000000000000000000000000000000000000000000000000815260048101839052602401611fe2565b6000816001600160a01b0316836103e890604051600060405180830381858888f193505050503d806000811461207a576040519150601f19603f3d011682016040523d82523d6000602084013e61207f565b606091505b5050905080156120b95760405183907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a26120e5565b60405183907fd1b3877a34a9595e45447dad16b7354ec5a8bece40075fd60d647e76397b476490600090a25b5061156f565b60405182907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a250505050565b612124612a86565b6121eb6121507f00000000000000000000000000000000000000000000000000000000000000006133f6565b604080517f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2060208201529081018490526060015b60408051601f1981840301815282825280516020918201207f19010000000000000000000000000000000000000000000000000000000000008483015260228401949094526042808401949094528151808403909401845260629092019052815191012090565b600081815260026020526040808220600190555191925082917fbef281d0d1046788f9baa37e73d57e85933f130d8ec1e223366e0b04fbf254029190a250565b612233612a86565b8180158061226e575060008484828161224e5761224e614de0565b9050602002016020810190612263919061534d565b6001600160a01b0316145b1561228c5760405163ec92598560e01b815260040160405180910390fd5b600061229661317e565b805190915060006122a784836150da565b9050605a8111156122cb5760405163ec92598560e01b815260040160405180910390fd5b60008167ffffffffffffffff8111156122e6576122e66147d9565b60405190808252806020026020018201604052801561230f578160200160208202803683370190505b50905060008060005b848110156125235781810392508782148061238d5750858310801561238d57508a8a8381811061234a5761234a614de0565b905060200201602081019061235f919061534d565b6001600160a01b031687848151811061237a5761237a614de0565b60200260200101516001600160a01b0316105b156123e3578683815181106123a4576123a4614de0565b60200260200101518482815181106123be576123be614de0565b60200260200101906001600160a01b031690816001600160a01b03168152505061249f565b8a8a838181106123f5576123f5614de0565b905060200201602081019061240a919061534d565b84828151811061241c5761241c614de0565b60200260200101906001600160a01b031690816001600160a01b0316815250508a8a8381811061244e5761244e614de0565b9050602002016020810190612463919061534d565b6001600160a01b03167f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2460405160405180910390a28160010191505b6000811180156124fd5750836124b66001836150c7565b815181106124c6576124c6614de0565b60200260200101516001600160a01b03168482815181106124e9576124e9614de0565b60200260200101516001600160a01b031611155b1561251b5760405163ec92598560e01b815260040160405180910390fd5b600101612318565b5061252e838961326d565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316308c8c60405160240161256f93929190615461565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f863deb1100000000000000000000000000000000000000000000000000000000179052516125d291906154bd565b6000604051808303816000865af19150503d806000811461260f576040519150601f19603f3d011682016040523d82523d6000602084013e612614565b606091505b5050905080612647576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b5050505050505050505050565b61265c612a86565b6126658161350b565b50565b612670612a86565b61267a6001603655565b6000806000846001148061268e5750846015145b905080156126a157600054925060015491505b855160005b81811015612a7c5760008882815181106126c2576126c2614de0565b60200260200101519050600060606000836060015160001480156126fb575060028b10806126f057508a6014145b806126fb57508a6015145b156127825783600001516001600160a01b03168460400151856020015160405161272591906154bd565b60006040518083038185875af1925050503d8060008114612762576040519150601f19603f3d011682016040523d82523d6000602084013e612767565b606091505b5090935091508261277d5761277d8186846135e2565b612a6d565b836060015160011480156127935750865b156128905783600001516001600160a01b031684602001516040516127b891906154bd565b600060405180830381855af49150503d80600081146127f3576040519150601f19603f3d011682016040523d82523d6000602084013e6127f8565b606091505b5090935091508261280e5761280e8186846135e2565b6128186001603655565b6000546001548a8214801561282c57508981145b6128895761283987613605565b604051602001612849919061554e565b60408051601f19818403018152908290527f08c379a0000000000000000000000000000000000000000000000000000000008252611fe29160040161495b565b5050612a6d565b836060015160021480156128ae57508a601414806128ae57508a6015145b15612a545789156128d2576128c285613605565b604051602001612849919061558f565b602084015160048101805190916128f191810160249081019101615654565b50604080518082018252601281527f657865637574654f7065617274696f6e28290000000000000000000000000000602091820152905191955061295e94508593504292507f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e9101615760565b60405160208183030381529060405280519060200120603660006101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff021916908360081c02179055508b6036601f6101000a81548160ff021916908360ff16021790555084600001516001600160a01b0316856040015186602001516040516129ec91906154bd565b60006040518083038185875af1925050503d8060008114612a29576040519150601f19603f3d011682016040523d82523d6000602084013e612a2e565b606091505b50909450925083612a4457612a448287856135e2565b612a4e6001603655565b50612a6d565b612a5d85613605565b60405160200161284991906157ab565b846001019450505050506126a6565b5050505050505050565b307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031603612ae8576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6060612b0182612af9846136a5565b60010161370f565b92915050565b6000600a8260ff161015612b1f575060300160f81b90565b60108260ff161015612b35575060570160f81b90565b6040517f9778fcab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085118015612ba75750836000191480612ba7575060005474010000000000000000000000000000000000000000900469ffffffffffffffffffff1684145b5042831180612bc05750600082118015612bc057504282105b50600081118015612bd15750803414155b505050505050565b60007fc84a2c176321157bd55b70feb5871c4304d99c870656d2fc420998eff645e207612c0585613952565b612c0e85613b44565b60408051602081019490945283019190915260608201526080810183905260a0016040516020818303038152906040528051906020012090509392505050565b80516001811115611b335760005b8181101561156f57828181518110612c7657612c76614de0565b60200260200101516000015184148015612cac5750828181518110612c9d57612c9d614de0565b60200260200101516020015146145b15612cb75750505050565b600101612c5c565b6000610d1d612ced7f00000000000000000000000000000000000000000000000000000000000000006133f6565b7fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b612d1786613952565b612d2086613b44565b6040805160208101949094528301919091526060820152608001612184565b8051600090606080835b83811015612ddc5782868281518110612d6457612d64614de0565b602002602001015160000151604051602001612d819291906157ec565b604051602081830303815290604052925081868281518110612da557612da5614de0565b602002602001015160200151604051602001612dc29291906157ec565b60408051601f198184030181529190529150600101612d49565b50612e59612e097f00000000000000000000000000000000000000000000000000000000000000006133f6565b8351602080860191909120845185830120604080517fb7c77dbcd01eff35f637803daf7abe3f0e3b86b5102459be5665dc7d9a85ac5e948101949094528301919091526060820152608001612184565b95945050505050565b60015474010000000000000000000000000000000000000000900460ff166000819003612e8d575060015b90565b6001547501000000000000000000000000000000000000000000900460ff166000819003612e8d5750600190565b60606000612ecd858585613bb8565b9250905080610d1b576040517f70a1717900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006060612f168580615174565b604080518082018252600e81527f5f63616c6c5461726765747328290000000000000000000000000000000000006020918201529051612f81939291808a01359142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b910161592b565b60408051808303601f190181529190528051602090910120603680547fff000000000000000000000000000000000000000000000000000000000000001660089290921c91909117905582613034576000805474010000000000000000000000000000000000000000900469ffffffffffffffffffff169060146130048361597e565b91906101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff16021790555050613047565b6000838152600360205260409020600190555b60006130538680615174565b876020013560405160240161306a939291906159a7565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb92e87fa0000000000000000000000000000000000000000000000000000000017815281516000805493945060609390928392916001600160a01b03165af493503d6040519150601f19601f6020830101168201604052808252806000602084013e508361316a57805160000361314d576040518060400160405280601781526020017f41564f5f5f524541534f4e5f4e4f545f444546494e4544000000000000000000815250925061316a565b600481019050808060200190518101906131679190614ff4565b92505b6131746001603655565b5050935093915050565b6001546060906001600160a01b03168061324b576040805160018082528183019092529060208083019080368337019050509150306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156131f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132149190615444565b8260008151811061322757613227614de0565b60200260200101906001600160a01b031690816001600160a01b0316815250505090565b61325481613f6e565b80602001905181019061326791906159cb565b91505090565b8151605a81118061327c575080155b1561329a5760405163ec92598560e01b815260040160405180910390fd5b80600103613322578160ff166001146132c65760405163ec92598560e01b815260040160405180910390fd5b6001805474010000000000000000000000000000000000000000900460ff161115613318576040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a25b6000600155505050565b600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000060ff8416021790556040516133909061337c908590602001615a00565b604051602081830303815290604052613f8f565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055611b338261350b565b6060610d1d8383604051806060016040528060278152602001615b136027913961405a565b604080518082018252601081527f41766f6361646f2d4d756c74697369670000000000000000000000000000000060209182015281518083018352600581527f312e312e300000000000000000000000000000000000000000000000000000009082015281517fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472918101919091527ff5d974efd8e94277ef83fff53580c07a80e6e3ffdb2e3fec63185a4e812655a2918101919091527f6815ba53416ba06aff1932cc76b3832272bafab9bc8e066be382e32b06ba5546606082015261027a60808201523060a082015260c0810182905260009060e0015b604051602081830303815290604052805190602001209050919050565b60ff81161580613527575061351e612e90565b60ff168160ff16115b156135455760405163ec92598560e01b815260040160405180910390fd5b60015460ff82811674010000000000000000000000000000000000000000909204161461266557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000060ff8416908102919091179091556040517f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a250565b6135eb82613605565b6135f482610d24565b604051602001612849929190615145565b60606000613612836140d2565b600101905060008167ffffffffffffffff811115613632576136326147d9565b6040519080825280601f01601f19166020018201604052801561365c576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461366657509392505050565b600080608083901c156136bd5760809290921c916010015b604083901c156136d25760409290921c916008015b602083901c156136e75760209290921c916004015b601083901c156136fc5760109290921c916002015b600883901c15612b015760010192915050565b6060600061371e8360026150ed565b6137299060026150da565b67ffffffffffffffff811115613741576137416147d9565b6040519080825280601f01601f19166020018201604052801561376b576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000816000815181106137a2576137a2614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f78000000000000000000000000000000000000000000000000000000000000008160018151811061380557613805614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006138418460026150ed565b61384c9060016150da565b90505b60018111156138e9577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061388d5761388d614de0565b1a60f81b8282815181106138a3576138a3614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c936138e281615a13565b905061384f565b508315610d1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401611fe2565b8051516000906060825b82811015613a9157817f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f658660000151838151811061399c5761399c614de0565b602002602001015160000151876000015184815181106139be576139be614de0565b60200260200101516020015180519060200120886000015185815181106139e7576139e7614de0565b60200260200101516040015189600001518681518110613a0957613a09614de0565b602002602001015160600151604051602001613a509594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120604051602001613a779291906157ec565b60408051601f19818403018152919052915060010161395c565b507fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc47818051906020012085602001518660400151876060015188608001518960a0015180519060200120604051602001613b25979695949392919096875260208701959095526040860193909352606085019190915260808401526001600160a01b031660a083015260c082015260e00190565b6040516020818303038152906040528051906020012092505050919050565b60007f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e826000015183602001518460400151856060015186608001516040516020016134ee96959493929190958652602086019490945260408501929092526060840152608083015260a082015260c00190565b8151600090606090613bc8612e62565b60ff16811080613bee5750838015613bee57506000868152600360205260409020546001145b15613c0c5760405163ec92598560e01b815260040160405180910390fd5b8067ffffffffffffffff811115613c2557613c256147d9565b604051908082528060200260200182016040528015613c4e578160200160208202803683370190505b5091506000613c5b61317e565b805190915060008080805b86811015613f5a57613c9e8b8281518110613c8357613c83614de0565b6020026020010151602001516001600160a01b03163b151590565b15613cfc578a8181518110613cb557613cb5614de0565b602002602001015160200151888281518110613cd357613cd3614de0565b60200260200101906001600160a01b031690816001600160a01b03168152505060019250613dbd565b613d238c8c8381518110613d1257613d12614de0565b6020026020010151600001516141b4565b888281518110613d3557613d35614de0565b60200260200101906001600160a01b031690816001600160a01b031681525050878181518110613d6757613d67614de0565b60200260200101516001600160a01b03168b8281518110613d8a57613d8a614de0565b6020026020010151602001516001600160a01b031614613dbd5760405163ec92598560e01b815260040160405180910390fd5b835b85811015613e2957888281518110613dd957613dd9614de0565b60200260200101516001600160a01b0316878281518110613dfc57613dfc614de0565b60200260200101516001600160a01b031603613e215760019250806001019450613e29565b600101613dbf565b5081613e3f576000985050505050505050613f66565b600091508215613f52578a517f1626ba7e00000000000000000000000000000000000000000000000000000000908c9083908110613e7f57613e7f614de0565b6020026020010151602001516001600160a01b0316631626ba7e8e8e8581518110613eac57613eac614de0565b6020026020010151600001516040518363ffffffff1660e01b8152600401613ed5929190615a2a565b602060405180830381865afa158015613ef2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f169190615a43565b7fffffffff000000000000000000000000000000000000000000000000000000001614613f4d576000985050505050505050613f66565b600092505b600101613c66565b50600197505050505050505b935093915050565b6060612b01826001613f8a816001600160a01b0384163b6150c7565b6141d0565b60008082604051602001613fa39190615a85565b6040516020818303038152906040529050600081604051602001613fc79190615a9e565b60405160208183030381529060405290508051602082016000f092506001600160a01b038316614053576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4445504c4f594d454e545f4641494c45440000000000000000000000000000006044820152606401611fe2565b5050919050565b6060600080856001600160a01b03168560405161407791906154bd565b600060405180830381855af49150503d80600081146140b2576040519150601f19603f3d011682016040523d82523d6000602084013e6140b7565b606091505b50915091506140c8868383876141f3565b9695505050505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061411b577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310614147576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061416557662386f26fc10000830492506010015b6305f5e100831061417d576305f5e100830492506008015b612710831061419157612710830492506004015b606483106141a3576064830492506002015b600a8310612b015760010192915050565b60008060006141c3858561428e565b91509150610d1b816142d3565b60408051603f8301601f19168101909152818152818360208301863c9392505050565b6060831561427c578251600003614275576001600160a01b0385163b614275576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611fe2565b5081614286565b6142868383614486565b949350505050565b60008082516041036142c45760208301516040840151606085015160001a6142b8878285856144ca565b945094505050506142cc565b506000905060025b9250929050565b60008160048111156142e7576142e7615ae3565b036142ef5750565b600181600481111561430357614303615ae3565b0361436a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611fe2565b600281600481111561437e5761437e615ae3565b036143e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611fe2565b60038160048111156143f9576143f9615ae3565b03612665576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401611fe2565b8151156144965781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fe2919061495b565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156145015750600090506003614585565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614555573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661457e57600060019250925050614585565b9150600090505b94509492505050565b60008083601f8401126145a057600080fd5b50813567ffffffffffffffff8111156145b857600080fd5b6020830191508360208260051b85010111156142cc57600080fd5b600080602083850312156145e657600080fd5b823567ffffffffffffffff8111156145fd57600080fd5b6146098582860161458e565b90969095509350505050565b60008083601f84011261462757600080fd5b50813567ffffffffffffffff81111561463f57600080fd5b6020830191508360208285010111156142cc57600080fd5b6001600160a01b038116811461266557600080fd5b803561467781614657565b919050565b60008060006040848603121561469157600080fd5b833567ffffffffffffffff8111156146a857600080fd5b6146b486828701614615565b90945092505060208401356146c881614657565b809150509250925092565b60005b838110156146ee5781810151838201526020016146d6565b50506000910152565b6000815180845261470f8160208601602086016146d3565b601f01601f19169290920160200192915050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b838110156147b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08984030185528151805187855261478c888601826146f7565b918901516001600160a01b0316948901949094529487019492509086019060010161474a565b509098975050505050505050565b6000602082840312156147d257600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561482b5761482b6147d9565b60405290565b6040516080810167ffffffffffffffff8111828210171561482b5761482b6147d9565b60405160c0810167ffffffffffffffff8111828210171561482b5761482b6147d9565b604051601f8201601f1916810167ffffffffffffffff811182821017156148a0576148a06147d9565b604052919050565b600067ffffffffffffffff8211156148c2576148c26147d9565b50601f01601f191660200190565b600082601f8301126148e157600080fd5b81356148f46148ef826148a8565b614877565b81815284602083860101111561490957600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561493857600080fd5b813567ffffffffffffffff81111561494f57600080fd5b614286848285016148d0565b602081526000610d1d60208301846146f7565b600067ffffffffffffffff821115614988576149886147d9565b5060051b60200190565b600082601f8301126149a357600080fd5b813560206149b36148ef8361496e565b82815260059290921b840181019181810190868411156149d257600080fd5b8286015b84811015614a6157803567ffffffffffffffff808211156149f75760008081fd5b8189019150604080601f19848d03011215614a125760008081fd5b614a1a614808565b8784013583811115614a2c5760008081fd5b614a3a8d8a838801016148d0565b8252509281013592614a4b84614657565b80880193909352505083529183019183016149d6565b509695505050505050565b600080600080848603610100811215614a8457600080fd5b853567ffffffffffffffff80821115614a9c57600080fd5b9087019060c0828a031215614ab057600080fd5b90955060209060a0601f1984011215614ac857600080fd5b818801955060c0880135925080831115614ae157600080fd5b614aed89848a01614992565b945060e0880135925080831115614b0357600080fd5b50818701915087601f830112614b1857600080fd5b8135614b266148ef8261496e565b81815260069190911b8301820190828101908a831115614b4557600080fd5b938301935b82851015614b88576040858c031215614b635760008081fd5b614b6b614808565b853581528486013585820152825260409094019390830190614b4a565b979a9699509497505050505050565b821515815260406020820152600061428660408301846146f7565b803560ff8116811461467757600080fd5b600080600060408486031215614bd857600080fd5b833567ffffffffffffffff811115614bef57600080fd5b614bfb8682870161458e565b9094509250614c0e905060208501614bb2565b90509250925092565b600080600060408486031215614c2c57600080fd5b8335614c3781614657565b9250602084013567ffffffffffffffff811115614c5357600080fd5b614c5f86828701614615565b9497909650939450505050565b60008060408385031215614c7f57600080fd5b50508035926020909101359150565b600060208284031215614ca057600080fd5b610d1d82614bb2565b600082601f830112614cba57600080fd5b81356020614cca6148ef8361496e565b82815260059290921b84018101918181019086841115614ce957600080fd5b8286015b84811015614a6157803567ffffffffffffffff80821115614d0e5760008081fd5b8189019150608080601f19848d03011215614d295760008081fd5b614d31614831565b87840135614d3e81614657565b815260408481013584811115614d545760008081fd5b614d628e8b838901016148d0565b838b01525060608581013591830191909152919093013590830152508352918301918301614ced565b600080600060608486031215614da057600080fd5b833567ffffffffffffffff811115614db757600080fd5b614dc386828701614ca9565b93505060208401359150604084013580151581146146c857600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215614e2157600080fd5b81356affffffffffffffffffffff81168114610d1d57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006000198203614e7e57614e7e614e3c565b5060010190565b60008085851115614e9557600080fd5b83861115614ea257600080fd5b5050820193919092039150565b7fffffffffffffffff0000000000000000000000000000000000000000000000008135818116916008851015614eef5780818660080360031b1b83161692505b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060208284031215614f3857600080fd5b813567ffffffffffffffff811115614f4f57600080fd5b61428684828501614992565b805160208083015191908110156111525760001960209190910360031b1b16919050565b7f5f5441524745545f50414e49434b45443a200000000000000000000000000000815260008251614fb78160128501602087016146d3565b9190910160120192915050565b6000614fd26148ef846148a8565b9050828152838383011115614fe657600080fd5b610d1d8360208301846146d3565b60006020828403121561500657600080fd5b815167ffffffffffffffff81111561501d57600080fd5b8201601f8101841361502e57600080fd5b61428684825160208401614fc4565b7f5f000000000000000000000000000000000000000000000000000000000000008152600082516150758160018501602087016146d3565b9190910160010192915050565b7f5f435553544f4d5f4552524f523a2000000000000000000000000000000000008152600082516150ba81600f8501602087016146d3565b91909101600f0192915050565b81810381811115612b0157612b01614e3c565b80820180821115612b0157612b01614e3c565b8082028115828204841417612b0157612b01614e3c565b600082516151168184602087016146d3565b7f2e20504152414d535f5241573a20000000000000000000000000000000000000920191825250600e01919050565b600083516151578184602088016146d3565b83519083019061516b8183602088016146d3565b01949350505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126151a957600080fd5b83018035915067ffffffffffffffff8211156151c457600080fd5b6020019150600581901b36038213156142cc57600080fd5b600060c082360312156151ee57600080fd5b6151f6614854565b823567ffffffffffffffff8082111561520e57600080fd5b61521a36838701614ca9565b83526020850135602084015260408501356040840152606085013560608401526152466080860161466c565b608084015260a085013591508082111561525f57600080fd5b5061526c368286016148d0565b60a08301525092915050565b600060a0828403121561528a57600080fd5b60405160a0810181811067ffffffffffffffff821117156152ad576152ad6147d9565b806040525082358152602083013560208201526040830135604082015260608301356060820152608083013560808201528091505092915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261531d57600080fd5b83018035915067ffffffffffffffff82111561533857600080fd5b6020019150368190038213156142cc57600080fd5b60006020828403121561535f57600080fd5b8135610d1d81614657565b600081518084526020808501945080840160005b838110156153a35781516001600160a01b03168752958201959082019060010161537e565b509495945050505050565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6040815260006153ec604083018661536a565b82810360208401526140c88185876153ae565b606081526000615412606083018761536a565b828103602084015261542481876146f7565b905082810360408401526154398185876153ae565b979650505050505050565b60006020828403121561545657600080fd5b8151610d1d81614657565b6001600160a01b03848116825260406020808401829052908301849052600091859160608501845b878110156154b057843561549c81614657565b841682529382019390820190600101615489565b5098975050505050505050565b600082516154cf8184602087016146d3565b9190910192915050565b6001600160a01b0384168152604060208201526000612e596040830184866153ae565b6001600160a01b0383168152604060208201526000614286604083018461536a565b6000806040838503121561553157600080fd5b82519150602083015161554381614657565b809150509250929050565b600082516155608184602087016146d3565b7f5f41564f5f5f4d4f4449464945445f53544f5241474500000000000000000000920191825250601601919050565b600082516155a18184602087016146d3565b7f5f41564f5f5f4e4f5f464c4153484c4f414e5f494e5f464c4153484c4f414e00920191825250601f01919050565b600082601f8301126155e157600080fd5b815160206155f16148ef8361496e565b82815260059290921b8401810191818101908684111561561057600080fd5b8286015b84811015614a6157805161562781614657565b8352918301918301615614565b600082601f83011261564557600080fd5b610d1d83835160208501614fc4565b600080600080600060a0868803121561566c57600080fd5b855167ffffffffffffffff8082111561568457600080fd5b61569089838a016155d0565b96506020915081880151818111156156a757600080fd5b8801601f81018a136156b857600080fd5b80516156c66148ef8261496e565b81815260059190911b8201840190848101908c8311156156e557600080fd5b928501925b82841015615703578351825292850192908501906156ea565b60408c015160608d0151919a50985094505050508082111561572457600080fd5b61573089838a01615634565b9350608088015191508082111561574657600080fd5b5061575388828901615634565b9150509295509295909350565b60608152600061577360608301866146f7565b90508360208301527fffffffff0000000000000000000000000000000000000000000000000000000083166040830152949350505050565b600082516157bd8184602087016146d3565b7f5f41564f5f5f494e56414c49445f49445f4f525f4f5045524154494f4e000000920191825250601d01919050565b600083516157fe8184602088016146d3565b9190910191825250602001919050565b81835260006020808501808196508560051b810191508460005b8781101561591e57828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8188360301811261586757600080fd5b87016080813561587681614657565b6001600160a01b0316865281870135368390037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe10181126158b657600080fd5b8201878101903567ffffffffffffffff8111156158d257600080fd5b8036038213156158e157600080fd5b82898901526158f383890182846153ae565b604085810135908a015260609485013594909801939093525050509784019790840190600101615828565b5091979650505050505050565b60808152600061593f60808301878961580e565b60208301959095525060408101929092527fffffffff000000000000000000000000000000000000000000000000000000001660609091015292915050565b600069ffffffffffffffffffff80831681810361599d5761599d614e3c565b6001019392505050565b6040815260006159bb60408301858761580e565b9050826020830152949350505050565b6000602082840312156159dd57600080fd5b815167ffffffffffffffff8111156159f457600080fd5b614286848285016155d0565b602081526000610d1d602083018461536a565b600081615a2257615a22614e3c565b506000190190565b82815260406020820152600061428660408301846146f7565b600060208284031215615a5557600080fd5b81517fffffffff0000000000000000000000000000000000000000000000000000000081168114610d1d57600080fd5b60008152600082516150758160018501602087016146d3565b7f600b5981380380925939f3000000000000000000000000000000000000000000815260008251615ad681600b8501602087016146d3565b91909101600b0192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122056ffe1aede88a9ceef845f067a7a2b08750fe3af8876247bec6db406ed00b04b64736f6c63430008120033000000000000000000000000779385ec7a04242259add4990e3130846f80ea6900000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068480000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d
Deployed Bytecode
0x6080604052600436106102345760003560e01c806387265c9511610138578063b4907ddc116100b0578063ec5904831161007f578063ed969f0a11610064578063ed969f0a146107d8578063f7b14fac1461080c578063fe6bfed31461082c57600080fd5b8063ec59048314610784578063ec80dcc2146107b857600080fd5b8063b4907ddc146106b3578063bb1c006b146106fc578063c7fe944f14610730578063eb2856e91461076457600080fd5b80639ee3f88311610107578063a0d06193116100ec578063a0d0619314610617578063a755ecfc1461064b578063a80ebaf11461067f57600080fd5b80639ee3f883146105e2578063a08519b5146105f757600080fd5b806387265c951461051e57806394f0320e14610567578063984cee411461059b5780639cae7aa8146105ae57600080fd5b806343ea1996116101cb5780635b502b401161019a57806364d4c8191161017f57806364d4c819146104b55780636fbc15e9146104e95780638129fc1c1461050957600080fd5b80635b502b40146104615780636043848a1461049557600080fd5b806343ea1996146103c3578063447f537f146103d957806349926a661461040d5780634cf5de191461042d57600080fd5b80632b537875116102075780632b537875146103145780633a06cc8c146103415780633e0cd897146103755780633edd9187146103a357600080fd5b8063044d3cd21461023957806316914ac01461025b5780631bbce23d146102ac5780632a5d1d66146102d9575b600080fd5b34801561024557600080fd5b506102596102543660046145d3565b610860565b005b34801561026757600080fd5b5061028f7f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc00684881565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102b857600080fd5b506102cc6102c736600461467c565b6109f7565b6040516102a39190614723565b3480156102e557600080fd5b506103066102f43660046147c0565b60036020526000908152604090205481565b6040519081526020016102a3565b34801561032057600080fd5b5061033461032f366004614926565b610d24565b6040516102a3919061495b565b34801561034d57600080fd5b506103067f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e81565b34801561038157600080fd5b50610395610390366004614a6c565b611158565b6040516102a3929190614b97565b3480156103af57600080fd5b506102596103be3660046145d3565b6114dd565b3480156103cf57600080fd5b5061030661027a81565b3480156103e557600080fd5b506103067f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2081565b34801561041957600080fd5b506102596104283660046147c0565b611575565b34801561043957600080fd5b506103067fc84a2c176321157bd55b70feb5871c4304d99c870656d2fc420998eff645e20781565b34801561046d57600080fd5b506103067f1a7f20cd17edb78769659fdd929cc47ea75b683f7b24e7933f7fa66c44ad88c081565b3480156104a157600080fd5b506102596104b0366004614bc3565b6115b8565b3480156104c157600080fd5b506103067fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac5647281565b3480156104f557600080fd5b50610259610504366004614c17565b6119ab565b34801561051557600080fd5b50610259611b38565b34801561052a57600080fd5b506103346040518060400160405280601081526020017f41766f6361646f2d4d756c74697369670000000000000000000000000000000081525081565b34801561057357600080fd5b5061028f7f00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a5481565b6102596105a9366004614c6c565b611dae565b3480156105ba57600080fd5b5061028f7f000000000000000000000000779385ec7a04242259add4990e3130846f80ea6981565b3480156105ee57600080fd5b50610306605a81565b34801561060357600080fd5b506102596106123660046147c0565b61211c565b34801561062357600080fd5b506103067fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b81565b34801561065757600080fd5b506103067fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc4781565b34801561068b57600080fd5b506103067f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f6581565b3480156106bf57600080fd5b506103346040518060400160405280600581526020017f312e312e3000000000000000000000000000000000000000000000000000000081525081565b34801561070857600080fd5b506103067fb7c77dbcd01eff35f637803daf7abe3f0e3b86b5102459be5665dc7d9a85ac5e81565b34801561073c57600080fd5b5061028f7f000000000000000000000000e8385fb3a5f15ded06eb5e20e5a81bf43115eb8e81565b34801561077057600080fd5b5061025961077f366004614bc3565b61222b565b34801561079057600080fd5b506103067f0000000000000000000000000000000000000000000000000001c6bf5263400081565b3480156107c457600080fd5b506102596107d3366004614c8e565b612654565b3480156107e457600080fd5b506103067f195ee08d2ba047c23da55fd07e3530ac91de13e8b3f1a46d6e18d4ab2f4177eb81565b34801561081857600080fd5b50610259610827366004614d8b565b612668565b34801561083857600080fd5b506103067f00000000000000000000000000000000000000000000000000b1a2bc2ec5000081565b610868612a86565b80600081900361087757505050565b60058111156108995760405163ec92598560e01b815260040160405180910390fd5b6000805474010000000000000000000000000000000000000000900469ffffffffffffffffffff16905b8281101561099e57818585838181106108de576108de614de0565b90506020020160208101906108f39190614e0f565b6affffffffffffffffffffff16036109425760405182907f5e8592bbef3f468eddd77c711ee57100f76bbb79500d8871a700fb6108929d9290600090a28161093a81614e6b565b925050610996565b8185858381811061095557610955614de0565b905060200201602081019061096a9190614e0f565b6affffffffffffffffffffff1611156109965760405163ec92598560e01b815260040160405180910390fd5b6001016108c3565b506000805469ffffffffffffffffffff90921674010000000000000000000000000000000000000000027fffff00000000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055505050565b6060827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf8101610ad05760408051600180825281830190925290816020015b604080518082019091526060815260006020820152815260200190600190039081610a36579050506040805160606020601f890181900402820181018352918101878152929450918291889088908190850183828082843760009201829052509385525050506001600160a01b0386166020909201919091528351849190610ac057610ac0614de0565b6020026020010181905250610d1b565b605a811015610b0b576040517fa8398eb900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fdec0de6520000000000000000000000000000000000000000000000000000000610b3a600560008789614e85565b610b4391614eaf565b7fffffffffffffffff0000000000000000000000000000000000000000000000001603610d0c5760557ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8201048067ffffffffffffffff811115610ba957610ba96147d9565b604051908082528060200260200182016040528015610bef57816020015b604080518082019091526060815260006020820152815260200190600190039081610bc75790505b50925060005b81811015610d0557605581026046810190600090610c1890605a01838a8c614e85565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050505060208101516040805180820190915291925060601c9080610c91857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf81018d8f614e85565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506001600160a01b0383166020909101528751889086908110610cec57610cec614de0565b6020026020010181905250836001019350505050610bf5565b5050610d1b565b610d1884860186614f26565b91505b505b9392505050565b6060600482511015610d6957505060408051808201909152601381527f5f524541534f4e5f4e4f545f444546494e454400000000000000000000000000602082015290565b60208201517fb1b7848f000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821601610df557600483019250610dcf83610dca90614f5b565b612aea565b604051602001610ddf9190614f7f565b6040516020818303038152906040529150611078565b7ff73c8660000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821601610e685760048301925082806020019051810190610e589190614ff4565b604051602001610ddf919061503d565b610e7560e082901c612aea565b604051602001610e859190615082565b6040516020818303038152906040529150600060048451610ea691906150c7565b9050610eb460fa60086150da565b610ebf8260026150ed565b1115610ed457610ed160fa60046150da565b90505b6000610ee18260026150ed565b67ffffffffffffffff811115610ef957610ef96147d9565b6040519080825280601f01601f191660200182016040528015610f23576020820181803683370190505b50905060005b8281101561102e57610f666010878360040181518110610f4b57610f4b614de0565b016020015160f81c81610f6057610f60614ef7565b04612b07565b828260020281518110610f7b57610f7b614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610fdf6010878360040181518110610fc457610fc4614de0565b016020015160f81c81610fd957610fd9614ef7565b06612b07565b828260020260010181518110610ff757610ff7614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101610f29565b50836040516020016110409190615104565b60405160208183030381529060405293508381604051602001611064929190615145565b604051602081830303815290604052935050505b60fa825111156111525781600061109160fa60016150da565b905060008167ffffffffffffffff8111156110ae576110ae6147d9565b6040519080825280601f01601f1916602001820160405280156110d8576020820181803683370190505b50905060005b8281101561114c578381815181106110f8576110f8614de0565b602001015160f81c60f81b82828151811061111557611115614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506001016110de565b50935050505b50919050565b60006060611164612a86565b61118c6111718780615174565b90508760400135876040013588606001358960800135612b67565b8251156111bd576111bd6111b76111a2886151dc565b6111b136899003890189615278565b46612bd9565b84612c4e565b6000808451116111ec576111e76111d3886151dc565b6111e236899003890189615278565b612cbf565b6111f5565b6111f584612d3f565b905060008086511161121157611209612e62565b60ff16611214565b85515b67ffffffffffffffff81111561122c5761122c6147d9565b604051908082528060200260200182016040528015611255578160200160208202803683370190505b50905085516000148061128757508560008151811061127657611276614de0565b602002602001015160000151516000145b806112c15750856000815181106112a0576112a0614de0565b6020026020010151602001516001600160a01b03166001600160a01b038016145b1561139f5760006112d0612e90565b60ff1690506000816001146112e757612af86112eb565b6121345b61ffff16905060018351111561132d57825161130990611af46150ed565b6113148360a06150ed565b611320906119006150da565b61132a91906150da565b90505b60408a01351961134657611343610898826150da565b90505b875160000361136a57825161135d90610ce46150ed565b61136790826150da565b90505b60005a905060005b825a61137e90846150c7565b1015611396578061138e81614e6b565b915050611372565b505050506113b5565b6113b282878a6040013560001914612ebe565b90505b6113ff886113e561271084518c8060a001906113d191906152e8565b600802610172929092029290920101919050565b8a60400135600019146113f9576000612f08565b84612f08565b9094509250831561146f573361141b60a08a0160808b0161534d565b6001600160a01b03167fbc2a38a1bedbfde5ed4d7163296382bfeadf60b16e6e52b5b08152e6ba40de1b8361145360a08d018d6152e8565b604051611462939291906153d9565b60405180910390a36114d2565b3361148060a08a0160808b0161534d565b6001600160a01b03167f4967a7ed928d6e5351e33550b4d353f899d0df48662f154a7fd92559213d008f83866114b960a08e018e6152e8565b6040516114c994939291906153ff565b60405180910390a35b505094509492505050565b6114e5612a86565b8060005b8181101561156f5760016003600086868581811061150957611509614de0565b9050602002013581526020019081526020016000208190555083838281811061153457611534614de0565b905060200201357f3749b30151183a20a5e6b93dce343f6010bde0e057441cca9548231f5727b70360405160405180910390a26001016114e9565b50505050565b61157d612a86565b6000818152600260205260408082208290555182917fc93ef95774a42ae5f9ca1e82cc762d17a6c2424041b91bf34dcf23aacc28cf1291a250565b6115c0612a86565b8160008190036115e35760405163ec92598560e01b815260040160405180910390fd5b60006115ed61317e565b805190915060006115fe84836150c7565b90506000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611640573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116649190615444565b905060008267ffffffffffffffff811115611681576116816147d9565b6040519080825280602002602001820160405280156116aa578160200160208202803683370190505b50905060008060005b868110156118595781810392508882148061171f57508b8b838181106116db576116db614de0565b90506020020160208101906116f0919061534d565b6001600160a01b031688828151811061170b5761170b614de0565b60200260200101516001600160a01b031614155b15611796578583101561177d5787818151811061173e5761173e614de0565b602002602001015184848151811061175857611758614de0565b60200260200101906001600160a01b031690816001600160a01b031681525050611851565b60405163ec92598560e01b815260040160405180910390fd5b846001600160a01b03168c8c848181106117b2576117b2614de0565b90506020020160208101906117c7919061534d565b6001600160a01b0316036117ee5760405163ec92598560e01b815260040160405180910390fd5b8b8b8381811061180057611800614de0565b9050602002016020810190611815919061534d565b6001600160a01b03167f3525e22824a8a7df2c9a6029941c824cf95b6447f1e13d5128fd3826d35afe8b60405160405180910390a28160010191505b6001016116b3565b5087811461187a5760405163ec92598560e01b815260040160405180910390fd5b611884838a61326d565b60007f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068486001600160a01b0316308d8d6040516024016118c593929190615461565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa1153e7f000000000000000000000000000000000000000000000000000000001790525161192891906154bd565b6000604051808303816000865af19150503d8060008114611965576040519150601f19603f3d011682016040523d82523d6000602084013e61196a565b606091505b505090508061199d576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b505050505050505050505050565b6119b3612a86565b6000546001600160a01b03848116911614611b33576040517efe90e30000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301527f000000000000000000000000779385ec7a04242259add4990e3130846f80ea69169062fe90e39060240160006040518083038186803b158015611a4057600080fd5b505afa158015611a54573d6000803e3d6000fd5b5050600080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b0388811691821784556040519216945092507fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b9190a2611b3084828585604051602401611ad3939291906154d9565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fe7e38cd3000000000000000000000000000000000000000000000000000000001790526133d1565b50505b505050565b611b40612a86565b6000306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba49190615444565b90506001600160a01b0381163b151580611bc557506001600160a01b038116155b15611be35760405163ec92598560e01b815260040160405180910390fd5b611bed6001603655565b6040516001600160a01b038216907f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2490600090a26040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a2604080516001808252818301909252600091602080830190803683370190505090508181600081518110611c8357611c83614de0565b6001600160a01b0392831660209182029290920101526040516000917f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068481690611cd290309085906024016154fc565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f863deb110000000000000000000000000000000000000000000000000000000017905251611d3591906154bd565b6000604051808303816000865af19150503d8060008114611d72576040519150601f19603f3d011682016040523d82523d6000602084013e611d77565b606091505b5050905080611b33576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a1505050565b611db6612a86565b60008060005a850390506000807f000000000000000000000000779385ec7a04242259add4990e3130846f80ea696001600160a01b0316613a9884604051602401611e0391815260200190565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f75dc7d8c0000000000000000000000000000000000000000000000000000000017905251611e6691906154bd565b6000604051808303818686fa925050503d8060008114611ea2576040519150601f19603f3d011682016040523d82523d6000602084013e611ea7565b606091505b5060408101519193509150828015611ec05750603f8251115b8015611ed357506001600160a01b038111155b15611f425781806020019051810190611eec919061551e565b90965094507f00000000000000000000000000000000000000000000000000b1a2bc2ec50000861115611f3d577f00000000000000000000000000000000000000000000000000b1a2bc2ec5000095505b611f89565b7f000000000000000000000000e8385fb3a5f15ded06eb5e20e5a81bf43115eb8e94507f0000000000000000000000000000000000000000000000000001c6bf5263400095505b5050505060008211156120eb57600083118015611fa557508282115b15611feb576040517f360ceed500000000000000000000000000000000000000000000000000000000815260048101839052602481018490526044015b60405180910390fd5b81471015612028576040517ffae5708700000000000000000000000000000000000000000000000000000000815260048101839052602401611fe2565b6000816001600160a01b0316836103e890604051600060405180830381858888f193505050503d806000811461207a576040519150601f19603f3d011682016040523d82523d6000602084013e61207f565b606091505b5050905080156120b95760405183907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a26120e5565b60405183907fd1b3877a34a9595e45447dad16b7354ec5a8bece40075fd60d647e76397b476490600090a25b5061156f565b60405182907f69e27f80547602d16208b028c44d20f25956e1fb7d0f51d62aa02f392426f37190600090a250505050565b612124612a86565b6121eb6121507f6e452848784197f00927d379e3db9e69a5131d2269f862bfcd05a0b38f6abf7f6133f6565b604080517f975a23dc79ce4197983f465f4920e2180ff7f4a4febb7a3a261b9099f5414e2060208201529081018490526060015b60408051601f1981840301815282825280516020918201207f19010000000000000000000000000000000000000000000000000000000000008483015260228401949094526042808401949094528151808403909401845260629092019052815191012090565b600081815260026020526040808220600190555191925082917fbef281d0d1046788f9baa37e73d57e85933f130d8ec1e223366e0b04fbf254029190a250565b612233612a86565b8180158061226e575060008484828161224e5761224e614de0565b9050602002016020810190612263919061534d565b6001600160a01b0316145b1561228c5760405163ec92598560e01b815260040160405180910390fd5b600061229661317e565b805190915060006122a784836150da565b9050605a8111156122cb5760405163ec92598560e01b815260040160405180910390fd5b60008167ffffffffffffffff8111156122e6576122e66147d9565b60405190808252806020026020018201604052801561230f578160200160208202803683370190505b50905060008060005b848110156125235781810392508782148061238d5750858310801561238d57508a8a8381811061234a5761234a614de0565b905060200201602081019061235f919061534d565b6001600160a01b031687848151811061237a5761237a614de0565b60200260200101516001600160a01b0316105b156123e3578683815181106123a4576123a4614de0565b60200260200101518482815181106123be576123be614de0565b60200260200101906001600160a01b031690816001600160a01b03168152505061249f565b8a8a838181106123f5576123f5614de0565b905060200201602081019061240a919061534d565b84828151811061241c5761241c614de0565b60200260200101906001600160a01b031690816001600160a01b0316815250508a8a8381811061244e5761244e614de0565b9050602002016020810190612463919061534d565b6001600160a01b03167f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2460405160405180910390a28160010191505b6000811180156124fd5750836124b66001836150c7565b815181106124c6576124c6614de0565b60200260200101516001600160a01b03168482815181106124e9576124e9614de0565b60200260200101516001600160a01b031611155b1561251b5760405163ec92598560e01b815260040160405180910390fd5b600101612318565b5061252e838961326d565b60007f000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068486001600160a01b0316308c8c60405160240161256f93929190615461565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f863deb1100000000000000000000000000000000000000000000000000000000179052516125d291906154bd565b6000604051808303816000865af19150503d806000811461260f576040519150601f19603f3d011682016040523d82523d6000602084013e612614565b606091505b5050905080612647576040517f8efad6383ff2377775d48a35c6feba6bcb6f3e48cbc16b502862fee83f888b5290600090a15b5050505050505050505050565b61265c612a86565b6126658161350b565b50565b612670612a86565b61267a6001603655565b6000806000846001148061268e5750846015145b905080156126a157600054925060015491505b855160005b81811015612a7c5760008882815181106126c2576126c2614de0565b60200260200101519050600060606000836060015160001480156126fb575060028b10806126f057508a6014145b806126fb57508a6015145b156127825783600001516001600160a01b03168460400151856020015160405161272591906154bd565b60006040518083038185875af1925050503d8060008114612762576040519150601f19603f3d011682016040523d82523d6000602084013e612767565b606091505b5090935091508261277d5761277d8186846135e2565b612a6d565b836060015160011480156127935750865b156128905783600001516001600160a01b031684602001516040516127b891906154bd565b600060405180830381855af49150503d80600081146127f3576040519150601f19603f3d011682016040523d82523d6000602084013e6127f8565b606091505b5090935091508261280e5761280e8186846135e2565b6128186001603655565b6000546001548a8214801561282c57508981145b6128895761283987613605565b604051602001612849919061554e565b60408051601f19818403018152908290527f08c379a0000000000000000000000000000000000000000000000000000000008252611fe29160040161495b565b5050612a6d565b836060015160021480156128ae57508a601414806128ae57508a6015145b15612a545789156128d2576128c285613605565b604051602001612849919061558f565b602084015160048101805190916128f191810160249081019101615654565b50604080518082018252601281527f657865637574654f7065617274696f6e28290000000000000000000000000000602091820152905191955061295e94508593504292507f1505515629d0073e3ba07c728aaf9bf1ae8ee7ac66316d54304b5d7dc2cb487e9101615760565b60405160208183030381529060405280519060200120603660006101000a8154817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff021916908360081c02179055508b6036601f6101000a81548160ff021916908360ff16021790555084600001516001600160a01b0316856040015186602001516040516129ec91906154bd565b60006040518083038185875af1925050503d8060008114612a29576040519150601f19603f3d011682016040523d82523d6000602084013e612a2e565b606091505b50909450925083612a4457612a448287856135e2565b612a4e6001603655565b50612a6d565b612a5d85613605565b60405160200161284991906157ab565b846001019450505050506126a6565b5050505050505050565b307f00000000000000000000000050bde2dbf42172c85fe358f0603eed0b7ecbc2ef6001600160a01b031603612ae8576040517f5e5225b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6060612b0182612af9846136a5565b60010161370f565b92915050565b6000600a8260ff161015612b1f575060300160f81b90565b60108260ff161015612b35575060570160f81b90565b6040517f9778fcab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085118015612ba75750836000191480612ba7575060005474010000000000000000000000000000000000000000900469ffffffffffffffffffff1684145b5042831180612bc05750600082118015612bc057504282105b50600081118015612bd15750803414155b505050505050565b60007fc84a2c176321157bd55b70feb5871c4304d99c870656d2fc420998eff645e207612c0585613952565b612c0e85613b44565b60408051602081019490945283019190915260608201526080810183905260a0016040516020818303038152906040528051906020012090509392505050565b80516001811115611b335760005b8181101561156f57828181518110612c7657612c76614de0565b60200260200101516000015184148015612cac5750828181518110612c9d57612c9d614de0565b60200260200101516020015146145b15612cb75750505050565b600101612c5c565b6000610d1d612ced7f6e452848784197f00927d379e3db9e69a5131d2269f862bfcd05a0b38f6abf7f6133f6565b7fe74ed9f75082a9594f22af0e866100073e626e818daffa7c892b007cd81bdf3b612d1786613952565b612d2086613b44565b6040805160208101949094528301919091526060820152608001612184565b8051600090606080835b83811015612ddc5782868281518110612d6457612d64614de0565b602002602001015160000151604051602001612d819291906157ec565b604051602081830303815290604052925081868281518110612da557612da5614de0565b602002602001015160200151604051602001612dc29291906157ec565b60408051601f198184030181529190529150600101612d49565b50612e59612e097f56de5bbd3725e9c80073ed00b9767413d9ddf7d1ec1d602a8f924f15a9c756856133f6565b8351602080860191909120845185830120604080517fb7c77dbcd01eff35f637803daf7abe3f0e3b86b5102459be5665dc7d9a85ac5e948101949094528301919091526060820152608001612184565b95945050505050565b60015474010000000000000000000000000000000000000000900460ff166000819003612e8d575060015b90565b6001547501000000000000000000000000000000000000000000900460ff166000819003612e8d5750600190565b60606000612ecd858585613bb8565b9250905080610d1b576040517f70a1717900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006060612f168580615174565b604080518082018252600e81527f5f63616c6c5461726765747328290000000000000000000000000000000000006020918201529051612f81939291808a01359142917f2c3dc36b4c32f0a95adcdc76a3bfe9d5de90545c72f1a422ba22155d69a7592b910161592b565b60408051808303601f190181529190528051602090910120603680547fff000000000000000000000000000000000000000000000000000000000000001660089290921c91909117905582613034576000805474010000000000000000000000000000000000000000900469ffffffffffffffffffff169060146130048361597e565b91906101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff16021790555050613047565b6000838152600360205260409020600190555b60006130538680615174565b876020013560405160240161306a939291906159a7565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb92e87fa0000000000000000000000000000000000000000000000000000000017815281516000805493945060609390928392916001600160a01b03165af493503d6040519150601f19601f6020830101168201604052808252806000602084013e508361316a57805160000361314d576040518060400160405280601781526020017f41564f5f5f524541534f4e5f4e4f545f444546494e4544000000000000000000815250925061316a565b600481019050808060200190518101906131679190614ff4565b92505b6131746001603655565b5050935093915050565b6001546060906001600160a01b03168061324b576040805160018082528183019092529060208083019080368337019050509150306001600160a01b031663b2bdfa7b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156131f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132149190615444565b8260008151811061322757613227614de0565b60200260200101906001600160a01b031690816001600160a01b0316815250505090565b61325481613f6e565b80602001905181019061326791906159cb565b91505090565b8151605a81118061327c575080155b1561329a5760405163ec92598560e01b815260040160405180910390fd5b80600103613322578160ff166001146132c65760405163ec92598560e01b815260040160405180910390fd5b6001805474010000000000000000000000000000000000000000900460ff161115613318576040516001907f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a25b6000600155505050565b600180547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000060ff8416021790556040516133909061337c908590602001615a00565b604051602081830303815290604052613f8f565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055611b338261350b565b6060610d1d8383604051806060016040528060278152602001615b136027913961405a565b604080518082018252601081527f41766f6361646f2d4d756c74697369670000000000000000000000000000000060209182015281518083018352600581527f312e312e300000000000000000000000000000000000000000000000000000009082015281517fd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472918101919091527ff5d974efd8e94277ef83fff53580c07a80e6e3ffdb2e3fec63185a4e812655a2918101919091527f6815ba53416ba06aff1932cc76b3832272bafab9bc8e066be382e32b06ba5546606082015261027a60808201523060a082015260c0810182905260009060e0015b604051602081830303815290604052805190602001209050919050565b60ff81161580613527575061351e612e90565b60ff168160ff16115b156135455760405163ec92598560e01b815260040160405180910390fd5b60015460ff82811674010000000000000000000000000000000000000000909204161461266557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000060ff8416908102919091179091556040517f473ed0e2326c87d478d77349ea54e3808e36c5da36c74a0a15a2b7aad0a6f28b90600090a250565b6135eb82613605565b6135f482610d24565b604051602001612849929190615145565b60606000613612836140d2565b600101905060008167ffffffffffffffff811115613632576136326147d9565b6040519080825280601f01601f19166020018201604052801561365c576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461366657509392505050565b600080608083901c156136bd5760809290921c916010015b604083901c156136d25760409290921c916008015b602083901c156136e75760209290921c916004015b601083901c156136fc5760109290921c916002015b600883901c15612b015760010192915050565b6060600061371e8360026150ed565b6137299060026150da565b67ffffffffffffffff811115613741576137416147d9565b6040519080825280601f01601f19166020018201604052801561376b576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000816000815181106137a2576137a2614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f78000000000000000000000000000000000000000000000000000000000000008160018151811061380557613805614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006138418460026150ed565b61384c9060016150da565b90505b60018111156138e9577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061388d5761388d614de0565b1a60f81b8282815181106138a3576138a3614de0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c936138e281615a13565b905061384f565b508315610d1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401611fe2565b8051516000906060825b82811015613a9157817f5c1c53221914feac61859607db2bf67fc5d2d108016fd0bab7ceb23e65e90f658660000151838151811061399c5761399c614de0565b602002602001015160000151876000015184815181106139be576139be614de0565b60200260200101516020015180519060200120886000015185815181106139e7576139e7614de0565b60200260200101516040015189600001518681518110613a0957613a09614de0565b602002602001015160600151604051602001613a509594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b60405160208183030381529060405280519060200120604051602001613a779291906157ec565b60408051601f19818403018152919052915060010161395c565b507fdc7eeb8956fa99ee1655bf2f897041e2392df70038b7ac74190fa437c58cfc47818051906020012085602001518660400151876060015188608001518960a0015180519060200120604051602001613b25979695949392919096875260208701959095526040860193909352606085019190915260808401526001600160a01b031660a083015260c082015260e00190565b6040516020818303038152906040528051906020012092505050919050565b60007f222df8c7761e6301d3e65134b6db7ac2b975814601340cc8d4c6bd6bc4742f9e826000015183602001518460400151856060015186608001516040516020016134ee96959493929190958652602086019490945260408501929092526060840152608083015260a082015260c00190565b8151600090606090613bc8612e62565b60ff16811080613bee5750838015613bee57506000868152600360205260409020546001145b15613c0c5760405163ec92598560e01b815260040160405180910390fd5b8067ffffffffffffffff811115613c2557613c256147d9565b604051908082528060200260200182016040528015613c4e578160200160208202803683370190505b5091506000613c5b61317e565b805190915060008080805b86811015613f5a57613c9e8b8281518110613c8357613c83614de0565b6020026020010151602001516001600160a01b03163b151590565b15613cfc578a8181518110613cb557613cb5614de0565b602002602001015160200151888281518110613cd357613cd3614de0565b60200260200101906001600160a01b031690816001600160a01b03168152505060019250613dbd565b613d238c8c8381518110613d1257613d12614de0565b6020026020010151600001516141b4565b888281518110613d3557613d35614de0565b60200260200101906001600160a01b031690816001600160a01b031681525050878181518110613d6757613d67614de0565b60200260200101516001600160a01b03168b8281518110613d8a57613d8a614de0565b6020026020010151602001516001600160a01b031614613dbd5760405163ec92598560e01b815260040160405180910390fd5b835b85811015613e2957888281518110613dd957613dd9614de0565b60200260200101516001600160a01b0316878281518110613dfc57613dfc614de0565b60200260200101516001600160a01b031603613e215760019250806001019450613e29565b600101613dbf565b5081613e3f576000985050505050505050613f66565b600091508215613f52578a517f1626ba7e00000000000000000000000000000000000000000000000000000000908c9083908110613e7f57613e7f614de0565b6020026020010151602001516001600160a01b0316631626ba7e8e8e8581518110613eac57613eac614de0565b6020026020010151600001516040518363ffffffff1660e01b8152600401613ed5929190615a2a565b602060405180830381865afa158015613ef2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f169190615a43565b7fffffffff000000000000000000000000000000000000000000000000000000001614613f4d576000985050505050505050613f66565b600092505b600101613c66565b50600197505050505050505b935093915050565b6060612b01826001613f8a816001600160a01b0384163b6150c7565b6141d0565b60008082604051602001613fa39190615a85565b6040516020818303038152906040529050600081604051602001613fc79190615a9e565b60405160208183030381529060405290508051602082016000f092506001600160a01b038316614053576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4445504c4f594d454e545f4641494c45440000000000000000000000000000006044820152606401611fe2565b5050919050565b6060600080856001600160a01b03168560405161407791906154bd565b600060405180830381855af49150503d80600081146140b2576040519150601f19603f3d011682016040523d82523d6000602084013e6140b7565b606091505b50915091506140c8868383876141f3565b9695505050505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061411b577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310614147576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061416557662386f26fc10000830492506010015b6305f5e100831061417d576305f5e100830492506008015b612710831061419157612710830492506004015b606483106141a3576064830492506002015b600a8310612b015760010192915050565b60008060006141c3858561428e565b91509150610d1b816142d3565b60408051603f8301601f19168101909152818152818360208301863c9392505050565b6060831561427c578251600003614275576001600160a01b0385163b614275576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611fe2565b5081614286565b6142868383614486565b949350505050565b60008082516041036142c45760208301516040840151606085015160001a6142b8878285856144ca565b945094505050506142cc565b506000905060025b9250929050565b60008160048111156142e7576142e7615ae3565b036142ef5750565b600181600481111561430357614303615ae3565b0361436a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401611fe2565b600281600481111561437e5761437e615ae3565b036143e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401611fe2565b60038160048111156143f9576143f9615ae3565b03612665576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401611fe2565b8151156144965781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fe2919061495b565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156145015750600090506003614585565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614555573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661457e57600060019250925050614585565b9150600090505b94509492505050565b60008083601f8401126145a057600080fd5b50813567ffffffffffffffff8111156145b857600080fd5b6020830191508360208260051b85010111156142cc57600080fd5b600080602083850312156145e657600080fd5b823567ffffffffffffffff8111156145fd57600080fd5b6146098582860161458e565b90969095509350505050565b60008083601f84011261462757600080fd5b50813567ffffffffffffffff81111561463f57600080fd5b6020830191508360208285010111156142cc57600080fd5b6001600160a01b038116811461266557600080fd5b803561467781614657565b919050565b60008060006040848603121561469157600080fd5b833567ffffffffffffffff8111156146a857600080fd5b6146b486828701614615565b90945092505060208401356146c881614657565b809150509250925092565b60005b838110156146ee5781810151838201526020016146d6565b50506000910152565b6000815180845261470f8160208601602086016146d3565b601f01601f19169290920160200192915050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b838110156147b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08984030185528151805187855261478c888601826146f7565b918901516001600160a01b0316948901949094529487019492509086019060010161474a565b509098975050505050505050565b6000602082840312156147d257600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561482b5761482b6147d9565b60405290565b6040516080810167ffffffffffffffff8111828210171561482b5761482b6147d9565b60405160c0810167ffffffffffffffff8111828210171561482b5761482b6147d9565b604051601f8201601f1916810167ffffffffffffffff811182821017156148a0576148a06147d9565b604052919050565b600067ffffffffffffffff8211156148c2576148c26147d9565b50601f01601f191660200190565b600082601f8301126148e157600080fd5b81356148f46148ef826148a8565b614877565b81815284602083860101111561490957600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561493857600080fd5b813567ffffffffffffffff81111561494f57600080fd5b614286848285016148d0565b602081526000610d1d60208301846146f7565b600067ffffffffffffffff821115614988576149886147d9565b5060051b60200190565b600082601f8301126149a357600080fd5b813560206149b36148ef8361496e565b82815260059290921b840181019181810190868411156149d257600080fd5b8286015b84811015614a6157803567ffffffffffffffff808211156149f75760008081fd5b8189019150604080601f19848d03011215614a125760008081fd5b614a1a614808565b8784013583811115614a2c5760008081fd5b614a3a8d8a838801016148d0565b8252509281013592614a4b84614657565b80880193909352505083529183019183016149d6565b509695505050505050565b600080600080848603610100811215614a8457600080fd5b853567ffffffffffffffff80821115614a9c57600080fd5b9087019060c0828a031215614ab057600080fd5b90955060209060a0601f1984011215614ac857600080fd5b818801955060c0880135925080831115614ae157600080fd5b614aed89848a01614992565b945060e0880135925080831115614b0357600080fd5b50818701915087601f830112614b1857600080fd5b8135614b266148ef8261496e565b81815260069190911b8301820190828101908a831115614b4557600080fd5b938301935b82851015614b88576040858c031215614b635760008081fd5b614b6b614808565b853581528486013585820152825260409094019390830190614b4a565b979a9699509497505050505050565b821515815260406020820152600061428660408301846146f7565b803560ff8116811461467757600080fd5b600080600060408486031215614bd857600080fd5b833567ffffffffffffffff811115614bef57600080fd5b614bfb8682870161458e565b9094509250614c0e905060208501614bb2565b90509250925092565b600080600060408486031215614c2c57600080fd5b8335614c3781614657565b9250602084013567ffffffffffffffff811115614c5357600080fd5b614c5f86828701614615565b9497909650939450505050565b60008060408385031215614c7f57600080fd5b50508035926020909101359150565b600060208284031215614ca057600080fd5b610d1d82614bb2565b600082601f830112614cba57600080fd5b81356020614cca6148ef8361496e565b82815260059290921b84018101918181019086841115614ce957600080fd5b8286015b84811015614a6157803567ffffffffffffffff80821115614d0e5760008081fd5b8189019150608080601f19848d03011215614d295760008081fd5b614d31614831565b87840135614d3e81614657565b815260408481013584811115614d545760008081fd5b614d628e8b838901016148d0565b838b01525060608581013591830191909152919093013590830152508352918301918301614ced565b600080600060608486031215614da057600080fd5b833567ffffffffffffffff811115614db757600080fd5b614dc386828701614ca9565b93505060208401359150604084013580151581146146c857600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060208284031215614e2157600080fd5b81356affffffffffffffffffffff81168114610d1d57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006000198203614e7e57614e7e614e3c565b5060010190565b60008085851115614e9557600080fd5b83861115614ea257600080fd5b5050820193919092039150565b7fffffffffffffffff0000000000000000000000000000000000000000000000008135818116916008851015614eef5780818660080360031b1b83161692505b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060208284031215614f3857600080fd5b813567ffffffffffffffff811115614f4f57600080fd5b61428684828501614992565b805160208083015191908110156111525760001960209190910360031b1b16919050565b7f5f5441524745545f50414e49434b45443a200000000000000000000000000000815260008251614fb78160128501602087016146d3565b9190910160120192915050565b6000614fd26148ef846148a8565b9050828152838383011115614fe657600080fd5b610d1d8360208301846146d3565b60006020828403121561500657600080fd5b815167ffffffffffffffff81111561501d57600080fd5b8201601f8101841361502e57600080fd5b61428684825160208401614fc4565b7f5f000000000000000000000000000000000000000000000000000000000000008152600082516150758160018501602087016146d3565b9190910160010192915050565b7f5f435553544f4d5f4552524f523a2000000000000000000000000000000000008152600082516150ba81600f8501602087016146d3565b91909101600f0192915050565b81810381811115612b0157612b01614e3c565b80820180821115612b0157612b01614e3c565b8082028115828204841417612b0157612b01614e3c565b600082516151168184602087016146d3565b7f2e20504152414d535f5241573a20000000000000000000000000000000000000920191825250600e01919050565b600083516151578184602088016146d3565b83519083019061516b8183602088016146d3565b01949350505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126151a957600080fd5b83018035915067ffffffffffffffff8211156151c457600080fd5b6020019150600581901b36038213156142cc57600080fd5b600060c082360312156151ee57600080fd5b6151f6614854565b823567ffffffffffffffff8082111561520e57600080fd5b61521a36838701614ca9565b83526020850135602084015260408501356040840152606085013560608401526152466080860161466c565b608084015260a085013591508082111561525f57600080fd5b5061526c368286016148d0565b60a08301525092915050565b600060a0828403121561528a57600080fd5b60405160a0810181811067ffffffffffffffff821117156152ad576152ad6147d9565b806040525082358152602083013560208201526040830135604082015260608301356060820152608083013560808201528091505092915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261531d57600080fd5b83018035915067ffffffffffffffff82111561533857600080fd5b6020019150368190038213156142cc57600080fd5b60006020828403121561535f57600080fd5b8135610d1d81614657565b600081518084526020808501945080840160005b838110156153a35781516001600160a01b03168752958201959082019060010161537e565b509495945050505050565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6040815260006153ec604083018661536a565b82810360208401526140c88185876153ae565b606081526000615412606083018761536a565b828103602084015261542481876146f7565b905082810360408401526154398185876153ae565b979650505050505050565b60006020828403121561545657600080fd5b8151610d1d81614657565b6001600160a01b03848116825260406020808401829052908301849052600091859160608501845b878110156154b057843561549c81614657565b841682529382019390820190600101615489565b5098975050505050505050565b600082516154cf8184602087016146d3565b9190910192915050565b6001600160a01b0384168152604060208201526000612e596040830184866153ae565b6001600160a01b0383168152604060208201526000614286604083018461536a565b6000806040838503121561553157600080fd5b82519150602083015161554381614657565b809150509250929050565b600082516155608184602087016146d3565b7f5f41564f5f5f4d4f4449464945445f53544f5241474500000000000000000000920191825250601601919050565b600082516155a18184602087016146d3565b7f5f41564f5f5f4e4f5f464c4153484c4f414e5f494e5f464c4153484c4f414e00920191825250601f01919050565b600082601f8301126155e157600080fd5b815160206155f16148ef8361496e565b82815260059290921b8401810191818101908684111561561057600080fd5b8286015b84811015614a6157805161562781614657565b8352918301918301615614565b600082601f83011261564557600080fd5b610d1d83835160208501614fc4565b600080600080600060a0868803121561566c57600080fd5b855167ffffffffffffffff8082111561568457600080fd5b61569089838a016155d0565b96506020915081880151818111156156a757600080fd5b8801601f81018a136156b857600080fd5b80516156c66148ef8261496e565b81815260059190911b8201840190848101908c8311156156e557600080fd5b928501925b82841015615703578351825292850192908501906156ea565b60408c015160608d0151919a50985094505050508082111561572457600080fd5b61573089838a01615634565b9350608088015191508082111561574657600080fd5b5061575388828901615634565b9150509295509295909350565b60608152600061577360608301866146f7565b90508360208301527fffffffff0000000000000000000000000000000000000000000000000000000083166040830152949350505050565b600082516157bd8184602087016146d3565b7f5f41564f5f5f494e56414c49445f49445f4f525f4f5045524154494f4e000000920191825250601d01919050565b600083516157fe8184602088016146d3565b9190910191825250602001919050565b81835260006020808501808196508560051b810191508460005b8781101561591e57828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8188360301811261586757600080fd5b87016080813561587681614657565b6001600160a01b0316865281870135368390037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe10181126158b657600080fd5b8201878101903567ffffffffffffffff8111156158d257600080fd5b8036038213156158e157600080fd5b82898901526158f383890182846153ae565b604085810135908a015260609485013594909801939093525050509784019790840190600101615828565b5091979650505050505050565b60808152600061593f60808301878961580e565b60208301959095525060408101929092527fffffffff000000000000000000000000000000000000000000000000000000001660609091015292915050565b600069ffffffffffffffffffff80831681810361599d5761599d614e3c565b6001019392505050565b6040815260006159bb60408301858761580e565b9050826020830152949350505050565b6000602082840312156159dd57600080fd5b815167ffffffffffffffff8111156159f457600080fd5b614286848285016155d0565b602081526000610d1d602083018461536a565b600081615a2257615a22614e3c565b506000190190565b82815260406020820152600061428660408301846146f7565b600060208284031215615a5557600080fd5b81517fffffffff0000000000000000000000000000000000000000000000000000000081168114610d1d57600080fd5b60008152600082516150758160018501602087016146d3565b7f600b5981380380925939f3000000000000000000000000000000000000000000815260008251615ad681600b8501602087016146d3565b91909101600b0192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122056ffe1aede88a9ceef845f067a7a2b08750fe3af8876247bec6db406ed00b04b64736f6c63430008120033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000779385ec7a04242259add4990e3130846f80ea6900000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc0068480000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d
-----Decoded View---------------
Arg [0] : avoRegistry_ (address): 0x779385EC7A04242259AdD4990e3130846f80eA69
Arg [1] : avoForwarder_ (address): 0x46978CD477A496028A18c02F07ab7F35EDBa5A54
Arg [2] : avoSignersList_ (address): 0xA27A71DD0348b5C69370eF9Da3743197DC006848
Arg [3] : avoConfigV1_ (address): 0x1412121E487478498b355C227c77D5ED6CF1798d
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000779385ec7a04242259add4990e3130846f80ea69
Arg [1] : 00000000000000000000000046978cd477a496028a18c02f07ab7f35edba5a54
Arg [2] : 000000000000000000000000a27a71dd0348b5c69370ef9da3743197dc006848
Arg [3] : 0000000000000000000000001412121e487478498b355c227c77d5ed6cf1798d
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.