Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 2 internal transactions
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 264691336 | 465 days ago | Contract Creation | 0 ETH | |||
| 264691336 | 465 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Safe7579
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import { IERC7579Account, Execution } from "./interfaces/IERC7579Account.sol";
import {
CallType,
ExecType,
ModeCode,
EXECTYPE_DEFAULT,
EXECTYPE_TRY,
CALLTYPE_SINGLE,
CALLTYPE_BATCH,
CALLTYPE_DELEGATECALL
} from "./lib/ModeLib.sol";
import { ExecutionLib } from "./lib/ExecutionLib.sol";
import {
IValidator,
MODULE_TYPE_VALIDATOR,
MODULE_TYPE_HOOK,
MODULE_TYPE_EXECUTOR,
MODULE_TYPE_FALLBACK
} from "erc7579/interfaces/IERC7579Module.sol";
import { ModuleInstallUtil } from "./utils/DCUtil.sol";
import { AccessControl } from "./core/AccessControl.sol";
import { Initializer } from "./core/Initializer.sol";
import { SafeOp } from "./core/SafeOp.sol";
import { ISafe } from "./interfaces/ISafe.sol";
import { ISafe7579 } from "./ISafe7579.sol";
import {
PackedUserOperation,
UserOperationLib
} from "@ERC4337/account-abstraction/contracts/core/UserOperationLib.sol";
import { _packValidationData } from "@ERC4337/account-abstraction/contracts/core/Helpers.sol";
import { IEntryPoint } from "@ERC4337/account-abstraction/contracts/interfaces/IEntryPoint.sol";
import { IERC1271 } from "./interfaces/IERC1271.sol";
import { SupportViewer } from "./core/SupportViewer.sol";
uint256 constant MULTITYPE_MODULE = 0;
/**
* @title ERC7579 Adapter for Safe accounts.
* creates full ERC7579 compliance to Safe accounts
* @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat)
* @dev This contract is a Safe account implementation that supports ERC7579 operations.
* In order to facilitate full ERC7579 compliance, the contract implements the IERC7579Account
* interface.
* This contract is an implementation of a Safe account supporting ERC7579 operations and complying
* with the IERC7579Account interface. It serves as a Safe FallbackHandler and module for Safe
* accounts, incorporating complex hacks to ensure ERC7579 compliance and requiring interactions and
* event emissions to be done via the SafeProxy as msg.sender using Safe's
* "executeTransactionFromModule" features.
*/
contract Safe7579 is ISafe7579, SafeOp, SupportViewer, AccessControl, Initializer {
using ExecutionLib for bytes;
bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH =
0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;
// keccak256("SafeMessage(bytes message)");
bytes32 private constant SAFE_MSG_TYPEHASH =
0x60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca;
// keccak256("safeSignature(bytes32,bytes32,bytes,bytes)");
bytes4 private constant SAFE_SIGNATURE_MAGIC_VALUE = 0x5fd7e97d;
/**
* @inheritdoc ISafe7579
*/
function execute(
ModeCode mode,
bytes calldata executionCalldata
)
external
withHook(IERC7579Account.execute.selector)
onlyEntryPointOrSelf
{
CallType callType;
ExecType execType;
// solhint-disable-next-line no-inline-assembly
assembly {
callType := mode
execType := shl(8, mode)
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* REVERT ON FAILED EXEC */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
ISafe safe = ISafe(msg.sender);
if (execType == EXECTYPE_DEFAULT) {
// DEFAULT EXEC & BATCH CALL
if (callType == CALLTYPE_BATCH) {
Execution[] calldata executions = executionCalldata.decodeBatch();
_exec(safe, executions);
}
// DEFAULT EXEC & SINGLE CALL
else if (callType == CALLTYPE_SINGLE) {
(address target, uint256 value, bytes calldata callData) =
executionCalldata.decodeSingle();
_exec(safe, target, value, callData);
}
// DEFAULT EXEC & DELEGATECALL
else if (callType == CALLTYPE_DELEGATECALL) {
address target = address(bytes20(executionCalldata[:20]));
bytes calldata callData = executionCalldata[20:];
_delegatecall(safe, target, callData);
}
// handle unsupported calltype
else {
revert UnsupportedCallType(callType);
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* TRY EXEC */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
else if (execType == EXECTYPE_TRY) {
// TRY EXEC & BATCH CALL
if (callType == CALLTYPE_BATCH) {
Execution[] calldata executions = executionCalldata.decodeBatch();
_tryExec(safe, executions);
}
// TRY EXEC & SINGLE CALL
else if (callType == CALLTYPE_SINGLE) {
(address target, uint256 value, bytes calldata callData) =
executionCalldata.decodeSingle();
_tryExec(safe, target, value, callData);
}
// TRY EXEC & DELEGATECALL
else if (callType == CALLTYPE_DELEGATECALL) {
address target = address(bytes20(executionCalldata[:20]));
bytes calldata callData = executionCalldata[20:];
_tryDelegatecall(safe, target, callData);
}
// handle unsupported calltype
else {
revert UnsupportedCallType(callType);
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HANDLE UNSUPPORTED EXEC TYPE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
else {
revert UnsupportedExecType(execType);
}
}
/**
* @inheritdoc ISafe7579
*/
function executeFromExecutor(
ModeCode mode,
bytes calldata executionCalldata
)
external
override
onlyExecutorModule
withHook(IERC7579Account.executeFromExecutor.selector)
withRegistry(_msgSender(), MODULE_TYPE_EXECUTOR)
returns (bytes[] memory returnDatas)
{
CallType callType;
ExecType execType;
// solhint-disable-next-line no-inline-assembly
assembly {
callType := mode
execType := shl(8, mode)
}
// using JUMPI to avoid stack too deep
return _executeReturn(execType, callType, executionCalldata);
}
/**
* Internal function that will be solely called by executeFromExecutor. Not super uniform code,
* but we need the JUMPI to avoid stack too deep, due to the modifiers in the
* executeFromExecutor function
*/
function _executeReturn(
ExecType execType,
CallType callType,
bytes calldata executionCalldata
)
private
returns (bytes[] memory returnDatas)
{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* REVERT ON FAILED EXEC */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
if (execType == EXECTYPE_DEFAULT) {
// DEFAULT EXEC & BATCH CALL
if (callType == CALLTYPE_BATCH) {
Execution[] calldata executions = executionCalldata.decodeBatch();
returnDatas = _execReturn(ISafe(msg.sender), executions);
}
// DEFAULT EXEC & SINGLE CALL
else if (callType == CALLTYPE_SINGLE) {
(address target, uint256 value, bytes calldata callData) =
executionCalldata.decodeSingle();
returnDatas = new bytes[](1);
returnDatas[0] = _execReturn(ISafe(msg.sender), target, value, callData);
}
// DEFAULT EXEC & DELEGATECALL
else if (callType == CALLTYPE_DELEGATECALL) {
address target = address(bytes20(executionCalldata[:20]));
bytes calldata callData = executionCalldata[20:];
returnDatas = new bytes[](1);
returnDatas[0] = _delegatecallReturn(ISafe(msg.sender), target, callData);
}
// handle unsupported calltype
else {
revert UnsupportedCallType(callType);
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* TRY EXEC */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
else if (execType == EXECTYPE_TRY) {
// TRY EXEC & BATCH CALL
if (callType == CALLTYPE_BATCH) {
Execution[] calldata executions = executionCalldata.decodeBatch();
(, returnDatas) = _tryExecReturn(ISafe(msg.sender), executions);
}
// TRY EXEC & SINGLE CALL
else if (callType == CALLTYPE_SINGLE) {
(address target, uint256 value, bytes calldata callData) =
executionCalldata.decodeSingle();
returnDatas = new bytes[](1);
returnDatas[0] = _tryExecReturn(ISafe(msg.sender), target, value, callData);
}
// TRY EXEC & DELEGATECALL
else if (callType == CALLTYPE_DELEGATECALL) {
address target = address(bytes20(executionCalldata[:20]));
bytes calldata callData = executionCalldata[20:];
returnDatas = new bytes[](1);
returnDatas[0] = _tryDelegatecallReturn(ISafe(msg.sender), target, callData);
}
// handle unsupported calltype
else {
revert UnsupportedCallType(callType);
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HANDLE UNSUPPORTED EXEC TYPE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
else {
revert UnsupportedExecType(execType);
}
}
/**
* @inheritdoc ISafe7579
*/
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
)
external
onlyEntryPoint
returns (uint256 validSignature)
{
address validator;
uint256 nonce = userOp.nonce;
// solhint-disable-next-line no-inline-assembly
assembly {
validator := shr(96, nonce)
}
// check if validator is enabled. If not, use Safe's checkSignatures()
if (validator == address(0) || !_isValidatorInstalled(validator)) {
validSignature = _validateSignatures(userOp);
} else {
// bubble up the return value of the validator module
bytes memory retData = _execReturn({
safe: ISafe(msg.sender),
target: validator,
value: 0,
callData: abi.encodeCall(IValidator.validateUserOp, (userOp, userOpHash))
});
validSignature = abi.decode(retData, (uint256));
}
// pay prefund
if (missingAccountFunds != 0) {
_exec({
safe: ISafe(msg.sender),
target: entryPoint(),
value: missingAccountFunds,
callData: ""
});
}
}
/**
* Function used as signature check fallback, if no valid validation module was selected.
* will use safe's ECDSA multisig. This code was copied of Safe's ERC4337 module
*/
function _validateSignatures(PackedUserOperation calldata userOp)
internal
view
returns (uint256 validationData)
{
(bytes memory operationData, uint48 validAfter, uint48 validUntil, bytes memory signatures)
= getSafeOp(userOp, entryPoint());
try ISafe((msg.sender)).checkSignatures(keccak256(operationData), operationData, signatures)
{
// The timestamps are validated by the entry point,
// therefore we will not check them again
validationData = _packValidationData({
sigFailed: false,
validUntil: validUntil,
validAfter: validAfter
});
} catch {
validationData = _packValidationData({
sigFailed: true,
validUntil: validUntil,
validAfter: validAfter
});
}
}
/**
* @inheritdoc ISafe7579
*/
function isValidSignature(
bytes32 hash,
bytes calldata data
)
external
view
returns (bytes4 magicValue)
{
ISafe safe = ISafe(msg.sender);
// check for safe's approved hashes
if (data.length == 0) {
bytes32 messageHash = keccak256(
EIP712.encodeMessageData(
safe.domainSeparator(),
SAFE_MSG_TYPEHASH,
abi.encode(keccak256(abi.encode(hash)))
)
);
require(safe.signedMessages(messageHash) != 0, "Hash not approved");
// return magic value
return IERC1271.isValidSignature.selector;
}
address validationModule = address(bytes20(data[:20]));
// If validation module with address(0) or no valid validator was provided,
// The signature validation mechanism falls back to Safe's checkSignatures() function
if (validationModule == address(0) || !_isValidatorInstalled(validationModule)) {
bytes memory messageData = EIP712.encodeMessageData(
safe.domainSeparator(), SAFE_MSG_TYPEHASH, abi.encode(keccak256(abi.encode(hash)))
);
bytes32 messageHash = keccak256(messageData);
safe.checkSignatures(messageHash, messageData, data[20:]);
return IERC1271.isValidSignature.selector;
}
// if a installed validator module was selected, use 7579 validation module
bytes memory ret = _staticcallReturn({
safe: ISafe(msg.sender),
target: validationModule,
callData: abi.encodeCall(
IValidator.isValidSignatureWithSender, (_msgSender(), hash, data[20:])
)
});
magicValue = abi.decode(ret, (bytes4));
}
/**
* @inheritdoc ISafe7579
*/
function installModule(
uint256 moduleType,
address module,
bytes calldata initData
)
external
override
withHook(IERC7579Account.installModule.selector)
onlyEntryPointOrSelf
{
// internal install functions will decode the initData param, and return sanitized
// moduleInitData. This is the initData that will be passed to Module.onInstall()
bytes memory moduleInitData;
if (moduleType == MODULE_TYPE_VALIDATOR) {
moduleInitData = _installValidator(module, initData);
} else if (moduleType == MODULE_TYPE_EXECUTOR) {
moduleInitData = _installExecutor(module, initData);
} else if (moduleType == MODULE_TYPE_FALLBACK) {
moduleInitData = _installFallbackHandler(module, initData);
} else if (moduleType == MODULE_TYPE_HOOK) {
moduleInitData = _installHook(module, initData);
} else if (moduleType == MULTITYPE_MODULE) {
moduleInitData = _multiTypeInstall(module, initData);
} else {
revert UnsupportedModuleType(moduleType);
}
// Initialize Module via Safe
_delegatecall({
safe: ISafe(msg.sender),
target: UTIL,
callData: abi.encodeCall(
ModuleInstallUtil.installModule, (moduleType, module, moduleInitData)
)
});
}
/**
* @inheritdoc ISafe7579
*/
function uninstallModule(
uint256 moduleType,
address module,
bytes calldata deInitData
)
external
override
tryWithHook(module, IERC7579Account.uninstallModule.selector)
onlyEntryPointOrSelf
{
// internal uninstall functions will decode the deInitData param, and return sanitized
// moduleDeInitData. This is the initData that will be passed to Module.onUninstall()
bytes memory moduleDeInitData;
if (moduleType == MODULE_TYPE_VALIDATOR) {
moduleDeInitData = _uninstallValidator(module, deInitData);
} else if (moduleType == MODULE_TYPE_EXECUTOR) {
moduleDeInitData = _uninstallExecutor(module, deInitData);
} else if (moduleType == MODULE_TYPE_FALLBACK) {
moduleDeInitData = _uninstallFallbackHandler(module, deInitData);
} else if (moduleType == MODULE_TYPE_HOOK) {
moduleDeInitData = _uninstallHook(module, deInitData);
} else if (moduleType == MULTITYPE_MODULE) {
moduleDeInitData = _multiTypeUninstall(module, deInitData);
} else {
revert UnsupportedModuleType(moduleType);
}
// Deinitialize Module via Safe.
// We are using "try" here, to avoid DoS. A module could revert in 'onUninstall' and prevent
// the account from removing the module
_tryDelegatecall({
safe: ISafe(msg.sender),
target: UTIL,
callData: abi.encodeCall(
ModuleInstallUtil.unInstallModule, (moduleType, module, moduleDeInitData)
)
});
}
/**
* @inheritdoc ISafe7579
*/
function isModuleInstalled(
uint256 moduleType,
address module,
bytes calldata additionalContext
)
external
view
returns (bool)
{
if (moduleType == MODULE_TYPE_VALIDATOR) {
// Safe7579 adapter allows for validator fallback to Safe's checkSignatures().
// It can thus be considered a valid validator module
if (module == msg.sender) return true;
return _isValidatorInstalled(module);
} else if (moduleType == MODULE_TYPE_EXECUTOR) {
return _isExecutorInstalled(module);
} else if (moduleType == MODULE_TYPE_FALLBACK) {
return _isFallbackHandlerInstalled(module, additionalContext);
} else if (moduleType == MODULE_TYPE_HOOK) {
return _isHookInstalled(module, additionalContext);
} else {
return false;
}
}
/**
* @inheritdoc ISafe7579
*/
function getNonce(address safe, address validator) external view returns (uint256 nonce) {
uint192 key = uint192(bytes24(bytes20(address(validator))));
nonce = IEntryPoint(entryPoint()).getNonce(safe, key);
}
}
library EIP712 {
function encodeMessageData(
bytes32 domainSeparator,
bytes32 typeHash,
bytes memory message
)
internal
pure
returns (bytes memory)
{
return abi.encodePacked(
bytes1(0x19),
bytes1(0x01),
domainSeparator,
keccak256(abi.encodePacked(typeHash, message))
);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;
/* solhint-disable no-inline-assembly */
/*
* For simulation purposes, validateUserOp (and validatePaymasterUserOp)
* must return this value in case of signature failure, instead of revert.
*/
uint256 constant SIG_VALIDATION_FAILED = 1;
/*
* For simulation purposes, validateUserOp (and validatePaymasterUserOp)
* return this value on success.
*/
uint256 constant SIG_VALIDATION_SUCCESS = 0;
/**
* Returned data from validateUserOp.
* validateUserOp returns a uint256, which is created by `_packedValidationData` and
* parsed by `_parseValidationData`.
* @param aggregator - address(0) - The account validated the signature by itself.
* address(1) - The account failed to validate the signature.
* otherwise - This is an address of a signature aggregator that must
* be used to validate the signature.
* @param validAfter - This UserOp is valid only after this timestamp.
* @param validaUntil - This UserOp is valid only up to this timestamp.
*/
struct ValidationData {
address aggregator;
uint48 validAfter;
uint48 validUntil;
}
/**
* Extract sigFailed, validAfter, validUntil.
* Also convert zero validUntil to type(uint48).max.
* @param validationData - The packed validation data.
*/
function _parseValidationData(
uint256 validationData
) pure returns (ValidationData memory data) {
address aggregator = address(uint160(validationData));
uint48 validUntil = uint48(validationData >> 160);
if (validUntil == 0) {
validUntil = type(uint48).max;
}
uint48 validAfter = uint48(validationData >> (48 + 160));
return ValidationData(aggregator, validAfter, validUntil);
}
/**
* Helper to pack the return value for validateUserOp.
* @param data - The ValidationData to pack.
*/
function _packValidationData(
ValidationData memory data
) pure returns (uint256) {
return
uint160(data.aggregator) |
(uint256(data.validUntil) << 160) |
(uint256(data.validAfter) << (160 + 48));
}
/**
* Helper to pack the return value for validateUserOp, when not using an aggregator.
* @param sigFailed - True for signature failure, false for success.
* @param validUntil - Last timestamp this UserOperation is valid (or zero for infinite).
* @param validAfter - First timestamp this UserOperation is valid.
*/
function _packValidationData(
bool sigFailed,
uint48 validUntil,
uint48 validAfter
) pure returns (uint256) {
return
(sigFailed ? 1 : 0) |
(uint256(validUntil) << 160) |
(uint256(validAfter) << (160 + 48));
}
/**
* keccak function over calldata.
* @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it.
*/
function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) {
assembly ("memory-safe") {
let mem := mload(0x40)
let len := data.length
calldatacopy(mem, data.offset, len)
ret := keccak256(mem, len)
}
}
/**
* The minimum of two numbers.
* @param a - First number.
* @param b - Second number.
*/
function min(uint256 a, uint256 b) pure returns (uint256) {
return a < b ? a : b;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;
/* solhint-disable no-inline-assembly */
import "../interfaces/PackedUserOperation.sol";
import {calldataKeccak, min} from "./Helpers.sol";
/**
* Utility functions helpful when working with UserOperation structs.
*/
library UserOperationLib {
uint256 public constant PAYMASTER_VALIDATION_GAS_OFFSET = 20;
uint256 public constant PAYMASTER_POSTOP_GAS_OFFSET = 36;
uint256 public constant PAYMASTER_DATA_OFFSET = 52;
/**
* Get sender from user operation data.
* @param userOp - The user operation data.
*/
function getSender(
PackedUserOperation calldata userOp
) internal pure returns (address) {
address data;
//read sender from userOp, which is first userOp member (saves 800 gas...)
assembly {
data := calldataload(userOp)
}
return address(uint160(data));
}
/**
* Relayer/block builder might submit the TX with higher priorityFee,
* but the user should not pay above what he signed for.
* @param userOp - The user operation data.
*/
function gasPrice(
PackedUserOperation calldata userOp
) internal view returns (uint256) {
unchecked {
(uint256 maxPriorityFeePerGas, uint256 maxFeePerGas) = unpackUints(userOp.gasFees);
if (maxFeePerGas == maxPriorityFeePerGas) {
//legacy mode (for networks that don't support basefee opcode)
return maxFeePerGas;
}
return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
}
}
/**
* Pack the user operation data into bytes for hashing.
* @param userOp - The user operation data.
*/
function encode(
PackedUserOperation calldata userOp
) internal pure returns (bytes memory ret) {
address sender = getSender(userOp);
uint256 nonce = userOp.nonce;
bytes32 hashInitCode = calldataKeccak(userOp.initCode);
bytes32 hashCallData = calldataKeccak(userOp.callData);
bytes32 accountGasLimits = userOp.accountGasLimits;
uint256 preVerificationGas = userOp.preVerificationGas;
bytes32 gasFees = userOp.gasFees;
bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
return abi.encode(
sender, nonce,
hashInitCode, hashCallData,
accountGasLimits, preVerificationGas, gasFees,
hashPaymasterAndData
);
}
function unpackUints(
bytes32 packed
) internal pure returns (uint256 high128, uint256 low128) {
return (uint128(bytes16(packed)), uint128(uint256(packed)));
}
//unpack just the high 128-bits from a packed value
function unpackHigh128(bytes32 packed) internal pure returns (uint256) {
return uint256(packed) >> 128;
}
// unpack just the low 128-bits from a packed value
function unpackLow128(bytes32 packed) internal pure returns (uint256) {
return uint128(uint256(packed));
}
function unpackMaxPriorityFeePerGas(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return unpackHigh128(userOp.gasFees);
}
function unpackMaxFeePerGas(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return unpackLow128(userOp.gasFees);
}
function unpackVerificationGasLimit(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return unpackHigh128(userOp.accountGasLimits);
}
function unpackCallGasLimit(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return unpackLow128(userOp.accountGasLimits);
}
function unpackPaymasterVerificationGasLimit(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_POSTOP_GAS_OFFSET]));
}
function unpackPostOpGasLimit(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET : PAYMASTER_DATA_OFFSET]));
}
function unpackPaymasterStaticFields(
bytes calldata paymasterAndData
) internal pure returns (address paymaster, uint256 validationGasLimit, uint256 postOpGasLimit) {
return (
address(bytes20(paymasterAndData[: PAYMASTER_VALIDATION_GAS_OFFSET])),
uint128(bytes16(paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_POSTOP_GAS_OFFSET])),
uint128(bytes16(paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET : PAYMASTER_DATA_OFFSET]))
);
}
/**
* Hash the user operation data.
* @param userOp - The user operation data.
*/
function hash(
PackedUserOperation calldata userOp
) internal pure returns (bytes32) {
return keccak256(encode(userOp));
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
import "./PackedUserOperation.sol";
/**
* Aggregated Signatures validator.
*/
interface IAggregator {
/**
* Validate aggregated signature.
* Revert if the aggregated signature does not match the given list of operations.
* @param userOps - Array of UserOperations to validate the signature for.
* @param signature - The aggregated signature.
*/
function validateSignatures(
PackedUserOperation[] calldata userOps,
bytes calldata signature
) external view;
/**
* Validate signature of a single userOp.
* This method should be called by bundler after EntryPointSimulation.simulateValidation() returns
* the aggregator this account uses.
* First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps.
* @param userOp - The userOperation received from the user.
* @return sigForUserOp - The value to put into the signature field of the userOp when calling handleOps.
* (usually empty, unless account and aggregator support some kind of "multisig".
*/
function validateUserOpSignature(
PackedUserOperation calldata userOp
) external view returns (bytes memory sigForUserOp);
/**
* Aggregate multiple signatures into a single value.
* This method is called off-chain to calculate the signature to pass with handleOps()
* bundler MAY use optimized custom code perform this aggregation.
* @param userOps - Array of UserOperations to collect the signatures from.
* @return aggregatedSignature - The aggregated signature.
*/
function aggregateSignatures(
PackedUserOperation[] calldata userOps
) external view returns (bytes memory aggregatedSignature);
}/**
** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
** Only one instance required on each chain.
**/
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */
import "./PackedUserOperation.sol";
import "./IStakeManager.sol";
import "./IAggregator.sol";
import "./INonceManager.sol";
interface IEntryPoint is IStakeManager, INonceManager {
/***
* An event emitted after each successful request.
* @param userOpHash - Unique identifier for the request (hash its entire content, except signature).
* @param sender - The account that generates this request.
* @param paymaster - If non-null, the paymaster that pays for this request.
* @param nonce - The nonce value from the request.
* @param success - True if the sender transaction succeeded, false if reverted.
* @param actualGasCost - Actual amount paid (by account or paymaster) for this UserOperation.
* @param actualGasUsed - Total gas used by this UserOperation (including preVerification, creation,
* validation and execution).
*/
event UserOperationEvent(
bytes32 indexed userOpHash,
address indexed sender,
address indexed paymaster,
uint256 nonce,
bool success,
uint256 actualGasCost,
uint256 actualGasUsed
);
/**
* Account "sender" was deployed.
* @param userOpHash - The userOp that deployed this account. UserOperationEvent will follow.
* @param sender - The account that is deployed
* @param factory - The factory used to deploy this account (in the initCode)
* @param paymaster - The paymaster used by this UserOp
*/
event AccountDeployed(
bytes32 indexed userOpHash,
address indexed sender,
address factory,
address paymaster
);
/**
* An event emitted if the UserOperation "callData" reverted with non-zero length.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
* @param revertReason - The return bytes from the (reverted) call to "callData".
*/
event UserOperationRevertReason(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce,
bytes revertReason
);
/**
* An event emitted if the UserOperation Paymaster's "postOp" call reverted with non-zero length.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
* @param revertReason - The return bytes from the (reverted) call to "callData".
*/
event PostOpRevertReason(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce,
bytes revertReason
);
/**
* UserOp consumed more than prefund. The UserOperation is reverted, and no refund is made.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
*/
event UserOperationPrefundTooLow(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce
);
/**
* An event emitted by handleOps(), before starting the execution loop.
* Any event emitted before this event, is part of the validation.
*/
event BeforeExecution();
/**
* Signature aggregator used by the following UserOperationEvents within this bundle.
* @param aggregator - The aggregator used for the following UserOperationEvents.
*/
event SignatureAggregatorChanged(address indexed aggregator);
/**
* A custom revert error of handleOps, to identify the offending op.
* Should be caught in off-chain handleOps simulation and not happen on-chain.
* Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts.
* NOTE: If simulateValidation passes successfully, there should be no reason for handleOps to fail on it.
* @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
* @param reason - Revert reason. The string starts with a unique code "AAmn",
* where "m" is "1" for factory, "2" for account and "3" for paymaster issues,
* so a failure can be attributed to the correct entity.
*/
error FailedOp(uint256 opIndex, string reason);
/**
* A custom revert error of handleOps, to report a revert by account or paymaster.
* @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
* @param reason - Revert reason. see FailedOp(uint256,string), above
* @param inner - data from inner cought revert reason
* @dev note that inner is truncated to 2048 bytes
*/
error FailedOpWithRevert(uint256 opIndex, string reason, bytes inner);
error PostOpReverted(bytes returnData);
/**
* Error case when a signature aggregator fails to verify the aggregated signature it had created.
* @param aggregator The aggregator that failed to verify the signature
*/
error SignatureValidationFailed(address aggregator);
// Return value of getSenderAddress.
error SenderAddressResult(address sender);
// UserOps handled, per aggregator.
struct UserOpsPerAggregator {
PackedUserOperation[] userOps;
// Aggregator address
IAggregator aggregator;
// Aggregated signature
bytes signature;
}
/**
* Execute a batch of UserOperations.
* No signature aggregator is used.
* If any account requires an aggregator (that is, it returned an aggregator when
* performing simulateValidation), then handleAggregatedOps() must be used instead.
* @param ops - The operations to execute.
* @param beneficiary - The address to receive the fees.
*/
function handleOps(
PackedUserOperation[] calldata ops,
address payable beneficiary
) external;
/**
* Execute a batch of UserOperation with Aggregators
* @param opsPerAggregator - The operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts).
* @param beneficiary - The address to receive the fees.
*/
function handleAggregatedOps(
UserOpsPerAggregator[] calldata opsPerAggregator,
address payable beneficiary
) external;
/**
* Generate a request Id - unique identifier for this request.
* The request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid.
* @param userOp - The user operation to generate the request ID for.
* @return hash the hash of this UserOperation
*/
function getUserOpHash(
PackedUserOperation calldata userOp
) external view returns (bytes32);
/**
* Gas and return values during simulation.
* @param preOpGas - The gas used for validation (including preValidationGas)
* @param prefund - The required prefund for this operation
* @param accountValidationData - returned validationData from account.
* @param paymasterValidationData - return validationData from paymaster.
* @param paymasterContext - Returned by validatePaymasterUserOp (to be passed into postOp)
*/
struct ReturnInfo {
uint256 preOpGas;
uint256 prefund;
uint256 accountValidationData;
uint256 paymasterValidationData;
bytes paymasterContext;
}
/**
* Returned aggregated signature info:
* The aggregator returned by the account, and its current stake.
*/
struct AggregatorStakeInfo {
address aggregator;
StakeInfo stakeInfo;
}
/**
* Get counterfactual sender address.
* Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation.
* This method always revert, and returns the address in SenderAddressResult error
* @param initCode - The constructor code to be passed into the UserOperation.
*/
function getSenderAddress(bytes memory initCode) external;
error DelegateAndRevert(bool success, bytes ret);
/**
* Helper method for dry-run testing.
* @dev calling this method, the EntryPoint will make a delegatecall to the given data, and report (via revert) the result.
* The method always revert, so is only useful off-chain for dry run calls, in cases where state-override to replace
* actual EntryPoint code is less convenient.
* @param target a target contract to make a delegatecall from entrypoint
* @param data data to pass to target in a delegatecall
*/
function delegateAndRevert(address target, bytes calldata data) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
interface INonceManager {
/**
* Return the next nonce for this sender.
* Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop)
* But UserOp with different keys can come with arbitrary order.
*
* @param sender the account address
* @param key the high 192 bit of the nonce
* @return nonce a full nonce to pass for next UserOp with this sender.
*/
function getNonce(address sender, uint192 key)
external view returns (uint256 nonce);
/**
* Manually increment the nonce of the sender.
* This method is exposed just for completeness..
* Account does NOT need to call it, neither during validation, nor elsewhere,
* as the EntryPoint will update the nonce regardless.
* Possible use-case is call it with various keys to "initialize" their nonces to one, so that future
* UserOperations will not pay extra for the first transaction with a given key.
*/
function incrementNonce(uint192 key) external;
}// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.7.5;
/**
* Manage deposits and stakes.
* Deposit is just a balance used to pay for UserOperations (either by a paymaster or an account).
* Stake is value locked for at least "unstakeDelay" by the staked entity.
*/
interface IStakeManager {
event Deposited(address indexed account, uint256 totalDeposit);
event Withdrawn(
address indexed account,
address withdrawAddress,
uint256 amount
);
// Emitted when stake or unstake delay are modified.
event StakeLocked(
address indexed account,
uint256 totalStaked,
uint256 unstakeDelaySec
);
// Emitted once a stake is scheduled for withdrawal.
event StakeUnlocked(address indexed account, uint256 withdrawTime);
event StakeWithdrawn(
address indexed account,
address withdrawAddress,
uint256 amount
);
/**
* @param deposit - The entity's deposit.
* @param staked - True if this entity is staked.
* @param stake - Actual amount of ether staked for this entity.
* @param unstakeDelaySec - Minimum delay to withdraw the stake.
* @param withdrawTime - First block timestamp where 'withdrawStake' will be callable, or zero if already locked.
* @dev Sizes were chosen so that deposit fits into one cell (used during handleOp)
* and the rest fit into a 2nd cell (used during stake/unstake)
* - 112 bit allows for 10^15 eth
* - 48 bit for full timestamp
* - 32 bit allows 150 years for unstake delay
*/
struct DepositInfo {
uint256 deposit;
bool staked;
uint112 stake;
uint32 unstakeDelaySec;
uint48 withdrawTime;
}
// API struct used by getStakeInfo and simulateValidation.
struct StakeInfo {
uint256 stake;
uint256 unstakeDelaySec;
}
/**
* Get deposit info.
* @param account - The account to query.
* @return info - Full deposit information of given account.
*/
function getDepositInfo(
address account
) external view returns (DepositInfo memory info);
/**
* Get account balance.
* @param account - The account to query.
* @return - The deposit (for gas payment) of the account.
*/
function balanceOf(address account) external view returns (uint256);
/**
* Add to the deposit of the given account.
* @param account - The account to add to.
*/
function depositTo(address account) external payable;
/**
* Add to the account's stake - amount and delay
* any pending unstake is first cancelled.
* @param _unstakeDelaySec - The new lock duration before the deposit can be withdrawn.
*/
function addStake(uint32 _unstakeDelaySec) external payable;
/**
* Attempt to unlock the stake.
* The value can be withdrawn (using withdrawStake) after the unstake delay.
*/
function unlockStake() external;
/**
* Withdraw from the (unlocked) stake.
* Must first call unlockStake and wait for the unstakeDelay to pass.
* @param withdrawAddress - The address to send withdrawn value.
*/
function withdrawStake(address payable withdrawAddress) external;
/**
* Withdraw from the deposit.
* @param withdrawAddress - The address to send withdrawn value.
* @param withdrawAmount - The amount to withdraw.
*/
function withdrawTo(
address payable withdrawAddress,
uint256 withdrawAmount
) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;
/**
* User Operation struct
* @param sender - The sender account of this request.
* @param nonce - Unique value the sender uses to verify it is not a replay.
* @param initCode - If set, the account contract will be created by this constructor/
* @param callData - The method call to execute on this account.
* @param accountGasLimits - Packed gas limits for validateUserOp and gas limit passed to the callData method call.
* @param preVerificationGas - Gas not calculated by the handleOps method, but added to the gas paid.
* Covers batch overhead.
* @param gasFees - packed gas fields maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters.
* @param paymasterAndData - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra data
* The paymaster will pay for the transaction instead of the sender.
* @param signature - Sender-verified signature over the entire request, the EntryPoint address and the chain ID.
*/
struct PackedUserOperation {
address sender;
uint256 nonce;
bytes initCode;
bytes callData;
bytes32 accountGasLimits;
uint256 preVerificationGas;
bytes32 gasFees;
bytes paymasterAndData;
bytes signature;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
address constant SENTINEL = address(0x1);
address constant ZERO_ADDRESS = address(0x0);
library SentinelListLib {
struct SentinelList {
mapping(address => address) entries;
}
error LinkedList_AlreadyInitialized();
error LinkedList_InvalidPage();
error LinkedList_InvalidEntry(address entry);
error LinkedList_EntryAlreadyInList(address entry);
function init(SentinelList storage self) internal {
if (alreadyInitialized(self)) revert LinkedList_AlreadyInitialized();
self.entries[SENTINEL] = SENTINEL;
}
function alreadyInitialized(SentinelList storage self) internal view returns (bool) {
return self.entries[SENTINEL] != ZERO_ADDRESS;
}
function getNext(SentinelList storage self, address entry) internal view returns (address) {
if (entry == ZERO_ADDRESS) {
revert LinkedList_InvalidEntry(entry);
}
return self.entries[entry];
}
function push(SentinelList storage self, address newEntry) internal {
if (newEntry == ZERO_ADDRESS || newEntry == SENTINEL) {
revert LinkedList_InvalidEntry(newEntry);
}
if (self.entries[newEntry] != ZERO_ADDRESS) revert LinkedList_EntryAlreadyInList(newEntry);
self.entries[newEntry] = self.entries[SENTINEL];
self.entries[SENTINEL] = newEntry;
}
function pop(SentinelList storage self, address prevEntry, address popEntry) internal {
if (popEntry == ZERO_ADDRESS || popEntry == SENTINEL) {
revert LinkedList_InvalidEntry(prevEntry);
}
if (self.entries[prevEntry] != popEntry) revert LinkedList_InvalidEntry(popEntry);
self.entries[prevEntry] = self.entries[popEntry];
self.entries[popEntry] = ZERO_ADDRESS;
}
function popAll(SentinelList storage self) internal {
address next = self.entries[SENTINEL];
while (next != ZERO_ADDRESS) {
address current = next;
next = self.entries[next];
self.entries[current] = ZERO_ADDRESS;
}
self.entries[SENTINEL] = ZERO_ADDRESS;
}
function contains(SentinelList storage self, address entry) internal view returns (bool) {
return SENTINEL != entry && self.entries[entry] != ZERO_ADDRESS;
}
function getEntriesPaginated(
SentinelList storage self,
address start,
uint256 pageSize
)
internal
view
returns (address[] memory array, address next)
{
if (start != SENTINEL && !contains(self, start)) revert LinkedList_InvalidEntry(start);
if (pageSize == 0) revert LinkedList_InvalidPage();
// Init array with max page size
array = new address[](pageSize);
// Populate return array
uint256 entryCount = 0;
next = self.entries[start];
while (next != ZERO_ADDRESS && next != SENTINEL && entryCount < pageSize) {
array[entryCount] = next;
next = self.entries[next];
entryCount++;
}
/**
* Because of the argument validation, we can assume that the loop will always iterate over
* the valid entry list values
* and the `next` variable will either be an enabled entry or a sentinel address
* (signalling the end).
*
* If we haven't reached the end inside the loop, we need to set the next pointer to
* the last element of the entry array
* because the `next` variable (which is a entry by itself) acting as a pointer to the
* start of the next page is neither
* incSENTINELrent page, nor will it be included in the next one if you pass it as a
* start.
*/
if (next != SENTINEL && entryCount > 0) {
next = array[entryCount - 1];
}
// Set correct size of returned array
// solhint-disable-next-line no-inline-assembly
/// @solidity memory-safe-assembly
assembly {
mstore(array, entryCount)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
address constant SENTINEL = address(0x1);
address constant ZERO_ADDRESS = address(0x0);
/**
* Implements a linked list, but adheres to ERC-4337 storage restrictions.
* Intended use: validator modules for modular ERC-4337 smart accounts
* @author kopy-kat | rhinestone.wtf
*/
library SentinelList4337Lib {
struct SentinelList {
mapping(address key => mapping(address account => address entry)) entries;
}
error LinkedList_AlreadyInitialized();
error LinkedList_InvalidPage();
error LinkedList_InvalidEntry(address entry);
error LinkedList_EntryAlreadyInList(address entry);
function init(SentinelList storage self, address account) internal {
if (alreadyInitialized(self, account)) revert LinkedList_AlreadyInitialized();
self.entries[SENTINEL][account] = SENTINEL;
}
function alreadyInitialized(
SentinelList storage self,
address account
)
internal
view
returns (bool)
{
return self.entries[SENTINEL][account] != ZERO_ADDRESS;
}
function getNext(
SentinelList storage self,
address account,
address entry
)
internal
view
returns (address)
{
if (entry == ZERO_ADDRESS) {
revert LinkedList_InvalidEntry(entry);
}
return self.entries[entry][account];
}
function push(SentinelList storage self, address account, address newEntry) internal {
if (newEntry == ZERO_ADDRESS || newEntry == SENTINEL) {
revert LinkedList_InvalidEntry(newEntry);
}
if (self.entries[newEntry][account] != ZERO_ADDRESS) {
revert LinkedList_EntryAlreadyInList(newEntry);
}
self.entries[newEntry][account] = self.entries[SENTINEL][account];
self.entries[SENTINEL][account] = newEntry;
}
function pop(
SentinelList storage self,
address account,
address prevEntry,
address popEntry
)
internal
{
if (popEntry == ZERO_ADDRESS || popEntry == SENTINEL) {
revert LinkedList_InvalidEntry(prevEntry);
}
if (self.entries[prevEntry][account] != popEntry) {
revert LinkedList_InvalidEntry(popEntry);
}
self.entries[prevEntry][account] = self.entries[popEntry][account];
self.entries[popEntry][account] = ZERO_ADDRESS;
}
function popAll(SentinelList storage self, address account) internal {
address next = self.entries[SENTINEL][account];
while (next != ZERO_ADDRESS) {
address current = next;
next = self.entries[next][account];
self.entries[current][account] = ZERO_ADDRESS;
}
self.entries[SENTINEL][account] = ZERO_ADDRESS;
}
function contains(
SentinelList storage self,
address account,
address entry
)
internal
view
returns (bool)
{
return SENTINEL != entry && self.entries[entry][account] != ZERO_ADDRESS;
}
function getEntriesPaginated(
SentinelList storage self,
address account,
address start,
uint256 pageSize
)
internal
view
returns (address[] memory array, address next)
{
if (start != SENTINEL && !contains(self, account, start)) {
revert LinkedList_InvalidEntry(start);
}
if (pageSize == 0) revert LinkedList_InvalidPage();
// Init array with max page size
array = new address[](pageSize);
// Populate return array
uint256 entryCount = 0;
next = self.entries[start][account];
while (next != ZERO_ADDRESS && next != SENTINEL && entryCount < pageSize) {
array[entryCount] = next;
next = self.entries[next][account];
entryCount++;
}
/**
* Because of the argument validation, we can assume that the loop will always iterate over
* the valid entry list values
* and the `next` variable will either be an enabled entry or a sentinel address
* (signalling the end).
*
* If we haven't reached the end inside the loop, we need to set the next pointer to
* the last element of the entry array
* because the `next` variable (which is a entry by itself) acting as a pointer to the
* start of the next page is neither
* incSENTINELrent page, nor will it be included in the next one if you pass it as a
* start.
*/
if (next != SENTINEL && entryCount > 0) {
next = array[entryCount - 1];
}
// Set correct size of returned array
// solhint-disable-next-line no-inline-assembly
/// @solidity memory-safe-assembly
assembly {
mstore(array, entryCount)
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Handler Context - Allows the fallback handler to extract addition context from the calldata
* @dev The fallback manager appends the following context to the calldata:
* 1. Fallback manager caller address (non-padded)
* based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/f8cc8b844a9f92f63dc55aa581f7d643a1bc5ac1/contracts/metatx/ERC2771Context.sol
* @author Richard Meissner - @rmeissner
*/
abstract contract HandlerContext {
/**
* @notice Allows fetching the original caller address.
* @dev This is only reliable in combination with a FallbackManager that supports this (e.g. Safe contract >=1.3.0).
* When using this functionality make sure that the linked _manager (aka msg.sender) supports this.
* This function does not rely on a trusted forwarder. Use the returned value only to
* check information against the calling manager.
* @return sender Original caller address.
*/
function _msgSender() internal pure returns (address sender) {
// The assembly code is more direct than the Solidity version using `abi.decode`.
// solhint-disable-next-line no-inline-assembly
assembly {
sender := shr(96, calldataload(sub(calldatasize(), 20)))
}
}
/**
* @notice Returns the FallbackManager address
* @return Fallback manager address
*/
function _manager() internal view returns (address) {
return msg.sender;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
/**
* @title reference implementation of the minimal modular smart account with Hook Extension
* @author zeroknots.eth | rhinestone.wtf
*/
contract AccountBase {
error AccountAccessUnauthorized();
/////////////////////////////////////////////////////
// Access Control
////////////////////////////////////////////////////
modifier onlyEntryPointOrSelf() virtual {
if (!(msg.sender == entryPoint() || msg.sender == address(this))) {
revert AccountAccessUnauthorized();
}
_;
}
modifier onlyEntryPoint() virtual {
if (msg.sender != entryPoint()) {
revert AccountAccessUnauthorized();
}
_;
}
function entryPoint() public view virtual returns (address) {
return 0x0000000071727De22E5E9d8BAf0edAc6f37da032;
}
/// @dev Sends to the EntryPoint (i.e. `msg.sender`) the missing funds for this transaction.
/// Subclass MAY override this modifier for better funds management.
/// (e.g. send to the EntryPoint more than the minimum required, so that in future transactions
/// it will not be required to send again)
///
/// `missingAccountFunds` is the minimum value this modifier should send the EntryPoint,
/// which MAY be zero, in case there is enough deposit, or the userOp has a paymaster.
modifier payPrefund(uint256 missingAccountFunds) virtual {
_;
/// @solidity memory-safe-assembly
assembly {
if missingAccountFunds {
// Ignore failure (it's EntryPoint's job to verify, not the account's).
pop(call(gas(), caller(), missingAccountFunds, codesize(), 0x00, codesize(), 0x00))
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/**
* @title Receiver
* @dev This contract receives safe-transferred ERC721 and ERC1155 tokens.
* @author Modified from Solady
* (https://github.com/Vectorized/solady/blob/main/src/accounts/Receiver.sol)
*/
abstract contract Receiver {
/// @dev For receiving ETH.
receive() external payable virtual { }
/// @dev Fallback function with the `receiverFallback` modifier.
fallback() external payable virtual receiverFallback { }
/// @dev Modifier for the fallback function to handle token callbacks.
modifier receiverFallback() virtual {
/// @solidity memory-safe-assembly
assembly {
let s := shr(224, calldataload(0))
// 0x150b7a02: `onERC721Received(address,address,uint256,bytes)`.
// 0xf23a6e61: `onERC1155Received(address,address,uint256,uint256,bytes)`.
// 0xbc197c81: `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
if or(eq(s, 0x150b7a02), or(eq(s, 0xf23a6e61), eq(s, 0xbc197c81))) {
mstore(0x20, s) // Store `msg.sig`.
return(0x3c, 0x20) // Return `msg.sig`.
}
}
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol";
uint256 constant VALIDATION_SUCCESS = 0;
uint256 constant VALIDATION_FAILED = 1;
uint256 constant MODULE_TYPE_VALIDATOR = 1;
uint256 constant MODULE_TYPE_EXECUTOR = 2;
uint256 constant MODULE_TYPE_FALLBACK = 3;
uint256 constant MODULE_TYPE_HOOK = 4;
interface IModule {
error AlreadyInitialized(address smartAccount);
error NotInitialized(address smartAccount);
/**
* @dev This function is called by the smart account during installation of the module
* @param data arbitrary data that may be required on the module during `onInstall`
* initialization
*
* MUST revert on error (i.e. if module is already enabled)
*/
function onInstall(bytes calldata data) external;
/**
* @dev This function is called by the smart account during uninstallation of the module
* @param data arbitrary data that may be required on the module during `onUninstall`
* de-initialization
*
* MUST revert on error
*/
function onUninstall(bytes calldata data) external;
/**
* @dev Returns boolean value if module is a certain type
* @param moduleTypeId the module type ID according the ERC-7579 spec
*
* MUST return true if the module is of the given type and false otherwise
*/
function isModuleType(uint256 moduleTypeId) external view returns (bool);
/**
* @dev Returns if the module was already initialized for a provided smartaccount
*/
function isInitialized(address smartAccount) external view returns (bool);
}
interface IValidator is IModule {
error InvalidTargetAddress(address target);
/**
* @dev Validates a transaction on behalf of the account.
* This function is intended to be called by the MSA during the ERC-4337 validaton phase
* Note: solely relying on bytes32 hash and signature is not suffcient for some
* validation implementations (i.e. SessionKeys often need access to userOp.calldata)
* @param userOp The user operation to be validated. The userOp MUST NOT contain any metadata.
* The MSA MUST clean up the userOp before sending it to the validator.
* @param userOpHash The hash of the user operation to be validated
* @return return value according to ERC-4337
*/
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
)
external
returns (uint256);
/**
* Validator can be used for ERC-1271 validation
*/
function isValidSignatureWithSender(
address sender,
bytes32 hash,
bytes calldata data
)
external
view
returns (bytes4);
}
interface IExecutor is IModule { }
interface IHook is IModule {
function preCheck(
address msgSender,
uint256 msgValue,
bytes calldata msgData
)
external
returns (bytes memory hookData);
function postCheck(bytes calldata hookData) external;
}
interface IFallback is IModule { }// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.25;
import { IERC7484 } from "./interfaces/IERC7484.sol";
import { CallType } from "./lib/ModeLib.sol";
struct FallbackHandler {
address handler;
CallType calltype;
}
enum HookType {
GLOBAL,
SIG
}
struct ModuleInit {
address module;
bytes initData;
}
struct RegistryInit {
IERC7484 registry;
address[] attesters;
uint8 threshold;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./DataTypes.sol";
import { IERC7579Account } from "./interfaces/IERC7579Account.sol";
import { ModeCode } from "./lib/ModeLib.sol";
import { PackedUserOperation } from
"@ERC4337/account-abstraction/contracts/core/UserOperationLib.sol";
import { ISafeOp } from "./interfaces/ISafeOp.sol";
/**
* @title ERC7579 Adapter for Safe accounts.
* creates full ERC7579 compliance to Safe accounts
* @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat)
*/
interface ISafe7579 is IERC7579Account, ISafeOp {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Validation */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/**
* ERC4337 v0.7 validation function
* @dev expects that a ERC7579 validator module is encoded within the UserOp nonce.
* if no validator module is provided, it will fallback to validate the transaction with
* Safe's signers
*/
function validateUserOp(
PackedUserOperation memory userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
)
external
returns (uint256 packedValidSig);
/**
* Will use Safe's signed messages or checkSignatures features or ERC7579 validation modules
* if no signature is provided, it makes use of Safe's signedMessages
* if address(0) or a non-installed validator module is provided, it will use Safe's
* checkSignatures
* if a valid validator module is provided, it will use the module's validateUserOp function
* @param hash message hash of ERC1271 request
* @param data abi.encodePacked(address validationModule, bytes signatures)
*/
function isValidSignature(
bytes32 hash,
bytes memory data
)
external
view
returns (bytes4 magicValue);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Executions */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/**
* @dev Executes a transaction on behalf of the Safe account.
* This function is intended to be called by ERC-4337 EntryPoint.sol
* @dev If a global hook and/or selector hook is set, it will be called
* @dev AccessControl: only Self of Entrypoint can install modules
* Safe7579 supports the following feature set:
* CallTypes:
* - CALLTYPE_SINGLE
* - CALLTYPE_BATCH
* - CALLTYPE_DELEGATECALL
* ExecTypes:
* - EXECTYPE_DEFAULT (revert if not successful)
* - EXECTYPE_TRY
* If a different mode is selected, this function will revert
* @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
* @param executionCalldata The encoded execution call data
*/
function execute(ModeCode mode, bytes memory executionCalldata) external;
/**
* @dev Executes a transaction on behalf of the Safe account.
* This function is intended to be called by executor modules
* @dev If a global hook and/or selector hook is set, it will be called
* @dev AccessControl: only enabled executor modules
* Safe7579 supports the following feature set:
* CallTypes:
* - CALLTYPE_SINGLE
* - CALLTYPE_BATCH
* - CALLTYPE_DELEGATECALL
* ExecTypes:
* - EXECTYPE_DEFAULT (revert if not successful)
* - EXECTYPE_TRY
* If a different mode is selected, this function will revert
* @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
* @param executionCalldata The encoded execution call data
*/
function executeFromExecutor(
ModeCode mode,
bytes memory executionCalldata
)
external
returns (bytes[] memory returnDatas);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Manage Modules */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/**
* Installs a 7579 Module of a certain type on the smart account
* @dev The module has to be initialized from msg.sender == SafeProxy, we thus use a
* delegatecall to DCUtil, which calls the onInstall/onUninstall function on the ERC7579
* module and emits the ModuleInstall/ModuleUnintall events
* @dev AccessControl: only Self of Entrypoint can install modules
* @dev If the safe set a registry, ERC7484 registry will be queried before installing
* @dev If a global hook and/or selector hook is set, it will be called
* @param moduleType the module type ID according the ERC-7579 spec
* Note: MULTITYPE_MODULE (uint(0)) is a special type to install a module with
* multiple types
* @param module the module address
* @param initData arbitrary data that may be required on the module during `onInstall`
* initialization.
*/
function installModule(uint256 moduleType, address module, bytes memory initData) external;
/**
* Uninstalls a Module of a certain type on the smart account.
* @dev The module has to be initialized from msg.sender == SafeProxy, we thus use a
* delegatecall to DCUtil, which calls the onInstall/onUninstall function on the ERC7579
* module and emits the ModuleInstall/ModuleUnintall events
* @dev AccessControl: only Self of Entrypoint can install modules
* @dev If a global hook and/or selector hook is set, it will be called
* @param moduleType the module type ID according the ERC-7579 spec
* @param module the module address
* @param deInitData arbitrary data that may be required on the module during `onUninstall`
* de-initialization.
*/
function uninstallModule(
uint256 moduleType,
address module,
bytes memory deInitData
)
external;
/**
* Function to check if the account has a certain module installed
* @param moduleType the module type ID according the ERC-7579 spec
* Note: keep in mind that some contracts can be multiple module types at the same time. It
* thus may be necessary to query multiple module types
* @param module the module address
* @param additionalContext additional context data that the smart account may interpret to
* identifiy conditions under which the module is installed.
* usually this is not necessary, but for some special hooks that
* are stored in mappings, this param might be needed
*/
function isModuleInstalled(
uint256 moduleType,
address module,
bytes memory additionalContext
)
external
view
returns (bool);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Initialize Safe7579 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/**
* This function can be called by the Launchpad.initSafe7579() or by already existing Safes that
* want to use Safe7579
* if this is called by the Launchpad, it is expected that launchpadValidators() was called
* previously, and the param validators is empty
* @param validators validator modules and initData
* @param executors executor modules and initData
* @param executors executor modules and initData
* @param fallbacks fallback modules and initData
* @param hooks hook module and initData
* @param registryInit (OPTIONAL) registry, attesters and threshold for IERC7484 Registry
* If not provided, the registry will be set to the zero address, and no
* registry checks will be performed
*/
function initializeAccount(
ModuleInit[] memory validators,
ModuleInit[] memory executors,
ModuleInit[] memory fallbacks,
ModuleInit[] memory hooks,
RegistryInit memory registryInit
)
external;
/**
* This function is intended to be called by Launchpad.validateUserOp()
* @dev it will initialize the SentinelList4337 list for validators, and sstore all
* validators
* @dev Since this function has to be 4337 compliant (storage access), only validator storage is acccess
* @dev Note: this function DOES NOT call onInstall() on the validator modules or emit
* ModuleInstalled events. this has to be done by the launchpad
*/
function initializeAccountWithValidators(ModuleInit[] memory validators) external;
/**
* Configure the Safe7579 with a IERC7484 registry
* @param registry IERC7484 registry
* @param attesters list of attesters
* @param threshold number of attesters required
*/
function setRegistry(IERC7484 registry, address[] memory attesters, uint8 threshold) external;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Query Account Details */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function getValidatorsPaginated(
address cursor,
uint256 pageSize
)
external
view
returns (address[] memory array, address next);
/**
* Get the current active global hook
*/
function getActiveHook() external view returns (address hook);
/**
* Get the current active selector hook
*/
function getActiveHook(bytes4 selector) external view returns (address hook);
function getExecutorsPaginated(
address cursor,
uint256 size
)
external
view
returns (address[] memory array, address next);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Query Misc */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/**
* Safe7579 is using validator selection encoding in the userop nonce.
* to make it easier for SDKs / devs to integrate, this function can be
* called to get the next nonce for a specific validator
* @param safe address of safe account
* @param validator ERC7579 validator to encode
*/
function getNonce(address safe, address validator) external view returns (uint256 nonce);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Custom Errors */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error InvalidModule(address module);
error InvalidModuleType(address module, uint256 moduleType);
// fallback handlers
error InvalidInput();
error InvalidCallType(CallType callType);
error NoFallbackHandler(bytes4 msgSig);
error InvalidFallbackHandler(bytes4 msgSig);
error FallbackInstalled(bytes4 msgSig);
// Hooks
error HookAlreadyInstalled(address currentHook);
error InvalidHookType();
// Registry Adapter
event ERC7484RegistryConfigured(address indexed smartAccount, IERC7484 indexed registry);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { HandlerContext } from "@safe-global/safe-contracts/contracts/handler/HandlerContext.sol";
import { AccountBase } from "erc7579/core/AccountBase.sol";
/**
* Implements AccessControl for Safe7579 adapter.
* Since Safe7579 Adapter is installed as a fallback handler on the safe account, we are making use
* of handlercontext (ERC2771)
* @author zeroknots.eth | rhinestone.wtf
*/
abstract contract AccessControl is HandlerContext, AccountBase {
modifier onlyEntryPointOrSelf() virtual override {
if (!(_msgSender() == entryPoint() || msg.sender == _msgSender())) {
revert AccountAccessUnauthorized();
}
_;
}
modifier onlyEntryPoint() virtual override {
if (_msgSender() != entryPoint()) {
revert AccountAccessUnauthorized();
}
_;
}
function entryPoint() public view virtual override returns (address) {
return 0x0000000071727De22E5E9d8BAf0edAc6f37da032;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { Safe7579DCUtil, Safe7579DCUtilSetup } from "./SetupDCUtil.sol";
import { BatchedExecUtil } from "../utils/DCUtil.sol";
import { Execution } from "../interfaces/IERC7579Account.sol";
import { ISafe } from "../interfaces/ISafe.sol";
/**
* Abstraction layer for executions.
* @dev All interactions with modules must originate from msg.sender == SafeProxy. This entails
* avoiding direct calls by the Safe7579 Adapter for actions like onInstall on modules or
* validateUserOp on validator modules, and utilizing the Safe's execTransactionFromModule feature
* instead.
* @dev Since Safe7579 offers features like TryExecute for batched executions, rewriting and
* verifying execution success across the codebase can be challenging and error-prone. These
* functions serve to interact with modules and external contracts.
*/
abstract contract ExecutionHelper is Safe7579DCUtilSetup {
event TryExecutionFailed(ISafe safe, uint256 numberInBatch);
event TryExecutionsFailed(ISafe safe, bool[] success);
error ExecutionFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EXEC - REVERT ON FAIL */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function _exec(ISafe safe, Execution[] calldata executions) internal {
_delegatecall({
safe: safe,
target: UTIL,
callData: abi.encodeCall(BatchedExecUtil.execute, executions)
});
}
function _exec(ISafe safe, address target, uint256 value, bytes memory callData) internal {
bool success = safe.execTransactionFromModule(target, value, callData, ISafe.Operation.Call);
if (!success) revert ExecutionFailed();
}
function _delegatecall(ISafe safe, address target, bytes memory callData) internal {
bool success =
safe.execTransactionFromModule(target, 0, callData, ISafe.Operation.DelegateCall);
if (!success) revert ExecutionFailed();
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EXEC - REVERT ON FAIL & Return Values */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/**
* Helper function to facilitate batched executions. Since Safe accounts do not support batched
* executions natively, we nudge the safe to delegatecall to ./utils/DCUTIL.sol, which then
* makes a multicall. This is to save on gas
*/
function _execReturn(
ISafe safe,
Execution[] calldata executions
)
internal
returns (bytes[] memory retDatas)
{
retDatas = abi.decode(
_delegatecallReturn({
safe: safe,
target: UTIL,
callData: abi.encodeCall(BatchedExecUtil.executeReturn, executions)
}),
(bytes[])
);
}
function _execReturn(
ISafe safe,
address target,
uint256 value,
bytes memory callData
)
internal
returns (bytes memory retData)
{
bool success;
(success, retData) =
safe.execTransactionFromModuleReturnData(target, value, callData, ISafe.Operation.Call);
if (!success) revert ExecutionFailed();
}
function _delegatecallReturn(
ISafe safe,
address target,
bytes memory callData
)
internal
returns (bytes memory retData)
{
bool success;
(success, retData) = safe.execTransactionFromModuleReturnData(
target, 0, callData, ISafe.Operation.DelegateCall
);
if (!success) revert ExecutionFailed();
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EXEC - TRY */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/**
* Helper function to facilitate batched executions. Since Safe accounts do not support batched
* executions natively, we nudge the safe to delegatecall to ./utils/DCUTIL.sol, which then
* makes a multicall. This is to save on gas
*/
function _tryExec(ISafe safe, Execution[] calldata executions) internal {
_tryDelegatecall({
safe: safe,
target: UTIL,
callData: abi.encodeCall(BatchedExecUtil.tryExecute, executions)
});
}
function _tryExec(ISafe safe, address target, uint256 value, bytes memory callData) internal {
bool success = safe.execTransactionFromModule(target, value, callData, ISafe.Operation.Call);
if (!success) emit TryExecutionFailed(safe, 0);
}
function _tryDelegatecall(ISafe safe, address target, bytes memory callData) internal {
bool success =
safe.execTransactionFromModule(target, 0, callData, ISafe.Operation.DelegateCall);
if (!success) emit TryExecutionFailed(safe, 0);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EXEC - TRY & Return Values */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/**
* Helper function to facilitate batched executions. Since Safe accounts do not support batched
* executions natively, we nudge the safe to delegatecall to ./utils/DCUTIL.sol, which then
* makes a multicall. This is to save on gas
*/
function _tryExecReturn(
ISafe safe,
Execution[] calldata executions
)
internal
returns (bool[] memory success, bytes[] memory retDatas)
{
bytes memory tmp = _tryDelegatecallReturn({
safe: safe,
target: UTIL,
callData: abi.encodeCall(BatchedExecUtil.tryExecuteReturn, executions)
});
(success, retDatas) = abi.decode(tmp, (bool[], bytes[]));
uint256 length = success.length;
for (uint256 i; i < length; i++) {
if (!success[i]) emit TryExecutionFailed(safe, i);
}
}
function _tryExecReturn(
ISafe safe,
address target,
uint256 value,
bytes memory callData
)
internal
returns (bytes memory retData)
{
bool success;
(success, retData) =
safe.execTransactionFromModuleReturnData(target, value, callData, ISafe.Operation.Call);
if (!success) emit TryExecutionFailed(safe, 0);
}
function _tryDelegatecallReturn(
ISafe safe,
address target,
bytes memory callData
)
internal
returns (bytes memory retData)
{
bool success;
(success, retData) = safe.execTransactionFromModuleReturnData(
target, 0, callData, ISafe.Operation.DelegateCall
);
if (!success) emit TryExecutionFailed(safe, 0);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STATICCALL TRICK */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/**
* Safe account does not natively implement Enum.Operation.StaticCall,
* using a trick with simulateAndRevert to execute a staticcall.
* @param safe Safe account to execute the staticcall
* @param target Target contract to staticcall
* @param callData Data to be passed to the target contract
*/
function _staticcallReturn(
ISafe safe,
address target,
bytes memory callData
)
internal
view
returns (bytes memory result)
{
bytes memory staticCallData = abi.encodeCall(Safe7579DCUtil.staticCall, (target, callData));
bytes memory simulationCallData =
abi.encodeCall(ISafe.simulateAndRevert, (address(UTIL), staticCallData));
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
pop(
staticcall(
gas(),
safe,
add(simulationCallData, 0x20),
mload(simulationCallData),
0x00,
0x20
)
)
let responseSize := sub(returndatasize(), 0x20)
result := mload(0x40)
mstore(0x40, add(result, responseSize))
returndatacopy(result, 0x20, responseSize)
if iszero(mload(0x00)) { revert(add(result, 0x20), mload(result)) }
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { ISafe7579 } from "../ISafe7579.sol";
import { ISafe } from "../interfaces/ISafe.sol";
import "../DataTypes.sol";
import { ModuleInstallUtil } from "../utils/DCUtil.sol";
import { ModuleManager } from "./ModuleManager.sol";
import {
MODULE_TYPE_VALIDATOR,
MODULE_TYPE_HOOK,
MODULE_TYPE_EXECUTOR,
MODULE_TYPE_FALLBACK
} from "erc7579/interfaces/IERC7579Module.sol";
import { IERC7484 } from "../interfaces/IERC7484.sol";
import { SentinelList4337Lib } from "sentinellist/SentinelList4337.sol";
import { SentinelListLib } from "sentinellist/SentinelList.sol";
/**
* Functions that can be used to initialze Safe7579 for a Safe Account
* @author zeroknots.eth | rhinestone.wtf
*/
abstract contract Initializer is ISafe7579, ModuleManager {
using SentinelList4337Lib for SentinelList4337Lib.SentinelList;
event Safe7579Initialized(address indexed safe);
error InvalidInitData(address safe);
/**
* @inheritdoc ISafe7579
*/
function initializeAccountWithValidators(ModuleInit[] calldata validators)
external
override
onlyEntryPointOrSelf
{
// this will revert if already initialized
$validators.init({ account: msg.sender });
uint256 length = validators.length;
for (uint256 i; i < length; i++) {
ModuleInit calldata validator = validators[i];
$validators.push({ account: msg.sender, newEntry: validator.module });
// @dev No events emitted here. Launchpad is expected to do this.
// at this point, the safeproxy singleton is not yet updated to the SafeSingleton
// calling execTransactionFromModule is not available yet.
}
emit Safe7579Initialized(msg.sender);
}
/**
* @inheritdoc ISafe7579
*/
function initializeAccount(
ModuleInit[] calldata validators,
ModuleInit[] calldata executors,
ModuleInit[] calldata fallbacks,
ModuleInit[] calldata hooks,
RegistryInit calldata registryInit
)
external
onlyEntryPointOrSelf
{
_configureRegistry(registryInit.registry, registryInit.attesters, registryInit.threshold);
// this will revert if already initialized
_initModules(validators, executors, fallbacks, hooks);
}
/**
* _initModules may be used via launchpad deploymet or directly by already deployed Safe
* accounts
*/
function _initModules(
ModuleInit[] calldata validators,
ModuleInit[] calldata executors,
ModuleInit[] calldata fallbacks,
ModuleInit[] calldata hooks
)
internal
{
bytes memory moduleInitData;
uint256 length = validators.length;
// if this function is called by the launchpad, validators will be initialized via
// launchpadValidators()
// to avoid double initialization, we check if the validators are already initialized
if (!$validators.alreadyInitialized({ account: msg.sender })) {
$validators.init({ account: msg.sender });
for (uint256 i; i < length; i++) {
ModuleInit calldata validator = validators[i];
// enable module on Safe7579, initialize module via Safe, emit events
moduleInitData = _installValidator(validator.module, validator.initData);
// Initialize Module via Safe
_delegatecall({
safe: ISafe(msg.sender),
target: UTIL,
callData: abi.encodeCall(
ModuleInstallUtil.installModule,
(MODULE_TYPE_VALIDATOR, validator.module, moduleInitData)
)
});
}
} else if (length != 0) {
revert InvalidInitData(msg.sender);
}
// this will revert if already initialized.
$executors.init({ account: msg.sender });
length = executors.length;
for (uint256 i; i < length; i++) {
ModuleInit calldata executor = executors[i];
// enable module on Safe7579, initialize module via Safe, emit events
moduleInitData = _installExecutor(executor.module, executor.initData);
// Initialize Module via Safe
_delegatecall({
safe: ISafe(msg.sender),
target: UTIL,
callData: abi.encodeCall(
ModuleInstallUtil.installModule,
(MODULE_TYPE_EXECUTOR, executor.module, moduleInitData)
)
});
}
length = fallbacks.length;
for (uint256 i; i < length; i++) {
ModuleInit calldata _fallback = fallbacks[i];
// enable module on Safe7579, initialize module via Safe, emit events
moduleInitData = _installFallbackHandler(_fallback.module, _fallback.initData);
// Initialize Module via Safe
_delegatecall({
safe: ISafe(msg.sender),
target: UTIL,
callData: abi.encodeCall(
ModuleInstallUtil.installModule,
(MODULE_TYPE_FALLBACK, _fallback.module, moduleInitData)
)
});
}
length = hooks.length;
for (uint256 i; i < length; i++) {
ModuleInit calldata hook = hooks[i];
// enable module on Safe7579, initialize module via Safe, emit events
moduleInitData = _installHook(hook.module, hook.initData);
// Initialize Module via Safe
_delegatecall({
safe: ISafe(msg.sender),
target: UTIL,
callData: abi.encodeCall(
ModuleInstallUtil.installModule, (MODULE_TYPE_HOOK, hook.module, moduleInitData)
)
});
}
emit Safe7579Initialized(msg.sender);
}
/**
* @inheritdoc ISafe7579
*/
function setRegistry(
IERC7484 registry,
address[] calldata attesters,
uint8 threshold
)
external
onlyEntryPointOrSelf
{
_configureRegistry(registry, attesters, threshold);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { SentinelListLib } from "sentinellist/SentinelList.sol";
import { SentinelList4337Lib } from "sentinellist/SentinelList4337.sol";
import { IModule, IHook } from "../interfaces/IERC7579Module.sol";
import { ISafe } from "../interfaces/ISafe.sol";
import { ISafe7579 } from "../ISafe7579.sol";
import "../DataTypes.sol";
import { RegistryAdapter } from "./RegistryAdapter.sol";
import { Receiver } from "erc7579/core/Receiver.sol";
import { AccessControl } from "./AccessControl.sol";
import { CallType, CALLTYPE_STATIC, CALLTYPE_SINGLE } from "../lib/ModeLib.sol";
import {
MODULE_TYPE_VALIDATOR,
MODULE_TYPE_EXECUTOR,
MODULE_TYPE_FALLBACK,
MODULE_TYPE_HOOK
} from "erc7579/interfaces/IERC7579Module.sol";
/**
* @title ModuleManager
* Contract that implements ERC7579 Module compatibility for Safe accounts
* @author zeroknots.eth | rhinestone.wtf
* @dev All Module types are handled within this
* contract. To make it a bit easier to read, the contract is split into different sections:
* - Validator Modules
* - Executor Modules
* - Fallback Modules
* - Hook Modules
* Note: the Storage mappings for each section, are not listed on the very top, but in the
* respective section
*/
abstract contract ModuleManager is ISafe7579, AccessControl, Receiver, RegistryAdapter {
using SentinelList4337Lib for SentinelList4337Lib.SentinelList;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* VALIDATOR MODULES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// No mapping account => list necessary. this sentinellist flavour handles associated storage to
// smart account itself to comply with 4337 storage restrictions
SentinelList4337Lib.SentinelList internal $validators;
/**
* install and initialize validator module
* @dev This function will install a validator module and return the moduleInitData
* @param validator address of the validator module
* @param data initialization data for the validator module
*/
function _installValidator(
address validator,
bytes calldata data
)
internal
withRegistry(validator, MODULE_TYPE_VALIDATOR)
returns (bytes memory moduleInitData)
{
$validators.push({ account: msg.sender, newEntry: validator });
return data;
}
/**
* Uninstall validator module
* @dev This function does not prevent the user from uninstalling all validator modules.
* Since the Safe7579 signature validation can fallback to Safe's checkSignature()
* function, it is okay, if all validator modules are removed.
* This does not brick the account
*/
function _uninstallValidator(
address validator,
bytes calldata data
)
internal
returns (bytes memory moduleInitData)
{
address prev;
(prev, moduleInitData) = abi.decode(data, (address, bytes));
$validators.pop({ account: msg.sender, prevEntry: prev, popEntry: validator });
}
/**
* Helper function that will calculate storage slot for
* validator address within the linked list in ValidatorStorageHelper
* and use Safe's getStorageAt() to read 32bytes from Safe's storage
*/
function _isValidatorInstalled(address validator)
internal
view
virtual
returns (bool isInstalled)
{
isInstalled = $validators.contains({ account: msg.sender, entry: validator });
}
/**
* Get paginated list of installed validators
*/
function getValidatorsPaginated(
address cursor,
uint256 pageSize
)
external
view
virtual
returns (address[] memory array, address next)
{
return $validators.getEntriesPaginated({
account: msg.sender,
start: cursor,
pageSize: pageSize
});
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EXECUTOR MODULES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
SentinelList4337Lib.SentinelList internal $executors;
modifier onlyExecutorModule() {
if (!_isExecutorInstalled(_msgSender())) revert InvalidModule(_msgSender());
_;
}
/**
* Install and initialize executor module
* @dev This function will install an executor module and return the moduleInitData
* @param executor address of the executor module
* @param data initialization data for the executor module
*/
function _installExecutor(
address executor,
bytes calldata data
)
internal
withRegistry(executor, MODULE_TYPE_EXECUTOR)
returns (bytes memory moduleInitData)
{
$executors.push({ account: msg.sender, newEntry: executor });
return data;
}
/**
* Uninstall executor module
* @dev This function will uninstall an executor module
* @param executor address of executor module to be uninstalled
* @param data abi encoded previous address and deinit data
*/
function _uninstallExecutor(
address executor,
bytes calldata data
)
internal
returns (bytes memory moduleDeInitData)
{
address prev;
(prev, moduleDeInitData) = abi.decode(data, (address, bytes));
$executors.pop({ account: msg.sender, prevEntry: prev, popEntry: executor });
}
function _isExecutorInstalled(address executor)
internal
view
virtual
returns (bool isInstalled)
{
isInstalled = $executors.contains({ account: msg.sender, entry: executor });
}
/**
* Get paginated list of installed executors
*/
function getExecutorsPaginated(
address cursor,
uint256 pageSize
)
external
view
virtual
returns (address[] memory array, address next)
{
return $executors.getEntriesPaginated({
account: msg.sender,
start: cursor,
pageSize: pageSize
});
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FALLBACK MODULES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
mapping(bytes4 selector => mapping(address smartAccount => FallbackHandler handlerConfig))
internal $fallbackStorage;
function _installFallbackHandler(
address handler,
bytes calldata params
)
internal
virtual
withRegistry(handler, MODULE_TYPE_FALLBACK)
returns (bytes memory moduleInitData)
{
(bytes4 functionSig, CallType calltype, bytes memory initData) =
abi.decode(params, (bytes4, CallType, bytes));
// disallow calls to onInstall or onUninstall.
// this could create a security issue
if (
functionSig == IModule.onInstall.selector || functionSig == IModule.onUninstall.selector
) revert InvalidFallbackHandler(functionSig);
// disallow unsupported calltypes
if (calltype != CALLTYPE_SINGLE && calltype != CALLTYPE_STATIC) {
revert InvalidCallType(calltype);
}
if (_isFallbackHandlerInstalled(functionSig)) revert FallbackInstalled(functionSig);
FallbackHandler storage $fallbacks = $fallbackStorage[functionSig][msg.sender];
$fallbacks.calltype = calltype;
$fallbacks.handler = handler;
return initData;
}
function _isFallbackHandlerInstalled(bytes4 functionSig) internal view virtual returns (bool) {
FallbackHandler storage $fallbacks = $fallbackStorage[functionSig][msg.sender];
return $fallbacks.handler != address(0);
}
function _uninstallFallbackHandler(
address, /*handler*/
bytes calldata context
)
internal
virtual
returns (bytes memory moduleDeInitData)
{
bytes4 functionSig;
(functionSig, moduleDeInitData) = abi.decode(context, (bytes4, bytes));
FallbackHandler storage $fallbacks = $fallbackStorage[functionSig][msg.sender];
delete $fallbacks.handler;
}
function _isFallbackHandlerInstalled(
address _handler,
bytes calldata additionalContext
)
internal
view
virtual
returns (bool)
{
bytes4 functionSig = abi.decode(additionalContext, (bytes4));
FallbackHandler storage $fallbacks = $fallbackStorage[functionSig][msg.sender];
return $fallbacks.handler == _handler;
}
/**
* @dev AccessControl: any external contract / EOA may call this function
* Safe7579 Fallback supports the following feature set:
* CallTypes:
* - CALLTYPE_SINGLE
* - CALLTYPE_BATCH
* @dev If a global hook and/or selector hook is set, it will be called
*/
// solhint-disable-next-line no-complex-fallback
fallback(bytes calldata callData)
external
payable
virtual
override(Receiver)
receiverFallback
withHook(msg.sig)
returns (bytes memory fallbackRet)
{
// using JUMPI to avoid stack too deep
return _callFallbackHandler(callData);
}
function _callFallbackHandler(bytes calldata callData)
private
returns (bytes memory fallbackRet)
{
// get handler for specific function selector
FallbackHandler storage $fallbacks = $fallbackStorage[msg.sig][msg.sender];
address handler = $fallbacks.handler;
CallType calltype = $fallbacks.calltype;
// if no handler is set for the msg.sig, revert
if (handler == address(0)) revert NoFallbackHandler(msg.sig);
// according to ERC7579, when calling to fallback modules, ERC2771 msg.sender has to be
// appended to the calldata, this allows fallback modules to implement
// authorization control
if (calltype == CALLTYPE_STATIC) {
return _staticcallReturn({
safe: ISafe(msg.sender),
target: handler,
callData: abi.encodePacked(callData, _msgSender()) // append ERC2771
});
}
if (calltype == CALLTYPE_SINGLE) {
return _execReturn({
safe: ISafe(msg.sender),
target: handler,
value: 0,
callData: abi.encodePacked(callData, _msgSender()) // append ERC2771
});
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HOOK MODULES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
mapping(address smartAccount => address globalHook) internal $globalHook;
mapping(bytes4 selector => mapping(address smartAccount => address hook)) internal $hookManager;
/**
* Run precheck hook for global and function selector specific
*/
function _preHooks(
address globalHook,
address sigHook
)
internal
returns (bytes memory global, bytes memory sig)
{
if (globalHook != address(0)) {
global = _execReturn({
safe: ISafe(msg.sender),
target: globalHook,
value: 0,
callData: abi.encodeCall(IHook.preCheck, (_msgSender(), msg.value, msg.data))
});
global = abi.decode(global, (bytes));
}
if (sigHook != address(0)) {
sig = _execReturn({
safe: ISafe(msg.sender),
target: sigHook,
value: 0,
callData: abi.encodeCall(IHook.preCheck, (_msgSender(), msg.value, msg.data))
});
sig = abi.decode(sig, (bytes));
}
}
/**
* Run post hooks (global and function sig)
*/
function _postHooks(
address globalHook,
address sigHook,
bytes memory global,
bytes memory sig
)
internal
{
if (globalHook != address(0)) {
_exec({
safe: ISafe(msg.sender),
target: globalHook,
value: 0,
callData: abi.encodeCall(IHook.postCheck, (global))
});
}
if (sigHook != address(0)) {
_exec({
safe: ISafe(msg.sender),
target: sigHook,
value: 0,
callData: abi.encodeCall(IHook.postCheck, (sig))
});
}
}
/**
* modifier that executes global hook, and function signature specific hook if enabled
*/
modifier withHook(bytes4 selector) {
address globalHook = $globalHook[msg.sender];
address sigHook = $hookManager[selector][msg.sender];
(bytes memory global, bytes memory sig) = _preHooks(globalHook, sigHook);
_;
_postHooks(globalHook, sigHook, global, sig);
}
modifier tryWithHook(address module, bytes4 selector) {
address globalHook = $globalHook[msg.sender];
address sigHook = $hookManager[selector][msg.sender];
if (module != globalHook && module != sigHook) {
(bytes memory global, bytes memory sig) = _preHooks(globalHook, sigHook);
_;
_postHooks(globalHook, sigHook, global, sig);
} else {
_;
}
}
/**
* Install and initialize hook module
* @dev This function will install a hook module and return the moduleInitData
* @param hook address of the hook module
* @param data initialization data for the hook module
*/
function _installHook(
address hook,
bytes calldata data
)
internal
virtual
withRegistry(hook, MODULE_TYPE_HOOK)
returns (bytes memory moduleInitData)
{
(HookType hookType, bytes4 selector, bytes memory initData) =
abi.decode(data, (HookType, bytes4, bytes));
address currentHook;
// handle global hooks
if (hookType == HookType.GLOBAL && selector == 0x0) {
currentHook = $globalHook[msg.sender];
// Dont allow hooks to be overwritten. If a hook is currently installed, it must be
// uninstalled first
if (currentHook != address(0)) {
revert HookAlreadyInstalled(currentHook);
}
$globalHook[msg.sender] = hook;
} else if (hookType == HookType.SIG) {
currentHook = $hookManager[selector][msg.sender];
// Dont allow hooks to be overwritten. If a hook is currently installed, it must be
// uninstalled first
if (currentHook != address(0)) {
revert HookAlreadyInstalled(currentHook);
}
$hookManager[selector][msg.sender] = hook;
} else {
revert InvalidHookType();
}
return initData;
}
function _uninstallHook(
address, /*hook*/
bytes calldata data
)
internal
virtual
returns (bytes memory moduleDeInitData)
{
HookType hookType;
bytes4 selector;
(hookType, selector, moduleDeInitData) = abi.decode(data, (HookType, bytes4, bytes));
if (hookType == HookType.GLOBAL && selector == 0x0) {
delete $globalHook[msg.sender];
} else if (hookType == HookType.SIG) {
delete $hookManager[selector][msg.sender];
} else {
revert InvalidHookType();
}
}
function _getCurrentHook(
HookType hookType,
bytes4 selector
)
internal
view
returns (address hook)
{
// handle global hooks
if (hookType == HookType.GLOBAL && selector == 0x0) {
hook = $globalHook[msg.sender];
}
if (hookType == HookType.SIG) {
hook = $hookManager[selector][msg.sender];
}
}
function _isHookInstalled(
address module,
bytes calldata context
)
internal
view
returns (bool)
{
(HookType hookType, bytes4 selector) = abi.decode(context, (HookType, bytes4));
address hook = _getCurrentHook({ hookType: hookType, selector: selector });
return hook == module;
}
function getActiveHook(bytes4 selector) public view returns (address hook) {
return $hookManager[selector][msg.sender];
}
function getActiveHook() public view returns (address hook) {
return $globalHook[msg.sender];
}
// solhint-disable-next-line code-complexity
/**
* To make it easier to install multiple modules at once, this function will
* install multiple modules at once. The init data is expected to be a abi encoded tuple
* of (uint[] types, bytes[] contexts, bytes moduleInitData)
* @dev Install multiple modules at once
* @param module address of the module
* @param initData initialization data for the module
*/
function _multiTypeInstall(
address module,
bytes calldata initData
)
internal
returns (bytes memory _moduleInitData)
{
uint256[] calldata types;
bytes[] calldata contexts;
bytes calldata moduleInitData;
// equivalent of:
// (types, contexs, moduleInitData) = abi.decode(initData,(uint[],bytes[],bytes)
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
let offset := initData.offset
let baseOffset := offset
let dataPointer := add(baseOffset, calldataload(offset))
types.offset := add(dataPointer, 32)
types.length := calldataload(dataPointer)
offset := add(offset, 32)
dataPointer := add(baseOffset, calldataload(offset))
contexts.offset := add(dataPointer, 32)
contexts.length := calldataload(dataPointer)
offset := add(offset, 32)
dataPointer := add(baseOffset, calldataload(offset))
moduleInitData.offset := add(dataPointer, 32)
moduleInitData.length := calldataload(dataPointer)
}
uint256 length = types.length;
if (contexts.length != length) revert InvalidInput();
// iterate over all module types and install the module as a type accordingly
for (uint256 i; i < length; i++) {
uint256 _type = types[i];
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INSTALL VALIDATORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
if (_type == MODULE_TYPE_VALIDATOR) {
_installValidator(module, contexts[i]);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INSTALL EXECUTORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
else if (_type == MODULE_TYPE_EXECUTOR) {
_installExecutor(module, contexts[i]);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INSTALL FALLBACK */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
else if (_type == MODULE_TYPE_FALLBACK) {
_installFallbackHandler(module, contexts[i]);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INSTALL HOOK (global or sig specific) */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
else if (_type == MODULE_TYPE_HOOK) {
_installHook(module, contexts[i]);
} else {
revert InvalidModuleType(module, _type);
}
}
// memory allocate the moduleInitData to return. This data should be used by the caller to
// initialize the module
_moduleInitData = moduleInitData;
}
function _multiTypeUninstall(
address module,
bytes calldata initData
)
internal
returns (bytes memory _moduleDeInitData)
{
uint256[] calldata types;
bytes[] calldata contexts;
bytes calldata moduleDeInitData;
// equivalent of:
// (types, contexs, moduleInitData) = abi.decode(initData,(uint[],bytes[],bytes)
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
let offset := initData.offset
let baseOffset := offset
let dataPointer := add(baseOffset, calldataload(offset))
types.offset := add(dataPointer, 32)
types.length := calldataload(dataPointer)
offset := add(offset, 32)
dataPointer := add(baseOffset, calldataload(offset))
contexts.offset := add(dataPointer, 32)
contexts.length := calldataload(dataPointer)
offset := add(offset, 32)
dataPointer := add(baseOffset, calldataload(offset))
moduleDeInitData.offset := add(dataPointer, 32)
moduleDeInitData.length := calldataload(dataPointer)
}
uint256 length = types.length;
if (contexts.length != length) revert InvalidInput();
// iterate over all module types and install the module as a type accordingly
for (uint256 i; i < length; i++) {
uint256 _type = types[i];
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INSTALL VALIDATORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
if (_type == MODULE_TYPE_VALIDATOR) {
_uninstallValidator(module, contexts[i]);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INSTALL EXECUTORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
else if (_type == MODULE_TYPE_EXECUTOR) {
_uninstallExecutor(module, contexts[i]);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INSTALL FALLBACK */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
else if (_type == MODULE_TYPE_FALLBACK) {
_uninstallFallbackHandler(module, contexts[i]);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INSTALL HOOK (global or sig specific) */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
else if (_type == MODULE_TYPE_HOOK) {
_uninstallHook(module, contexts[i]);
} else {
revert InvalidModuleType(module, _type);
}
}
// memory allocate the moduleInitData to return. This data should be used by the caller to
// initialize the module
_moduleDeInitData = moduleDeInitData;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { IERC7484 } from "../interfaces/IERC7484.sol";
import { ExecutionHelper } from "./ExecutionHelper.sol";
import { ISafe } from "../interfaces/ISafe.sol";
import { ISafe7579 } from "../ISafe7579.sol";
/**
* IERC7484 Registry adapter.
* this feature is opt-in. The smart account owner can choose to use the registry and which
* attesters to trust.
* @author zeroknots.eth | rhinestone.wtf
*/
abstract contract RegistryAdapter is ISafe7579, ExecutionHelper {
mapping(address smartAccount => IERC7484 registry) internal $registry;
modifier withRegistry(address module, uint256 moduleType) {
_checkRegistry(module, moduleType);
_;
}
/**
* Check on ERC7484 Registry, if suffcient attestations were made
* This will revert, if not succicient valid attestations are on the registry
*/
function _checkRegistry(address module, uint256 moduleType) internal view {
IERC7484 registry = $registry[msg.sender];
if (address(registry) != address(0)) {
// this will revert if attestations / threshold are not met
registry.checkForAccount(msg.sender, module, moduleType);
}
}
/**
* Configure ERC7484 Registry for Safe
*/
function _configureRegistry(
IERC7484 registry,
address[] calldata attesters,
uint8 threshold
)
internal
{
// sstore value in any case, as this function may be used to disable the use of registry
$registry[msg.sender] = registry;
// registry is an opt in feature for Safe7579. if set, configure trusted attesters
if (registry != IERC7484(address(0))) {
_exec({
safe: ISafe(msg.sender),
target: address(registry),
value: 0,
callData: abi.encodeCall(IERC7484.trustAttesters, (threshold, attesters))
});
}
emit ERC7484RegistryConfigured(msg.sender, registry);
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {
PackedUserOperation,
UserOperationLib
} from "@ERC4337/account-abstraction/contracts/core/UserOperationLib.sol";
import { SAFE_OP_TYPEHASH, ISafeOp } from "../interfaces/ISafeOp.sol";
abstract contract SafeOp is ISafeOp {
using UserOperationLib for PackedUserOperation;
bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH =
0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;
/**
* @dev Decodes an ERC-4337 user operation into a Safe operation.
* @param userOp The ERC-4337 user operation.
* @return operationData Encoded EIP-712 Safe operation data bytes used for signature
* verification.
* @return validAfter The timestamp the user operation is valid from.
* @return validUntil The timestamp the user operation is valid until.
* @return signatures The Safe owner signatures extracted from the user operation.
*/
function getSafeOp(
PackedUserOperation calldata userOp,
address entryPoint
)
public
view
returns (
bytes memory operationData,
uint48 validAfter,
uint48 validUntil,
bytes memory signatures
)
{
// Extract additional Safe operation fields from the user operation signature which is
// encoded as:
// `abi.encodePacked(validAfter, validUntil, signatures)`
{
bytes calldata sig = userOp.signature;
validAfter = uint48(bytes6(sig[0:6]));
validUntil = uint48(bytes6(sig[6:12]));
signatures = sig[12:];
}
// It is important that **all** user operation fields are represented in the `SafeOp` data
// somehow, to prevent
// user operations from being submitted that do not fully respect the user preferences. The
// only exception is
// the `signature` bytes. Note that even `initCode` needs to be represented in the operation
// data, otherwise
// it can be replaced with a more expensive initialization that would charge the user
// additional fees.
{
// In order to work around Solidity "stack too deep" errors related to too many stack
// variables, manually
// encode the `SafeOp` fields into a memory `struct` for computing the EIP-712
// struct-hash. This works
// because the `EncodedSafeOpStruct` struct has no "dynamic" fields so its memory layout
// is identical to the
// result of `abi.encode`-ing the individual fields.
EncodedSafeOpStruct memory encodedSafeOp = EncodedSafeOpStruct({
typeHash: SAFE_OP_TYPEHASH,
safe: userOp.sender,
nonce: userOp.nonce,
initCodeHash: keccak256(userOp.initCode),
callDataHash: keccak256(userOp.callData),
verificationGasLimit: uint128(userOp.unpackVerificationGasLimit()),
callGasLimit: uint128(userOp.unpackCallGasLimit()),
preVerificationGas: userOp.preVerificationGas,
maxPriorityFeePerGas: uint128(userOp.unpackMaxPriorityFeePerGas()),
maxFeePerGas: uint128(userOp.unpackMaxFeePerGas()),
paymasterAndDataHash: keccak256(userOp.paymasterAndData),
validAfter: validAfter,
validUntil: validUntil,
entryPoint: entryPoint
});
bytes32 safeOpStructHash;
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
// Since the `encodedSafeOp` value's memory layout is identical to the result of
// `abi.encode`-ing the
// individual `SafeOp` fields, we can pass it directly to `keccak256`. Additionally,
// there are 14
// 32-byte fields to hash, for a length of `14 * 32 = 448` bytes.
safeOpStructHash := keccak256(encodedSafeOp, 448)
}
operationData =
abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator(), safeOpStructHash);
}
}
function domainSeparator() public view returns (bytes32) {
return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, block.chainid, this));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { Safe7579DCUtil } from "../utils/DCUtil.sol";
/**
* Deployes Safe7579DCUtil
*/
abstract contract Safe7579DCUtilSetup {
address internal immutable UTIL;
constructor() {
UTIL = address(new Safe7579DCUtil());
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
import {
CallType,
ExecType,
ModeCode,
EXECTYPE_DEFAULT,
EXECTYPE_TRY,
CALLTYPE_SINGLE,
CALLTYPE_BATCH,
CALLTYPE_DELEGATECALL
} from "../lib/ModeLib.sol";
import {
MODULE_TYPE_VALIDATOR,
MODULE_TYPE_HOOK,
MODULE_TYPE_EXECUTOR,
MODULE_TYPE_FALLBACK
} from "erc7579/interfaces/IERC7579Module.sol";
import { IERC7579AccountView } from "../interfaces/IERC7579Account.sol";
abstract contract SupportViewer is IERC7579AccountView {
function accountId() external pure returns (string memory accountImplementationId) {
return "rhinestone.safe7579.v1.0.0";
}
function supportsExecutionMode(ModeCode encodedMode) external pure returns (bool supported) {
CallType callType;
ExecType execType;
// solhint-disable-next-line no-inline-assembly
assembly {
callType := encodedMode
execType := shl(8, encodedMode)
}
if (callType == CALLTYPE_BATCH) supported = true;
else if (callType == CALLTYPE_SINGLE) supported = true;
else if (callType == CALLTYPE_DELEGATECALL) supported = true;
else return false;
if (supported && execType == EXECTYPE_DEFAULT) return supported;
else if (supported && execType == EXECTYPE_TRY) return supported;
else return false;
}
function supportsModule(uint256 moduleTypeId) external pure returns (bool) {
if (moduleTypeId == MODULE_TYPE_VALIDATOR) return true;
else if (moduleTypeId == MODULE_TYPE_EXECUTOR) return true;
else if (moduleTypeId == MODULE_TYPE_FALLBACK) return true;
else if (moduleTypeId == MODULE_TYPE_HOOK) return true;
else return false;
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.20;
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param _dataHash Arbitrary length data signed on behalf of address(this)
* @param _signature Signature byte array associated with _data
*
* MUST return the bytes4 magic value 0x1626ba7e when function passes.
* MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc >
* 0.5)
* MUST allow external calls
*/
function isValidSignature(
bytes32 _dataHash,
bytes calldata _signature
)
external
view
returns (bytes4);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC7484 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Check with Registry internal attesters */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function check(address module) external view;
function checkForAccount(address smartAccount, address module) external view;
function check(address module, uint256 moduleType) external view;
function checkForAccount(
address smartAccount,
address module,
uint256 moduleType
)
external
view;
/**
* Allows Smart Accounts - the end users of the registry - to appoint
* one or many attesters as trusted.
* @dev this function reverts, if address(0), or duplicates are provided in attesters[]
*
* @param threshold The minimum number of attestations required for a module
* to be considered secure.
* @param attesters The addresses of the attesters to be trusted.
*/
function trustAttesters(uint8 threshold, address[] calldata attesters) external;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Check with external attester(s) */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function check(address module, address[] calldata attesters, uint256 threshold) external view;
function check(
address module,
uint256 moduleType,
address[] calldata attesters,
uint256 threshold
)
external
view;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { CallType, ExecType, ModeCode } from "../lib/ModeLib.sol";
struct Execution {
address target;
uint256 value;
bytes callData;
}
interface IERC7579AccountEvents {
event ModuleInstalled(uint256 moduleTypeId, address module);
event ModuleUninstalled(uint256 moduleTypeId, address module);
}
interface IERC7579AccountView {
/**
* @dev Returns the account id of the smart account
* @return accountImplementationId the account id of the smart account
* the accountId should be structured like so:
* "vendorname.accountname.semver"
*/
function accountId() external view returns (string memory accountImplementationId);
/**
* Function to check if the account supports a certain CallType or ExecType (see ModeLib.sol)
* @param encodedMode the encoded mode
*/
function supportsExecutionMode(ModeCode encodedMode) external view returns (bool);
/**
* Function to check if the account supports installation of a certain module type Id
* @param moduleTypeId the module type ID according the ERC-7579 spec
*/
function supportsModule(uint256 moduleTypeId) external view returns (bool);
}
interface IERC7579Account is IERC7579AccountEvents, IERC7579AccountView {
// Error thrown when an unsupported ModuleType is requested
error UnsupportedModuleType(uint256 moduleTypeId);
// Error thrown when an execution with an unsupported CallType was made
error UnsupportedCallType(CallType callType);
// Error thrown when an execution with an unsupported ExecType was made
error UnsupportedExecType(ExecType execType);
/**
* @dev Executes a transaction on behalf of the account.
* This function is intended to be called by ERC-4337 EntryPoint.sol
* @dev Ensure adequate authorization control: i.e. onlyEntryPointOrSelf
*
* @dev MSA MUST implement this function signature.
* If a mode is requested that is not supported by the Account, it MUST revert
* @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
* @param executionCalldata The encoded execution call data
*/
function execute(ModeCode mode, bytes calldata executionCalldata) external;
/**
* @dev Executes a transaction on behalf of the account.
* This function is intended to be called by Executor Modules
* @dev Ensure adequate authorization control: i.e. onlyExecutorModule
*
* @dev MSA MUST implement this function signature.
* If a mode is requested that is not supported by the Account, it MUST revert
* @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
* @param executionCalldata The encoded execution call data
*/
function executeFromExecutor(
ModeCode mode,
bytes calldata executionCalldata
)
external
returns (bytes[] memory returnData);
/**
* @dev ERC-1271 isValidSignature
* This function is intended to be used to validate a smart account signature
* and may forward the call to a validator module
*
* @param hash The hash of the data that is signed
* @param data The data that is signed
*/
function isValidSignature(bytes32 hash, bytes calldata data) external returns (bytes4);
/**
* @dev installs a Module of a certain type on the smart account
* @dev Implement Authorization control of your chosing
* @param moduleTypeId the module type ID according the ERC-7579 spec
* @param module the module address
* @param initData arbitrary data that may be required on the module during `onInstall`
* initialization.
*/
function installModule(
uint256 moduleTypeId,
address module,
bytes calldata initData
)
external;
/**
* @dev uninstalls a Module of a certain type on the smart account
* @dev Implement Authorization control of your chosing
* @param moduleTypeId the module type ID according the ERC-7579 spec
* @param module the module address
* @param deInitData arbitrary data that may be required on the module during `onUninstall`
* de-initialization.
*/
function uninstallModule(
uint256 moduleTypeId,
address module,
bytes calldata deInitData
)
external;
/**
* Function to check if the account has a certain module installed
* @param moduleTypeId the module type ID according the ERC-7579 spec
* Note: keep in mind that some contracts can be multiple module types at the same time. It
* thus may be necessary to query multiple module types
* @param module the module address
* @param additionalContext additional context data that the smart account may interpret to
* identifiy conditions under which the module is installed.
* usually this is not necessary, but for some special hooks that
* are stored in mappings, this param might be needed
*/
function isModuleInstalled(
uint256 moduleTypeId,
address module,
bytes calldata additionalContext
)
external
view
returns (bool);
}pragma solidity ^0.8.20;
import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol";
uint256 constant VALIDATION_SUCCESS = 0;
uint256 constant VALIDATION_FAILED = 1;
uint256 constant MODULE_TYPE_VALIDATOR = 1;
uint256 constant MODULE_TYPE_EXECUTOR = 2;
uint256 constant MODULE_TYPE_FALLBACK = 3;
uint256 constant MODULE_TYPE_HOOK = 4;
interface IModule {
/**
* @dev This function is called by the smart account during installation of the module
* @param data arbitrary data that may be required on the module during `onInstall`
* initialization
*
* MUST revert on error (i.e. if module is already enabled)
*/
function onInstall(bytes calldata data) external;
/**
* @dev This function is called by the smart account during uninstallation of the module
* @param data arbitrary data that may be required on the module during `onUninstall`
* de-initialization
*
* MUST revert on error
*/
function onUninstall(bytes calldata data) external;
/**
* @dev Returns boolean value if module is a certain type
* @param moduleTypeId the module type ID according the ERC-7579 spec
*
* MUST return true if the module is of the given type and false otherwise
*/
function isModuleType(uint256 moduleTypeId) external view returns (bool);
/**
* @dev Returns if the module was already initialized for a provided smartaccount
*/
function isInitialized(address smartAccount) external view returns (bool);
}
interface IValidator is IModule {
/**
* @dev Validates a transaction on behalf of the account.
* This function is intended to be called by the MSA during the ERC-4337 validaton phase
* Note: solely relying on bytes32 hash and signature is not suffcient for some
* validation implementations (i.e. SessionKeys often need access to userOp.calldata)
* @param userOp The user operation to be validated. The userOp MUST NOT contain any metadata.
* The MSA MUST clean up the userOp before sending it to the validator.
* @param userOpHash The hash of the user operation to be validated
* @return return value according to ERC-4337
*/
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
)
external
returns (uint256);
/**
* Validator can be used for ERC-1271 validation
*/
function isValidSignatureWithSender(
address sender,
bytes32 hash,
bytes calldata data
)
external
view
returns (bytes4);
}
interface IExecutor is IModule { }
interface IHook is IModule {
function preCheck(
address msgSender,
uint256 msgValue,
bytes calldata msgData
)
external
returns (bytes memory hookData);
function postCheck(bytes calldata hookData) external;
}
interface IFallback is IModule { }// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.0;
interface ISafe {
enum Operation {
Call,
DelegateCall
}
function setup(
address[] calldata _owners,
uint256 _threshold,
address to,
bytes calldata data,
address fallbackHandler,
address paymentToken,
uint256 payment,
address payable paymentReceiver
)
external;
/**
* @dev Allows a Module to execute a Safe transaction without any further confirmations.
* @param to Destination address of module transaction.
* @param value Ether value of module transaction.
* @param data Data payload of module transaction.
* @param operation Operation type of module transaction.
*/
function execTransactionFromModule(
address to,
uint256 value,
bytes memory data,
Operation operation
)
external
returns (bool success);
/**
* @notice Execute `operation` (0: Call, 1: DelegateCall) to `to` with `value` (Native Token)
* and return data
* @param to Destination address of module transaction.
* @param value Ether value of module transaction.
* @param data Data payload of module transaction.
* @param operation Operation type of module transaction.
* @return success Boolean flag indicating if the call succeeded.
* @return returnData Data returned by the call.
*/
function execTransactionFromModuleReturnData(
address to,
uint256 value,
bytes memory data,
Operation operation
)
external
returns (bool success, bytes memory returnData);
/**
* @dev Checks whether the signature provided is valid for the provided data, hash. Will revert
* otherwise.
* @param dataHash Hash of the data (could be either a message hash or transaction hash)
* @param data That should be signed (this is passed to an external validator contract)
* @param signatures Signature data that should be verified. Can be ECDSA signature, contract
* signature (EIP-1271) or approved hash.
*/
function checkSignatures(
bytes32 dataHash,
bytes memory data,
bytes memory signatures
)
external
view;
function signedMessages(bytes32) external view returns (uint256);
/**
* @dev Returns the domain separator for this contract, as defined in the EIP-712 standard.
* @return bytes32 The domain separator hash.
*/
function domainSeparator() external view returns (bytes32);
function VERSION() external pure returns (string memory);
function getStorageAt(uint256 offset, uint256 length) external view returns (bytes memory);
/**
* @dev Returns array of modules.
* @param start Start of the page.
* @param pageSize Maximum number of modules that should be returned.
* @return array Array of modules.
* @return next Start of the next page.
*/
function getModulesPaginated(
address start,
uint256 pageSize
)
external
view
returns (address[] memory array, address next);
/**
* @notice Enables the module `module` for the Safe.
* @dev This can only be done via a Safe transaction.
* @param module Module to be enabled.
*/
function enableModule(address module) external;
function setFallbackHandler(address handler) external;
function simulateAndRevert(address targetContract, bytes memory calldataPayload) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol";
bytes32 constant SAFE_OP_TYPEHASH =
0xc03dfc11d8b10bf9cf703d558958c8c42777f785d998c62060d85a4f0ef6ea7f;
interface ISafeOp {
/**
* @notice The EIP-712 type-hash for a SafeOp, representing the structure of a User Operation
* for
* the Safe.
* {address} safe - The address of the safe on which the operation is performed.
* {uint256} nonce - A unique number associated with the user operation, preventing replay
* attacks
* by ensuring each operation is unique.
* {bytes} initCode - The packed encoding of a factory address and its factory-specific data
* for
* creating a new Safe account.
* {bytes} callData - The bytes representing the data of the function call to be executed.
* {uint128} verificationGasLimit - The maximum amount of gas allowed for the verification
* process.
* {uint128} callGasLimit - The maximum amount of gas allowed for executing the function call.
* {uint256} preVerificationGas - The amount of gas allocated for pre-verification steps before
* executing the main operation.
* {uint128} maxPriorityFeePerGas - The maximum priority fee per gas that the user is willing
* to
* pay for the transaction.
* {uint128} maxFeePerGas - The maximum fee per gas that the user is willing to pay for the
* transaction.
* {bytes} paymasterAndData - The packed encoding of a paymaster address and its
* paymaster-specific
* data for sponsoring the user operation.
* {uint48} validAfter - A timestamp representing from when the user operation is valid.
* {uint48} validUntil - A timestamp representing until when the user operation is valid, or 0
* to
* indicated "forever".
* {address} entryPoint - The address of the entry point that will execute the user operation.
* @dev When validating the user operation, the signature timestamps are pre-pended to the
* signature
* bytes. Equal to:
* keccak256(
* "SafeOp(address safe,uint256 nonce,bytes initCode,bytes callData,uint128
* verificationGasLimit,uint128 callGasLimit,uint256 preVerificationGas,uint128
* maxPriorityFeePerGas,uint128 maxFeePerGas,bytes paymasterAndData,uint48 validAfter,uint48
* validUntil,address entryPoint)"
* ) = 0xc03dfc11d8b10bf9cf703d558958c8c42777f785d998c62060d85a4f0ef6ea7f
*/
struct EncodedSafeOpStruct {
bytes32 typeHash;
address safe;
uint256 nonce;
bytes32 initCodeHash;
bytes32 callDataHash;
uint128 verificationGasLimit;
uint128 callGasLimit;
uint256 preVerificationGas;
uint128 maxPriorityFeePerGas;
uint128 maxFeePerGas;
bytes32 paymasterAndDataHash;
uint48 validAfter;
uint48 validUntil;
address entryPoint;
}
function domainSeparator() external view returns (bytes32);
function getSafeOp(
PackedUserOperation calldata userOp,
address entryPoint
)
external
view
returns (
bytes memory operationData,
uint48 validAfter,
uint48 validUntil,
bytes calldata signatures
);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import { Execution } from "../interfaces/IERC7579Account.sol";
/**
* Helper Library for decoding Execution calldata
* malloc for memory allocation is bad for gas. use this assembly instead
* @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat)
*/
library ExecutionLib {
function decodeBatch(bytes calldata callData)
internal
pure
returns (Execution[] calldata executionBatch)
{
/*
* Batch Call Calldata Layout
* Offset (in bytes) | Length (in bytes) | Contents
* 0x0 | 0x4 | bytes4 function selector
* 0x4 | - |
abi.encode(IERC7579Execution.Execution[])
*/
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
let dataPointer := add(callData.offset, calldataload(callData.offset))
// Extract the ERC7579 Executions
executionBatch.offset := add(dataPointer, 32)
executionBatch.length := calldataload(dataPointer)
}
}
function encodeBatch(Execution[] memory executions)
internal
pure
returns (bytes memory callData)
{
callData = abi.encode(executions);
}
function decodeSingle(bytes calldata executionCalldata)
internal
pure
returns (address target, uint256 value, bytes calldata callData)
{
target = address(bytes20(executionCalldata[0:20]));
value = uint256(bytes32(executionCalldata[20:52]));
callData = executionCalldata[52:];
}
function encodeSingle(
address target,
uint256 value,
bytes memory callData
)
internal
pure
returns (bytes memory userOpCalldata)
{
userOpCalldata = abi.encodePacked(target, value, callData);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/**
* @title ModeLib
* @author rhinestone | zeroknots.eth, Konrad Kopp (@kopy-kat)
* To allow smart accounts to be very simple, but allow for more complex execution, A custom mode
* encoding is used.
* Function Signature of execute function:
* function execute(ModeCode mode, bytes calldata executionCalldata) external payable;
* This allows for a single bytes32 to be used to encode the execution mode, calltype, execType and
* context.
* NOTE: Simple Account implementations only have to scope for the most significant byte. Account that
* implement
* more complex execution modes may use the entire bytes32.
*
* |--------------------------------------------------------------------|
* | CALLTYPE | EXECTYPE | UNUSED | ModeSelector | ModePayload |
* |--------------------------------------------------------------------|
* | 1 byte | 1 byte | 4 bytes | 4 bytes | 22 bytes |
* |--------------------------------------------------------------------|
*
* CALLTYPE: 1 byte
* CallType is used to determine how the executeCalldata paramter of the execute function has to be
* decoded.
* It can be either single, batch or delegatecall. In the future different calls could be added.
* CALLTYPE can be used by a validation module to determine how to decode <userOp.callData[36:]>.
*
* EXECTYPE: 1 byte
* ExecType is used to determine how the account should handle the execution.
* It can indicate if the execution should revert on failure or continue execution.
* In the future more execution modes may be added.
* Default Behavior (EXECTYPE = 0x00) is to revert on a single failed execution. If one execution in
* a batch fails, the entire batch is reverted
*
* UNUSED: 4 bytes
* Unused bytes are reserved for future use.
*
* ModeSelector: bytes4
* The "optional" mode selector can be used by account vendors, to implement custom behavior in
* their accounts.
* the way a ModeSelector is to be calculated is bytes4(keccak256("vendorname.featurename"))
* this is to prevent collisions between different vendors, while allowing innovation and the
* development of new features without coordination between ERC-7579 implementing accounts
*
* ModePayload: 22 bytes
* Mode payload is used to pass additional data to the smart account execution, this may be
* interpreted depending on the ModeSelector
*
* ExecutionCallData: n bytes
* single, delegatecall or batch exec abi.encoded as bytes
*/
// Custom type for improved developer experience
type ModeCode is bytes32;
type CallType is bytes1;
type ExecType is bytes1;
type ModeSelector is bytes4;
type ModePayload is bytes22;
// Default CallType
CallType constant CALLTYPE_SINGLE = CallType.wrap(0x00);
// Batched CallType
CallType constant CALLTYPE_BATCH = CallType.wrap(0x01);
CallType constant CALLTYPE_STATIC = CallType.wrap(0xFE);
// @dev Implementing delegatecall is OPTIONAL!
// implement delegatecall with extreme care.
CallType constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
// @dev default behavior is to revert on failure
// To allow very simple accounts to use mode encoding, the default behavior is to revert on failure
// Since this is value 0x00, no additional encoding is required for simple accounts
ExecType constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
// @dev account may elect to change execution behavior. For example "try exec" / "allow fail"
ExecType constant EXECTYPE_TRY = ExecType.wrap(0x01);
ModeSelector constant MODE_DEFAULT = ModeSelector.wrap(bytes4(0x00000000));
// Example declaration of a custom mode selector
ModeSelector constant MODE_OFFSET = ModeSelector.wrap(bytes4(keccak256("default.mode.offset")));
/**
* @dev ModeLib is a helper library to encode/decode ModeCodes
*/
library ModeLib {
function decode(ModeCode mode)
internal
pure
returns (
CallType _calltype,
ExecType _execType,
ModeSelector _modeSelector,
ModePayload _modePayload
)
{
// solhint-disable-next-line no-inline-assembly
assembly {
_calltype := mode
_execType := shl(8, mode)
_modeSelector := shl(48, mode)
_modePayload := shl(80, mode)
}
}
function encode(
CallType callType,
ExecType execType,
ModeSelector mode,
ModePayload payload
)
internal
pure
returns (ModeCode)
{
return ModeCode.wrap(
bytes32(
abi.encodePacked(callType, execType, bytes4(0), ModeSelector.unwrap(mode), payload)
)
);
}
function encodeSimpleBatch() internal pure returns (ModeCode mode) {
mode = encode(CALLTYPE_BATCH, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function encodeSimpleSingle() internal pure returns (ModeCode mode) {
mode = encode(CALLTYPE_SINGLE, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00));
}
function getCallType(ModeCode mode) internal pure returns (CallType calltype) {
// solhint-disable-next-line no-inline-assembly
assembly {
calltype := mode
}
}
}
using { eqModeSelector as == } for ModeSelector global;
using { eqCallType as == } for CallType global;
using { neqCallType as != } for CallType global;
using { eqExecType as == } for ExecType global;
function eqCallType(CallType a, CallType b) pure returns (bool) {
return CallType.unwrap(a) == CallType.unwrap(b);
}
function neqCallType(CallType a, CallType b) pure returns (bool) {
return CallType.unwrap(a) == CallType.unwrap(b);
}
function eqExecType(ExecType a, ExecType b) pure returns (bool) {
return ExecType.unwrap(a) == ExecType.unwrap(b);
}
function eqModeSelector(ModeSelector a, ModeSelector b) pure returns (bool) {
return ModeSelector.unwrap(a) == ModeSelector.unwrap(b);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import { Execution } from "../interfaces/IERC7579Account.sol";
import { IModule as IERC7579Module } from "../interfaces/IERC7579Module.sol";
import { IERC7579AccountEvents } from "../interfaces/IERC7579Account.sol";
contract ModuleInstallUtil is IERC7579AccountEvents {
function installModule(
uint256 moduleTypeId,
address module,
bytes calldata initData
)
external
{
IERC7579Module(module).onInstall(initData);
emit ModuleInstalled(moduleTypeId, address(module));
}
function unInstallModule(
uint256 moduleTypeId,
address module,
bytes calldata initData
)
external
{
IERC7579Module(module).onUninstall(initData);
emit ModuleUninstalled(moduleTypeId, address(module));
}
}
contract BatchedExecUtil {
function tryExecute(Execution[] calldata executions) external {
uint256 length = executions.length;
for (uint256 i; i < length; i++) {
Execution calldata _exec = executions[i];
_tryExecute(_exec.target, _exec.value, _exec.callData);
}
}
function execute(Execution[] calldata executions) external {
uint256 length = executions.length;
for (uint256 i; i < length; i++) {
Execution calldata _exec = executions[i];
_execute(_exec.target, _exec.value, _exec.callData);
}
}
function executeReturn(Execution[] calldata executions)
external
returns (bytes[] memory result)
{
uint256 length = executions.length;
result = new bytes[](length);
for (uint256 i; i < length; i++) {
Execution calldata _exec = executions[i];
result[i] = _execute(_exec.target, _exec.value, _exec.callData);
}
}
function tryExecuteReturn(Execution[] calldata executions)
external
returns (bool[] memory success, bytes[] memory result)
{
uint256 length = executions.length;
result = new bytes[](length);
success = new bool[](length);
for (uint256 i; i < length; i++) {
Execution calldata _exec = executions[i];
(success[i], result[i]) = _tryExecute(_exec.target, _exec.value, _exec.callData);
}
}
function _execute(
address target,
uint256 value,
bytes calldata callData
)
internal
virtual
returns (bytes memory result)
{
// solhint-disable-next-line no-inline-assembly
assembly {
result := mload(0x40)
calldatacopy(result, callData.offset, callData.length)
if iszero(call(gas(), target, value, result, callData.length, codesize(), 0x00)) {
// Bubble up the revert if the call reverts.
returndatacopy(result, 0x00, returndatasize())
revert(result, returndatasize())
}
mstore(result, returndatasize()) // Store the length.
let o := add(result, 0x20)
returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
mstore(0x40, add(o, returndatasize())) // Allocate the memory.
}
}
function _tryExecute(
address target,
uint256 value,
bytes calldata callData
)
internal
virtual
returns (bool success, bytes memory result)
{
// solhint-disable-next-line no-inline-assembly
assembly {
result := mload(0x40)
calldatacopy(result, callData.offset, callData.length)
success := call(gas(), target, value, result, callData.length, codesize(), 0x00)
mstore(result, returndatasize()) // Store the length.
let o := add(result, 0x20)
returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
mstore(0x40, add(o, returndatasize())) // Allocate the memory.
}
}
}
contract Safe7579DCUtil is ModuleInstallUtil, BatchedExecUtil {
function staticCall(address target, bytes memory data) external view {
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
let ptr := mload(0x40)
let success := staticcall(gas(), target, add(data, 0x20), mload(data), 0x00, 0x00)
returndatacopy(ptr, 0x00, returndatasize())
if success { return(ptr, returndatasize()) }
revert(ptr, returndatasize())
}
}
}{
"evmVersion": "paris",
"libraries": {},
"metadata": {
"appendCBOR": true,
"bytecodeHash": "ipfs",
"useLiteralContent": false
},
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": [
"@rhinestone/=node_modules/@rhinestone/",
"sentinellist/=node_modules/@rhinestone/sentinellist/src/",
"erc4337-validation/=node_modules/@rhinestone/erc4337-validation/src/",
"modulekit/=node_modules/@rhinestone/modulekit/src/",
"module-bases/=node_modules/@rhinestone/module-bases/src/",
"@ERC4337/=node_modules/@ERC4337/",
"account-abstraction/=node_modules/@ERC4337/account-abstraction/contracts/",
"account-abstraction-v0.6/=node_modules/@ERC4337/account-abstraction-v0.6/contracts/",
"@openzeppelin/=node_modules/@openzeppelin/",
"@safe-global/=node_modules/@safe-global/",
"ds-test/=node_modules/ds-test/src/",
"erc7579/=node_modules/erc7579/src/",
"forge-std/=node_modules/forge-std/src/",
"solady/=node_modules/solady/src/",
"solarray/=node_modules/solarray/src/",
"@prb/math/=node_modules/@prb/math/src/",
"@gnosis.pm/=node_modules/@gnosis.pm/",
"hardhat-deploy/=node_modules/hardhat-deploy/",
"hardhat/=node_modules/hardhat/"
],
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"AccountAccessUnauthorized","type":"error"},{"inputs":[],"name":"ExecutionFailed","type":"error"},{"inputs":[{"internalType":"bytes4","name":"msgSig","type":"bytes4"}],"name":"FallbackInstalled","type":"error"},{"inputs":[{"internalType":"address","name":"currentHook","type":"address"}],"name":"HookAlreadyInstalled","type":"error"},{"inputs":[{"internalType":"CallType","name":"callType","type":"bytes1"}],"name":"InvalidCallType","type":"error"},{"inputs":[{"internalType":"bytes4","name":"msgSig","type":"bytes4"}],"name":"InvalidFallbackHandler","type":"error"},{"inputs":[],"name":"InvalidHookType","type":"error"},{"inputs":[{"internalType":"address","name":"safe","type":"address"}],"name":"InvalidInitData","type":"error"},{"inputs":[],"name":"InvalidInput","type":"error"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"InvalidModule","type":"error"},{"inputs":[{"internalType":"address","name":"module","type":"address"},{"internalType":"uint256","name":"moduleType","type":"uint256"}],"name":"InvalidModuleType","type":"error"},{"inputs":[],"name":"LinkedList_AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"entry","type":"address"}],"name":"LinkedList_EntryAlreadyInList","type":"error"},{"inputs":[{"internalType":"address","name":"entry","type":"address"}],"name":"LinkedList_InvalidEntry","type":"error"},{"inputs":[],"name":"LinkedList_InvalidPage","type":"error"},{"inputs":[{"internalType":"bytes4","name":"msgSig","type":"bytes4"}],"name":"NoFallbackHandler","type":"error"},{"inputs":[{"internalType":"CallType","name":"callType","type":"bytes1"}],"name":"UnsupportedCallType","type":"error"},{"inputs":[{"internalType":"ExecType","name":"execType","type":"bytes1"}],"name":"UnsupportedExecType","type":"error"},{"inputs":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"}],"name":"UnsupportedModuleType","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"smartAccount","type":"address"},{"indexed":true,"internalType":"contract IERC7484","name":"registry","type":"address"}],"name":"ERC7484RegistryConfigured","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"indexed":false,"internalType":"address","name":"module","type":"address"}],"name":"ModuleInstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"moduleTypeId","type":"uint256"},{"indexed":false,"internalType":"address","name":"module","type":"address"}],"name":"ModuleUninstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"safe","type":"address"}],"name":"Safe7579Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ISafe","name":"safe","type":"address"},{"indexed":false,"internalType":"uint256","name":"numberInBatch","type":"uint256"}],"name":"TryExecutionFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ISafe","name":"safe","type":"address"},{"indexed":false,"internalType":"bool[]","name":"success","type":"bool[]"}],"name":"TryExecutionsFailed","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"accountId","outputs":[{"internalType":"string","name":"accountImplementationId","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"entryPoint","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"ModeCode","name":"mode","type":"bytes32"},{"internalType":"bytes","name":"executionCalldata","type":"bytes"}],"name":"execute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"ModeCode","name":"mode","type":"bytes32"},{"internalType":"bytes","name":"executionCalldata","type":"bytes"}],"name":"executeFromExecutor","outputs":[{"internalType":"bytes[]","name":"returnDatas","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getActiveHook","outputs":[{"internalType":"address","name":"hook","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"getActiveHook","outputs":[{"internalType":"address","name":"hook","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"cursor","type":"address"},{"internalType":"uint256","name":"pageSize","type":"uint256"}],"name":"getExecutorsPaginated","outputs":[{"internalType":"address[]","name":"array","type":"address[]"},{"internalType":"address","name":"next","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"safe","type":"address"},{"internalType":"address","name":"validator","type":"address"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes32","name":"accountGasLimits","type":"bytes32"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"bytes32","name":"gasFees","type":"bytes32"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct PackedUserOperation","name":"userOp","type":"tuple"},{"internalType":"address","name":"entryPoint","type":"address"}],"name":"getSafeOp","outputs":[{"internalType":"bytes","name":"operationData","type":"bytes"},{"internalType":"uint48","name":"validAfter","type":"uint48"},{"internalType":"uint48","name":"validUntil","type":"uint48"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"cursor","type":"address"},{"internalType":"uint256","name":"pageSize","type":"uint256"}],"name":"getValidatorsPaginated","outputs":[{"internalType":"address[]","name":"array","type":"address[]"},{"internalType":"address","name":"next","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"initData","type":"bytes"}],"internalType":"struct ModuleInit[]","name":"validators","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"initData","type":"bytes"}],"internalType":"struct ModuleInit[]","name":"executors","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"initData","type":"bytes"}],"internalType":"struct ModuleInit[]","name":"fallbacks","type":"tuple[]"},{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"initData","type":"bytes"}],"internalType":"struct ModuleInit[]","name":"hooks","type":"tuple[]"},{"components":[{"internalType":"contract IERC7484","name":"registry","type":"address"},{"internalType":"address[]","name":"attesters","type":"address[]"},{"internalType":"uint8","name":"threshold","type":"uint8"}],"internalType":"struct RegistryInit","name":"registryInit","type":"tuple"}],"name":"initializeAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"initData","type":"bytes"}],"internalType":"struct ModuleInit[]","name":"validators","type":"tuple[]"}],"name":"initializeAccountWithValidators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"moduleType","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"initData","type":"bytes"}],"name":"installModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"moduleType","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"additionalContext","type":"bytes"}],"name":"isModuleInstalled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"magicValue","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC7484","name":"registry","type":"address"},{"internalType":"address[]","name":"attesters","type":"address[]"},{"internalType":"uint8","name":"threshold","type":"uint8"}],"name":"setRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"ModeCode","name":"encodedMode","type":"bytes32"}],"name":"supportsExecutionMode","outputs":[{"internalType":"bool","name":"supported","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"moduleTypeId","type":"uint256"}],"name":"supportsModule","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"moduleType","type":"uint256"},{"internalType":"address","name":"module","type":"address"},{"internalType":"bytes","name":"deInitData","type":"bytes"}],"name":"uninstallModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes32","name":"accountGasLimits","type":"bytes32"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"bytes32","name":"gasFees","type":"bytes32"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct PackedUserOperation","name":"userOp","type":"tuple"},{"internalType":"bytes32","name":"userOpHash","type":"bytes32"},{"internalType":"uint256","name":"missingAccountFunds","type":"uint256"}],"name":"validateUserOp","outputs":[{"internalType":"uint256","name":"validSignature","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60a0604052348015600f57600080fd5b50604051601a906046565b604051809103906000f0801580156035573d6000803e3d6000fd5b506001600160a01b03166080526053565b6109e180614ee983390190565b608051614e2e6100bb600039600081816112230152818161141b0152818161157c01528181611dc501528181612216015281816122f0015281816123870152818161241e0152818161357a015281816136320152818161395a0152613a7a0152614e2e6000f3fe60806040526004361061012e5760003560e01c8063a71763a8116100ab578063d828435d1161006f578063d828435d146104a2578063e9ae5c53146104c2578063ea5f61d0146104e2578063eab77e1714610502578063f2dc691d14610522578063f698da251461054257610135565b8063a71763a8146103c7578063b0d691fe146103e7578063b875d5d81461040a578063d03c791414610455578063d691c9641461047557610135565b806355d559f4116100f257806355d559f4146102dd5780635faac46b1461030d57806379aad60e1461033b5780639517e29f1461035b5780639cfd7cff1461037b57610135565b80630a664dba146101e0578063112d3a7d146102245780631626ba7e1461025457806319822f7c1461028d578063540fb4f9146102bb57610135565b3661013557005b600036606060003560e01c63bc197c81811463f23a6e6182141763150b7a028214171561016657806020526020603cf35b50336000818152600460209081526040808320546001600160e01b031984351680855260058452828520958552949092528220546001600160a01b0391821692911690806101b48484610557565b915091506101c28888610615565b95506101d084848484610728565b5050505050915050805190602001f35b3480156101ec57600080fd5b50336000908152600460205260409020546001600160a01b03165b6040516001600160a01b0390911681526020015b60405180910390f35b34801561023057600080fd5b5061024461023f366004613d49565b6107ab565b604051901515815260200161021b565b34801561026057600080fd5b5061027461026f366004613da4565b610820565b6040516001600160e01b0319909116815260200161021b565b34801561029957600080fd5b506102ad6102a8366004613e08565b610b96565b60405190815260200161021b565b3480156102c757600080fd5b506102db6102d6366004613eab565b610caa565b005b3480156102e957600080fd5b506102fd6102f8366004613fa7565b610d5a565b60405161021b9493929190614048565b34801561031957600080fd5b5061032d610328366004614094565b610fb7565b60405161021b9291906140c0565b34801561034757600080fd5b506102db610356366004614123565b610fd3565b34801561036757600080fd5b506102db610376366004613d49565b6110c8565b34801561038757600080fd5b50604080518082018252601a81527f7268696e6573746f6e652e73616665373537392e76312e302e300000000000006020820152905161021b9190614164565b3480156103d357600080fd5b506102db6103e2366004613d49565b611293565b3480156103f357600080fd5b506f71727de22e5e9d8baf0edac6f37da032610207565b34801561041657600080fd5b5061020761042536600461418d565b6001600160e01b03191660009081526005602090815260408083203384529091529020546001600160a01b031690565b34801561046157600080fd5b506102446104703660046141aa565b6115ba565b34801561048157600080fd5b50610495610490366004613da4565b611662565b60405161021b91906141c3565b3480156104ae57600080fd5b506102ad6104bd366004614228565b61174b565b3480156104ce57600080fd5b506102db6104dd366004613da4565b6117eb565b3480156104ee57600080fd5b5061032d6104fd366004614094565b611b88565b34801561050e57600080fd5b506102db61051d366004614267565b611b99565b34801561052e57600080fd5b5061024461053d3660046141aa565b611c08565b34801561054e57600080fd5b506102ad611c57565b6060806001600160a01b038416156105d7576105be33856000610578611cb0565b3460003660405160240161058f94939291906142f6565b60408051601f198184030181529190526020810180516001600160e01b031663d68f602560e01b179052611cbc565b9150818060200190518101906105d491906143df565b91505b6001600160a01b0383161561060e576105f533846000610578611cb0565b90508080602001905181019061060b91906143df565b90505b9250929050565b600080356001600160e01b031916815260036020908152604080832033845290915290208054606091906001600160a01b03811690600160a01b900460f81b8161068557604051632464e76d60e11b81526001600160e01b03196000351660048201526024015b60405180910390fd5b61069381607f60f91b611d65565b156106d7576106cd338388886106a7611cb0565b6040516020016106b993929190614413565b604051602081830303815290604052611d77565b9350505050610722565b6106e2816000611d65565b1561071e576106cd3383600089896106f8611cb0565b60405160200161070a93929190614413565b604051602081830303815290604052611cbc565b5050505b92915050565b6001600160a01b0384161561077e5761077e338560008560405160240161074f9190614164565b60408051601f198184030181529190526020810180516001600160e01b0316630b9dfbed60e11b179052611e4c565b6001600160a01b038316156107a5576107a5338460008460405160240161074f9190614164565b50505050565b6000600185036107dd57336001600160a01b038516036107cd57506001610818565b6107d684611eeb565b9050610818565b600285036107ee576107d684611ef9565b60038503610801576107d6848484611f07565b60048503610814576107d6848484611f53565b5060005b949350505050565b6000338282036109ba5760006108f1826001600160a01b031663f698da256040518163ffffffff1660e01b8152600401602060405180830381865afa15801561086d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108919190614439565b60408051602081018a90527f60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca91015b60408051601f1981840301815282825280516020918201209083015201604051602081830303815290604052611f8e565b8051602090910120604051635ae6bd3760e01b8152600481018290529091506001600160a01b03831690635ae6bd3790602401602060405180830381865afa158015610941573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109659190614439565b6000036109a85760405162461bcd60e51b815260206004820152601160248201527012185cda081b9bdd08185c1c1c9bdd9959607a1b604482015260640161067c565b50630b135d3f60e11b9150610b8f9050565b60006109c96014828688614452565b6109d29161447c565b60601c90508015806109ea57506109e881611eeb565b155b15610b14576000610a89836001600160a01b031663f698da256040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a569190614439565b60408051602081018b90527f60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca91016108c0565b805160208201209091506001600160a01b03841663934f3a118284610ab18a6014818e614452565b6040518563ffffffff1660e01b8152600401610ad094939291906144c9565b60006040518083038186803b158015610ae857600080fd5b505afa158015610afc573d6000803e3d6000fd5b50630b135d3f60e11b9750610b8f9650505050505050565b6000610b733383610b23611cb0565b8a610b318a6014818e614452565b604051602401610b4494939291906142f6565b60408051601f198184030181529190526020810180516001600160e01b0316637aa8f17760e11b179052611d77565b905080806020019051810190610b8991906144f5565b93505050505b9392505050565b60006f71727de22e5e9d8baf0edac6f37da032610bb1611cb0565b6001600160a01b031614610bd857604051635629665f60e11b815260040160405180910390fd5b6020840135606081901c90811580610bf65750610bf482611eeb565b155b15610c0b57610c0486612008565b9250610c70565b6000610c56338460008a8a604051602401610c27929190614557565b60408051601f198184030181529190526020810180516001600160e01b0316639700320360e01b179052611cbc565b905080806020019051810190610c6c9190614439565b9350505b8315610ca157610ca1336f71727de22e5e9d8baf0edac6f37da0328660405180602001604052806000815250611e4c565b50509392505050565b6f71727de22e5e9d8baf0edac6f37da032610cc3611cb0565b6001600160a01b03161480610cf05750610cdb611cb0565b6001600160a01b0316336001600160a01b0316145b610d0d57604051635629665f60e11b815260040160405180910390fd5b610d3f610d1d602083018361465b565b610d2a6020840184614678565b610d3a60608601604087016146c1565b6120b7565b610d4f898989898989898961216e565b505050505050505050565b6060600080823682610d706101008901896146dc565b9092509050610d83600660008385614452565b610d8c91614722565b60d01c9450610d9f600c60068385614452565b610da891614722565b60d01c9350610dba81600c8185614452565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250604080516101c081019091527fc03dfc11d8b10bf9cf703d558958c8c42777f785d998c62060d85a4f0ef6ea7f815294975095509293505060208084019250610e3691508a018a61465b565b6001600160a01b0316815260200188602001358152602001888060400190610e5e91906146dc565b604051610e6c929190614758565b6040519081900390208152602001610e8760608a018a6146dc565b604051610e95929190614758565b60405180910390208152602001610eab8961248d565b6001600160801b03168152602001610ec28961249d565b6001600160801b031681526020018860a001358152602001610ee3896124b2565b6001600160801b03168152602001610efa896124c1565b6001600160801b03168152602001610f1560e08a018a6146dc565b604051610f23929190614758565b6040805191829003909120825265ffffffffffff80881660208401528616908201526001600160a01b0388166060909101526101c08120909150601960f81b600160f81b610f6f611c57565b6040516001600160f81b031993841660208201529290911660218301526022820152604281018290526062016040516020818303038152906040529550505092959194509250565b60606000610fc860013386866124d6565b915091509250929050565b6f71727de22e5e9d8baf0edac6f37da032610fec611cb0565b6001600160a01b031614806110195750611004611cb0565b6001600160a01b0316336001600160a01b0316145b61103657604051635629665f60e11b815260040160405180910390fd5b61104160013361269a565b8060005b81811015611097573684848381811061106057611060614768565b9050602002810190611072919061477e565b905061108e33611085602084018461465b565b60019190612715565b50600101611045565b5060405133907ff48581d8a62b775b74f2fb67f1d5806a9a356fbcc598040ab3071d3e37af40c290600090a2505050565b336000908152600460209081526040808320547fd00bcad91b775f25eb0aee15808d12dfdaa6dc5a7d6b754e26f9c205d2a3b8df909252822054639517e29f60e01b926001600160a01b039283169290911690806111268484610557565b915091506111416f71727de22e5e9d8baf0edac6f37da03290565b6001600160a01b0316611152611cb0565b6001600160a01b0316148061117f575061116a611cb0565b6001600160a01b0316336001600160a01b0316145b61119c57604051635629665f60e11b815260040160405180910390fd5b606060018a036111b8576111b189898961280a565b905061121d565b60028a036111cb576111b1898989612866565b60038a036111de576111b1898989612881565b60048a036111f1576111b1898989612a00565b89611201576111b1898989612ba0565b60405163041c38b360e41b8152600481018b905260240161067c565b611286337f00000000000000000000000000000000000000000000000000000000000000008c8c856040516024016112579392919061479e565b60408051601f198184030181529190526020810180516001600160e01b0316639517e29f60e01b179052612d51565b50610d4f84848484610728565b336000908152600460209081526040808320547fc45a7acaba78a7b4c4432b604d301355b314c0a7811768a1ee2563c9d34ca4e09092529091205484916314e2ec7560e31b916001600160a01b0391821691908116908416821480159061130c5750806001600160a01b0316846001600160a01b031614155b156114925760008061131e8484610557565b915091506113396f71727de22e5e9d8baf0edac6f37da03290565b6001600160a01b031661134a611cb0565b6001600160a01b031614806113775750611362611cb0565b6001600160a01b0316336001600160a01b0316145b61139457604051635629665f60e11b815260040160405180910390fd5b606060018b036113b0576113a98a8a8a612dea565b9050611415565b60028b036113c3576113a98a8a8a612e13565b60038b036113d6576113a98a8a8a612e34565b60048b036113e9576113a98a8a8a612e80565b8a6113f9576113a98a8a8a612f38565b60405163041c38b360e41b8152600481018c905260240161067c565b61147e337f00000000000000000000000000000000000000000000000000000000000000008d8d8560405160240161144f9392919061479e565b60408051601f198184030181529190526020810180516001600160e01b0316637827252560e01b17905261308b565b5061148b84848484610728565b50506115b0565b6f71727de22e5e9d8baf0edac6f37da0326114ab611cb0565b6001600160a01b031614806114d857506114c3611cb0565b6001600160a01b0316336001600160a01b0316145b6114f557604051635629665f60e11b815260040160405180910390fd5b6060600189036115115761150a888888612dea565b9050611576565b600289036115245761150a888888612e13565b600389036115375761150a888888612e34565b6004890361154a5761150a888888612e80565b8861155a5761150a888888612f38565b60405163041c38b360e41b8152600481018a905260240161067c565b610d4f337f00000000000000000000000000000000000000000000000000000000000000008b8b8560405160240161144f9392919061479e565b5050505050505050565b600081600881901b6115d082600160f81b611d65565b156115de5760019250611621565b6115e9826000611d65565b156115f75760019250611621565b611609826001600160f81b0319611d65565b156116175760019250611621565b5060009392505050565b8280156116345750611634816000611d65565b15611640575050919050565b828015611656575061165681600160f81b611d65565b15611617575050919050565b606061167461166f611cb0565b611ef9565b6116a557611680611cb0565b604051635c93ff2f60e11b81526001600160a01b03909116600482015260240161067c565b336000908152600460209081526040808320547f4162c930ade27880570b0eb31efbc8a626b97e81a40ced27163509dfe16398829092528220546335a4725960e21b926001600160a01b039283169290911690806117038484610557565b9150915061170f611cb0565b600261171b8282613141565b8a600881901b61172d81838e8e6131cc565b99505050505061173f84848484610728565b50505050509392505050565b6000602082901b640100000000600160c01b03166f71727de22e5e9d8baf0edac6f37da032604051631aab3f0d60e11b81526001600160a01b0386811660048301526001600160c01b038416602483015291909116906335567e1a90604401602060405180830381865afa1580156117c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108189190614439565b336000908152600460209081526040808320547fccfe1b2bef70acd8e661aeb5430e147f4c75cb320972acba718a28da428e1a3090925282205463e9ae5c5360e01b926001600160a01b039283169290911690806118498484610557565b915091506118646f71727de22e5e9d8baf0edac6f37da03290565b6001600160a01b0316611875611cb0565b6001600160a01b031614806118a2575061188d611cb0565b6001600160a01b0316336001600160a01b0316145b6118bf57604051635629665f60e11b815260040160405180910390fd5b87600881901b336118d1826000611d65565b15611a2a576118e483600160f81b611d65565b156119055789358a016020810190356118fe838383613574565b5050611b79565b611910836000611d65565b15611978576000803660006119258e8e6135db565b935093509350935061196f85858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611e4c92505050565b50505050611b79565b61198a836001600160f81b0319611d65565b15611a0f57600061199e6014828c8e614452565b6119a79161447c565b60601c90503660008c8c60149080926119c293929190614452565b91509150611a07848484848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612d5192505050565b505050611b79565b82604051632e5bf3f960e21b815260040161067c91906147d1565b611a3882600160f81b611d65565b15611b5e57611a4b83600160f81b611d65565b15611a655789358a016020810190356118fe83838361362c565b611a70836000611d65565b15611acf57600080366000611a858e8e6135db565b935093509350935061196f85858585858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061369392505050565b611ae1836001600160f81b0319611d65565b15611a0f576000611af56014828c8e614452565b611afe9161447c565b60601c90503660008c8c6014908092611b1993929190614452565b91509150611a07848484848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061308b92505050565b816040516308c3ee0360e11b815260040161067c91906147d1565b5050506115b084848484610728565b60606000610fc860023386866124d6565b6f71727de22e5e9d8baf0edac6f37da032611bb2611cb0565b6001600160a01b03161480611bdf5750611bca611cb0565b6001600160a01b0316336001600160a01b0316145b611bfc57604051635629665f60e11b815260040160405180910390fd5b6107a5848484846120b7565b600060018203611c1a57506001919050565b60028203611c2a57506001919050565b60038203611c3a57506001919050565b60048203611c4a57506001919050565b506000919050565b919050565b604080517f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a794692186020820152469181019190915230606082015260009060800160405160208183030381529060405280519060200120905090565b60131936013560601c90565b60606000856001600160a01b0316635229073f86868660006040518563ffffffff1660e01b8152600401611cf3949392919061481e565b6000604051808303816000875af1158015611d12573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611d3a9190810190614864565b9250905080611d5c57604051632b3f6d1160e21b815260040160405180910390fd5b50949350505050565b6001600160f81b031990811691161490565b606060008383604051602401611d8e9291906148b1565b60408051601f198184030181529181526020820180516001600160e01b0316636a22165760e01b17905251909150600090611def907f00000000000000000000000000000000000000000000000000000000000000009084906024016148b1565b60408051601f19818403018152919052602080820180516001600160e01b031663b4faba0960e01b17815282519293509091600091895afa5060203d036040519350808401604052806020853e50600051610ca157825160208401fd5b60405163468721a760e01b81526000906001600160a01b0386169063468721a790611e8190879087908790879060040161481e565b6020604051808303816000875af1158015611ea0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ec491906148d5565b905080611ee457604051632b3f6d1160e21b815260040160405180910390fd5b5050505050565b600061072260013384613749565b600061072260023384613749565b600080611f168385018561418d565b6001600160e01b03191660009081526003602090815260408083203384529091529020546001600160a01b03908116908616149150509392505050565b60008080611f63848601866148ff565b915091506000611f73838361378d565b6001600160a01b039081169088161493505050509392505050565b6060601960f81b600160f81b858585604051602001611fae92919061492b565b60408051808303601f190181529082905280516020918201206001600160f81b0319958616918301919091529290931660218401526022830152604282015260620160405160208183030381529060405290509392505050565b600080808080612028866f71727de22e5e9d8baf0edac6f37da032610d5a565b8351602085012060405163934f3a1160e01b81529498509296509094509250339163934f3a119161205f9188908690600401614951565b60006040518083038186803b15801561207757600080fd5b505afa925050508015612088575060015b61209f5761209860018385613823565b94506120ae565b6120ab60008385613823565b94505b50505050919050565b33600090815260208190526040902080546001600160a01b0319166001600160a01b0386169081179091551561213257612132338560008487876040516024016121039392919061497c565b60408051601f198184030181529190526020810180516001600160e01b031663f05c04e160e01b179052611e4c565b6040516001600160a01b0385169033907f9452c8fb077c3ea8f28a77c87488af657b1e44d010ad9a5992d73870da040e9490600090a350505050565b3360009081527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f602052604090205460609088906001600160a01b0316612266576121ba60013361269a565b60005b8181101561226057368b8b838181106121d8576121d8614768565b90506020028101906121ea919061477e565b905061220e6121fc602083018361465b565b61220960208401846146dc565b61280a565b9350612257337f00000000000000000000000000000000000000000000000000000000000000006001612244602086018661465b565b886040516024016112579392919061479e565b506001016121bd565b50612287565b80156122875760405163d8e3ed1b60e01b815233600482015260240161067c565b61229260023361269a565b508560005b8181101561232757368989838181106122b2576122b2614768565b90506020028101906122c4919061477e565b90506122e86122d6602083018361465b565b6122e360208401846146dc565b612866565b935061231e337f00000000000000000000000000000000000000000000000000000000000000006002612244602086018661465b565b50600101612297565b5084905060005b818110156123be573687878381811061234957612349614768565b905060200281019061235b919061477e565b905061237f61236d602083018361465b565b61237a60208401846146dc565b612881565b93506123b5337f00000000000000000000000000000000000000000000000000000000000000006003612244602086018661465b565b5060010161232e565b5082905060005b8181101561245557368585838181106123e0576123e0614768565b90506020028101906123f2919061477e565b9050612416612404602083018361465b565b61241160208401846146dc565b612a00565b935061244c337f00000000000000000000000000000000000000000000000000000000000000006004612244602086018661465b565b506001016123c5565b5060405133907ff48581d8a62b775b74f2fb67f1d5806a9a356fbcc598040ab3071d3e37af40c290600090a250505050505050505050565b6000610722826080013560801c90565b60006001600160801b03608083013516610722565b600060c082013560801c610722565b60006001600160801b0360c083013516610722565b606060006001600160a01b0384166001148015906124fc57506124fa868686613749565b155b1561252557604051637c84ecfb60e01b81526001600160a01b038516600482015260240161067c565b826000036125465760405163f725081760e01b815260040160405180910390fd5b826001600160401b0381111561255e5761255e614328565b604051908082528060200260200182016040528015612587578160200160208202803683370190505b506001600160a01b038086166000908152602089815260408083208a85168452909152812054929450911691505b6001600160a01b038216158015906125d757506001600160a01b038216600114155b80156125e257508381105b1561264757818382815181106125fa576125fa614768565b6001600160a01b039283166020918202929092018101919091529281166000908152888452604080822089841683529094529290922054909116908061263f816149ea565b9150506125b5565b6001600160a01b0382166001148015906126615750600081115b1561268d5782612672600183614a03565b8151811061268257612682614768565b602002602001015191505b8083525094509492505050565b60016000908152602083815260408083206001600160a01b03808616855292529091205416156126dd576040516329e42f3360e11b815260040160405180910390fd5b60016000818152602093845260408082206001600160a01b0394909416825292909352912080546001600160a01b0319169091179055565b6001600160a01b038116158061273457506001600160a01b0381166001145b1561275d57604051637c84ecfb60e01b81526001600160a01b038216600482015260240161067c565b6001600160a01b03818116600090815260208581526040808320868516845290915290205416156127ac57604051631034f46960e21b81526001600160a01b038216600482015260240161067c565b60016000908152602084815260408083206001600160a01b039586168085528184528285208054968816808752988552838620918652908452919093208054949095166001600160a01b031994851617909455528154169091179055565b60608360016128198282613141565b61282560013388612715565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929998505050505050505050565b60608360026128758282613141565b61282560023388612715565b60608360036128908282613141565b600080806128a087890189614a67565b919450925090506001600160e01b031983166306d61fe760e41b14806128d657506001600160e01b03198316638a91b0e360e01b145b15612900576040516379bd117b60e01b81526001600160e01b03198416600482015260240161067c565b61290b826000611d65565b8015612920575061292082607f60f91b611d65565b1561294057816040516376087dc160e01b815260040161067c91906147d1565b6001600160e01b0319831660009081526003602090815260408083203384529091529020546001600160a01b031615612998576040516374420d1560e01b81526001600160e01b03198416600482015260240161067c565b6001600160e01b03199092166000908152600360209081526040808320338452909152902080546001600160a01b038a166001600160a01b031960f89490941c600160a01b02939093166001600160a81b031990911617919091179055925050509392505050565b6060836004612a0f8282613141565b60008080612a1f87890189614ad5565b91945092509050600080846001811115612a3b57612a3b6147e6565b148015612a5057506001600160e01b03198316155b15612ac65750336000908152600460205260409020546001600160a01b03168015612a995760405163741cbe0360e01b81526001600160a01b038216600482015260240161067c565b33600090815260046020526040902080546001600160a01b0319166001600160a01b038c16179055612b93565b6001846001811115612ada57612ada6147e6565b03612b7a57506001600160e01b0319821660009081526005602090815260408083203384529091529020546001600160a01b03168015612b385760405163741cbe0360e01b81526001600160a01b038216600482015260240161067c565b6001600160e01b031983166000908152600560209081526040808320338452909152902080546001600160a01b0319166001600160a01b038c16179055612b93565b604051635691922f60e01b815260040160405180910390fd5b5098975050505050505050565b60608235830160208181019135908581013586018082019190359060408801358801908101903584838114612be85760405163b4fa3fb360e01b815260040160405180910390fd5b60005b81811015612d0a576000888883818110612c0757612c07614768565b90506020020135905060018103612c4657612c408d888885818110612c2e57612c2e614768565b905060200281019061220991906146dc565b50612d01565b60028103612c7657612c408d888885818110612c6457612c64614768565b90506020028101906122e391906146dc565b60038103612ca657612c408d888885818110612c9457612c94614768565b905060200281019061237a91906146dc565b60048103612cd657612c408d888885818110612cc457612cc4614768565b905060200281019061241191906146dc565b60405163484d218160e01b81526001600160a01b038e1660048201526024810182905260440161067c565b50600101612beb565b5082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929e9d5050505050505050505050505050565b60405163468721a760e01b81526000906001600160a01b0385169063468721a790612d879086908590879060019060040161481e565b6020604051808303816000875af1158015612da6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dca91906148d5565b9050806107a557604051632b3f6d1160e21b815260040160405180910390fd5b60606000612dfa83850185614b03565b92509050612e0b600133838861385b565b509392505050565b60606000612e2383850185614b03565b92509050612e0b600233838861385b565b60606000612e4483850185614b48565b6001600160e01b03199091166000908152600360209081526040808320338452909152902080546001600160a01b031916905595945050505050565b6060600080612e9184860186614ad5565b945090925090506000826001811115612eac57612eac6147e6565b148015612ec157506001600160e01b03198116155b15612ee85733600090815260046020526040902080546001600160a01b0319169055610ca1565b6001826001811115612efc57612efc6147e6565b03612b7a576001600160e01b031981166000908152600560209081526040808320338452909152902080546001600160a01b0319169055610ca1565b60608235830160208181019135908581013586018082019190359060408801358801908101903584838114612f805760405163b4fa3fb360e01b815260040160405180910390fd5b60005b81811015612d0a576000888883818110612f9f57612f9f614768565b90506020020135905060018103612fe357612fdd8d888885818110612fc657612fc6614768565b9050602002810190612fd891906146dc565b612dea565b50613082565b6002810361301857612fdd8d88888581811061300157613001614768565b905060200281019061301391906146dc565b612e13565b6003810361304d57612fdd8d88888581811061303657613036614768565b905060200281019061304891906146dc565b612e34565b60048103612cd657612fdd8d88888581811061306b5761306b614768565b905060200281019061307d91906146dc565b612e80565b50600101612f83565b60405163468721a760e01b81526000906001600160a01b0385169063468721a7906130c19086908590879060019060040161481e565b6020604051808303816000875af11580156130e0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061310491906148d5565b9050806107a557604080516001600160a01b038616815260006020820152600080516020614dd9833981519152910160405180910390a150505050565b336000908152602081905260409020546001600160a01b031680156131c75760405163529562a160e01b81523360048201526001600160a01b0384811660248301526044820184905282169063529562a19060640160006040518083038186803b1580156131ae57600080fd5b505afa1580156131c2573d6000803e3d6000fd5b505050505b505050565b60606131d9856000611d65565b156133c5576131ec84600160f81b611d65565b1561320f5782358301602081019035613206338383613952565b92505050610818565b61321a846000611d65565b156132ce5760008036600061322f87876135db565b6040805160018082528183019092529498509296509094509250816020015b606081526020019060019003908161324e5790505094506132a733858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611cbc92505050565b856000815181106132ba576132ba614768565b602002602001018190525050505050610818565b6132e0846001600160f81b0319611d65565b156133aa5760006132f46014828587614452565b6132fd9161447c565b60601c90503660006133128560148189614452565b604080516001808252818301909252929450909250816020015b606081526020019060019003908161332c579050509350613384338484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506139ce92505050565b8460008151811061339757613397614768565b6020026020010181905250505050610818565b83604051632e5bf3f960e21b815260040161067c91906147d1565b6133d385600160f81b611d65565b15613559576133e684600160f81b611d65565b1561340b5782358301602081019035613400338383613a6f565b935061081892505050565b613416846000611d65565b156134a35760008036600061342b87876135db565b6040805160018082528183019092529498509296509094509250816020015b606081526020019060019003908161344a5790505094506132a733858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613b6592505050565b6134b5846001600160f81b0319611d65565b156133aa5760006134c96014828587614452565b6134d29161447c565b60601c90503660006134e78560148189614452565b604080516001808252818301909252929450909250816020015b6060815260200190600190039081613501579050509350613384338484848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613c2592505050565b846040516308c3ee0360e11b815260040161067c91906147d1565b6131c7837f000000000000000000000000000000000000000000000000000000000000000084846040516024016135ac929190614b66565b60408051601f198184030181529190526020810180516001600160e01b0316633f707e6b60e01b179052612d51565b60008036816135ed6014828789614452565b6135f69161447c565b60601c9350613609603460148789614452565b61361291614c18565b92506136218560348189614452565b949793965094505050565b6131c7837f00000000000000000000000000000000000000000000000000000000000000008484604051602401613664929190614b66565b60408051601f198184030181529190526020810180516001600160e01b0316632864481160e11b17905261308b565b60405163468721a760e01b81526000906001600160a01b0386169063468721a7906136c890879087908790879060040161481e565b6020604051808303816000875af11580156136e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061370b91906148d5565b905080611ee457604080516001600160a01b038716815260006020820152600080516020614dd9833981519152910160405180910390a15050505050565b600060016001600160a01b038316148015906108185750506001600160a01b0390811660009081526020938452604080822093831682529290935291205416151590565b6000808360018111156137a2576137a26147e6565b1480156137b757506001600160e01b03198216155b156137d75750336000908152600460205260409020546001600160a01b03165b60018360018111156137eb576137eb6147e6565b0361072257506001600160e01b03191660009081526005602090815260408083203384529091529020546001600160a01b0316919050565b600060d08265ffffffffffff16901b60a08465ffffffffffff16901b8561384b57600061384e565b60015b60ff161717949350505050565b6001600160a01b038116158061387a57506001600160a01b0381166001145b156138a357604051637c84ecfb60e01b81526001600160a01b038316600482015260240161067c565b6001600160a01b0382811660009081526020868152604080832087851684529091529020548116908216146138f657604051637c84ecfb60e01b81526001600160a01b038216600482015260240161067c565b6001600160a01b039081166000908152602085815260408083209584168084528683528184208054968616855297835281842090845282529091208054939092166001600160a01b031993841617909155919091528154169055565b60606139bb847f0000000000000000000000000000000000000000000000000000000000000000858560405160240161398c929190614b66565b60408051601f198184030181529190526020810180516001600160e01b0316636108557360e01b1790526139ce565b8060200190518101906108189190614ce5565b60606000846001600160a01b0316635229073f8560008660016040518563ffffffff1660e01b8152600401613a06949392919061481e565b6000604051808303816000875af1158015613a25573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613a4d9190810190614864565b9250905080612e0b57604051632b3f6d1160e21b815260040160405180910390fd5b6060806000613adb867f00000000000000000000000000000000000000000000000000000000000000008787604051602401613aac929190614b66565b60408051601f198184030181529190526020810180516001600160e01b0316639abb6e1760e01b179052613c25565b905080806020019051810190613af19190614d19565b8151919450925060005b81811015613b5a57848181518110613b1557613b15614768565b6020026020010151613b5257604080516001600160a01b038a16815260208101839052600080516020614dd9833981519152910160405180910390a15b600101613afb565b505050935093915050565b60606000856001600160a01b0316635229073f86868660006040518563ffffffff1660e01b8152600401613b9c949392919061481e565b6000604051808303816000875af1158015613bbb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613be39190810190614864565b9250905080611d5c57604080516001600160a01b038816815260006020820152600080516020614dd9833981519152910160405180910390a150949350505050565b60606000846001600160a01b0316635229073f8560008660016040518563ffffffff1660e01b8152600401613c5d949392919061481e565b6000604051808303816000875af1158015613c7c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613ca49190810190614864565b9250905080612e0b57604080516001600160a01b038716815260006020820152600080516020614dd9833981519152910160405180910390a1509392505050565b6001600160a01b0381168114613cfa57600080fd5b50565b8035611c5281613ce5565b60008083601f840112613d1a57600080fd5b5081356001600160401b03811115613d3157600080fd5b60208301915083602082850101111561060e57600080fd5b60008060008060608587031215613d5f57600080fd5b843593506020850135613d7181613ce5565b925060408501356001600160401b03811115613d8c57600080fd5b613d9887828801613d08565b95989497509550505050565b600080600060408486031215613db957600080fd5b8335925060208401356001600160401b03811115613dd657600080fd5b613de286828701613d08565b9497909650939450505050565b60006101208284031215613e0257600080fd5b50919050565b600080600060608486031215613e1d57600080fd5b83356001600160401b03811115613e3357600080fd5b613e3f86828701613def565b9660208601359650604090950135949350505050565b60008083601f840112613e6757600080fd5b5081356001600160401b03811115613e7e57600080fd5b6020830191508360208260051b850101111561060e57600080fd5b600060608284031215613e0257600080fd5b600080600080600080600080600060a08a8c031215613ec957600080fd5b89356001600160401b03811115613edf57600080fd5b613eeb8c828d01613e55565b909a5098505060208a01356001600160401b03811115613f0a57600080fd5b613f168c828d01613e55565b90985096505060408a01356001600160401b03811115613f3557600080fd5b613f418c828d01613e55565b90965094505060608a01356001600160401b03811115613f6057600080fd5b613f6c8c828d01613e55565b90945092505060808a01356001600160401b03811115613f8b57600080fd5b613f978c828d01613e99565b9150509295985092959850929598565b60008060408385031215613fba57600080fd5b82356001600160401b03811115613fd057600080fd5b613fdc85828601613def565b9250506020830135613fed81613ce5565b809150509250929050565b60005b83811015614013578181015183820152602001613ffb565b50506000910152565b60008151808452614034816020860160208601613ff8565b601f01601f19169290920160200192915050565b60808152600061405b608083018761401c565b65ffffffffffff8616602084015265ffffffffffff851660408401528281036060840152614089818561401c565b979650505050505050565b600080604083850312156140a757600080fd5b82356140b281613ce5565b946020939093013593505050565b6040808252835190820181905260009060208501906060840190835b818110156141035783516001600160a01b03168352602093840193909201916001016140dc565b50506001600160a01b039490941660209390930192909252509092915050565b6000806020838503121561413657600080fd5b82356001600160401b0381111561414c57600080fd5b61415885828601613e55565b90969095509350505050565b602081526000610b8f602083018461401c565b6001600160e01b031981168114613cfa57600080fd5b60006020828403121561419f57600080fd5b8135610b8f81614177565b6000602082840312156141bc57600080fd5b5035919050565b6000602082016020835280845180835260408501915060408160051b86010192506020860160005b8281101561421c57603f1987860301845261420785835161401c565b945060209384019391909101906001016141eb565b50929695505050505050565b6000806040838503121561423b57600080fd5b823561424681613ce5565b91506020830135613fed81613ce5565b803560ff81168114611c5257600080fd5b6000806000806060858703121561427d57600080fd5b843561428881613ce5565b935060208501356001600160401b038111156142a357600080fd5b6142af87828801613e55565b90945092506142c2905060408601614256565b905092959194509250565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60018060a01b038516815283602082015260606040820152600061431e6060830184866142cd565b9695505050505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561436657614366614328565b604052919050565b60006001600160401b0382111561438757614387614328565b50601f01601f191660200190565b600082601f8301126143a657600080fd5b81516143b96143b48261436e565b61433e565b8181528460208386010111156143ce57600080fd5b610818826020830160208701613ff8565b6000602082840312156143f157600080fd5b81516001600160401b0381111561440757600080fd5b61081884828501614395565b8284823760609190911b6bffffffffffffffffffffffff19169101908152601401919050565b60006020828403121561444b57600080fd5b5051919050565b6000808585111561446257600080fd5b8386111561446f57600080fd5b5050820193919092039150565b80356bffffffffffffffffffffffff1981169060148410156144c2576bffffffffffffffffffffffff196bffffffffffffffffffffffff198560140360031b1b82161691505b5092915050565b8481526060602082015260006144e2606083018661401c565b82810360408401526140898185876142cd565b60006020828403121561450757600080fd5b8151610b8f81614177565b6000808335601e1984360301811261452957600080fd5b83016020810192503590506001600160401b0381111561454857600080fd5b80360382131561060e57600080fd5b604081526145786040820161456b85613cfd565b6001600160a01b03169052565b6020830135606082015260006145916040850185614512565b61012060808501526145a8610160850182846142cd565b9150506145b86060860186614512565b848303603f190160a08601526145cf8382846142cd565b608088013560c08781019190915260a089013560e0808901919091529089013561010088015290935061460792508701905086614512565b848303603f190161012086015261461f8382846142cd565b92505050614631610100860186614512565b848303603f19016101408601526146498382846142cd565b93505050508260208301529392505050565b60006020828403121561466d57600080fd5b8135610b8f81613ce5565b6000808335601e1984360301811261468f57600080fd5b8301803591506001600160401b038211156146a957600080fd5b6020019150600581901b360382131561060e57600080fd5b6000602082840312156146d357600080fd5b610b8f82614256565b6000808335601e198436030181126146f357600080fd5b8301803591506001600160401b0382111561470d57600080fd5b60200191503681900382131561060e57600080fd5b80356001600160d01b031981169060068410156144c2576001600160d01b031960069490940360031b84901b1690921692915050565b8183823760009101908152919050565b634e487b7160e01b600052603260045260246000fd5b60008235603e1983360301811261479457600080fd5b9190910192915050565b8381526001600160a01b03831660208201526060604082018190526000906147c89083018461401c565b95945050505050565b6001600160f81b031991909116815260200190565b634e487b7160e01b600052602160045260246000fd5b6002811061481a57634e487b7160e01b600052602160045260246000fd5b9052565b60018060a01b0385168152836020820152608060408201526000614845608083018561401c565b90506147c860608301846147fc565b80518015158114611c5257600080fd5b6000806040838503121561487757600080fd5b61488083614854565b915060208301516001600160401b0381111561489b57600080fd5b6148a785828601614395565b9150509250929050565b6001600160a01b03831681526040602082018190526000906108189083018461401c565b6000602082840312156148e757600080fd5b610b8f82614854565b803560028110611c5257600080fd5b6000806040838503121561491257600080fd5b61491b836148f0565b91506020830135613fed81614177565b82815260008251614943816020850160208701613ff8565b919091016020019392505050565b83815260606020820152600061496a606083018561401c565b828103604084015261431e818561401c565b60ff84168152604060208201819052810182905260008360608301825b858110156149c95782356149ac81613ce5565b6001600160a01b0316825260209283019290910190600101614999565b509695505050505050565b634e487b7160e01b600052601160045260246000fd5b6000600182016149fc576149fc6149d4565b5060010190565b81810381811115610722576107226149d4565b600082601f830112614a2757600080fd5b8135614a356143b48261436e565b818152846020838601011115614a4a57600080fd5b816020850160208301376000918101602001919091529392505050565b600080600060608486031215614a7c57600080fd5b8335614a8781614177565b925060208401356001600160f81b031981168114614aa457600080fd5b915060408401356001600160401b03811115614abf57600080fd5b614acb86828701614a16565b9150509250925092565b600080600060608486031215614aea57600080fd5b614af3846148f0565b92506020840135614aa481614177565b60008060408385031215614b1657600080fd5b8235614b2181613ce5565b915060208301356001600160401b03811115614b3c57600080fd5b6148a785828601614a16565b60008060408385031215614b5b57600080fd5b8235614b2181614177565b6020808252810182905260006040600584901b830181019083018583605e1936839003015b87821015614c0b57868503603f190184528235818112614baa57600080fd5b89018035614bb781613ce5565b6001600160a01b0316865260208181013590870152614bd96040820182614512565b915060606040880152614bf06060880183836142cd565b96505050602083019250602084019350600182019150614b8b565b5092979650505050505050565b8035602083101561072257600019602084900360031b1b1692915050565b60006001600160401b03821115614c4f57614c4f614328565b5060051b60200190565b600082601f830112614c6a57600080fd5b8151614c786143b482614c36565b8082825260208201915060208360051b860101925085831115614c9a57600080fd5b602085015b83811015614cdb5780516001600160401b03811115614cbd57600080fd5b614ccc886020838a0101614395565b84525060209283019201614c9f565b5095945050505050565b600060208284031215614cf757600080fd5b81516001600160401b03811115614d0d57600080fd5b61081884828501614c59565b60008060408385031215614d2c57600080fd5b82516001600160401b03811115614d4257600080fd5b8301601f81018513614d5357600080fd5b8051614d616143b482614c36565b8082825260208201915060208360051b850101925087831115614d8357600080fd5b6020840193505b82841015614dac57614d9b84614854565b825260209384019390910190614d8a565b8095505050505060208301516001600160401b03811115614dcc57600080fd5b6148a785828601614c5956feb8bc84bd77f5eb08210b8eb20fd63b3ec6a7992d277ab94663bae0e066f792aca2646970667358221220ea7603497be0a0e1ddc106666c75af038c8db18b0f9143f6418b0e8b232c109164736f6c634300081a00336080604052348015600f57600080fd5b506109c28061001f6000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c80636a2216571161005b5780636a221657146100d357806378272525146100e65780639517e29f146100f95780639abb6e171461010c57600080fd5b80633f707e6b1461008257806350c890221461009757806361085573146100aa575b600080fd5b6100956100903660046105ba565b61012d565b005b6100956100a53660046105ba565b610197565b6100bd6100b83660046105ba565b6101fc565b6040516100ca91906106c4565b60405180910390f35b6100956100e1366004610710565b6102be565b6100956100f43660046107da565b6102e1565b6100956101073660046107da565b61038a565b61011f61011a3660046105ba565b610429565b6040516100ca929190610864565b8060005b81811015610191573684848381811061014c5761014c6108be565b905060200281019061015e91906108d4565b905061018761017060208301836108f4565b6020830135610182604085018561090f565b610553565b5050600101610131565b50505050565b8060005b8181101561019157368484838181106101b6576101b66108be565b90506020028101906101c891906108d4565b90506101f16101da60208301836108f4565b60208301356101ec604085018561090f565b610589565b50505060010161019b565b6060818067ffffffffffffffff811115610218576102186106fa565b60405190808252806020026020018201604052801561024b57816020015b60608152602001906001900390816102365790505b50915060005b818110156102b6573685858381811061026c5761026c6108be565b905060200281019061027e91906108d4565b905061029061017060208301836108f4565b8483815181106102a2576102a26108be565b602090810291909101015250600101610251565b505092915050565b604051600080835160208501865afa3d6000833e80156102dc573d82f35b503d81fd5b604051638a91b0e360e01b81526001600160a01b03841690638a91b0e39061030f908590859060040161095d565b600060405180830381600087803b15801561032957600080fd5b505af115801561033d573d6000803e3d6000fd5b5050604080518781526001600160a01b03871660208201527f341347516a9de374859dfda710fa4828b2d48cb57d4fbe4c1149612b8e02276e93500190505b60405180910390a150505050565b6040516306d61fe760e41b81526001600160a01b03841690636d61fe70906103b8908590859060040161095d565b600060405180830381600087803b1580156103d257600080fd5b505af11580156103e6573d6000803e3d6000fd5b5050604080518781526001600160a01b03871660208201527fd21d0b289f126c4b473ea641963e766833c2f13866e4ff480abd787c100ef123935001905061037c565b606080828067ffffffffffffffff811115610446576104466106fa565b60405190808252806020026020018201604052801561047957816020015b60608152602001906001900390816104645790505b5091508067ffffffffffffffff811115610495576104956106fa565b6040519080825280602002602001820160405280156104be578160200160208202803683370190505b50925060005b8181101561054a57368686838181106104df576104df6108be565b90506020028101906104f191906108d4565b90506105036101da60208301836108f4565b868481518110610515576105156108be565b6020026020010186858151811061052e5761052e6108be565b60209081029190910101919091529015159052506001016104c4565b50509250929050565b60405181838237600038838387895af1610570573d6000823e3d81fd5b3d8152602081013d6000823e3d01604052949350505050565b604051600090828482376000388483888a5af191503d8152602081013d6000823e3d81016040525094509492505050565b600080602083850312156105cd57600080fd5b823567ffffffffffffffff8111156105e457600080fd5b8301601f810185136105f557600080fd5b803567ffffffffffffffff81111561060c57600080fd5b8560208260051b840101111561062157600080fd5b6020919091019590945092505050565b600082825180855260208501945060208160051b8301016020850160005b838110156106b857848303601f190188528151805180855260005b818110156106865760208184018101518783018201520161066a565b506000602082870101526020601f19601f8301168601019450505060208201915060208801975060018101905061064f565b50909695505050505050565b6020815260006106d76020830184610631565b9392505050565b80356001600160a01b03811681146106f557600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561072357600080fd5b61072c836106de565b9150602083013567ffffffffffffffff81111561074857600080fd5b8301601f8101851361075957600080fd5b803567ffffffffffffffff811115610773576107736106fa565b604051601f8201601f19908116603f0116810167ffffffffffffffff811182821017156107a2576107a26106fa565b6040528181528282016020018710156107ba57600080fd5b816020840160208301376000602083830101528093505050509250929050565b600080600080606085870312156107f057600080fd5b84359350610800602086016106de565b9250604085013567ffffffffffffffff81111561081c57600080fd5b8501601f8101871361082d57600080fd5b803567ffffffffffffffff81111561084457600080fd5b87602082840101111561085657600080fd5b949793965060200194505050565b6040808252835190820181905260009060208501906060840190835b818110156108a05783511515835260209384019390920191600101610880565b505083810360208501526108b48186610631565b9695505050505050565b634e487b7160e01b600052603260045260246000fd5b60008235605e198336030181126108ea57600080fd5b9190910192915050565b60006020828403121561090657600080fd5b6106d7826106de565b6000808335601e1984360301811261092657600080fd5b83018035915067ffffffffffffffff82111561094157600080fd5b60200191503681900382131561095657600080fd5b9250929050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f1916010191905056fea26469706673582212205c0679bac7989901111b6824de732603e322da5b9b75ee077344853294d8b7b664736f6c634300081a0033
Deployed Bytecode
0x60806040526004361061012e5760003560e01c8063a71763a8116100ab578063d828435d1161006f578063d828435d146104a2578063e9ae5c53146104c2578063ea5f61d0146104e2578063eab77e1714610502578063f2dc691d14610522578063f698da251461054257610135565b8063a71763a8146103c7578063b0d691fe146103e7578063b875d5d81461040a578063d03c791414610455578063d691c9641461047557610135565b806355d559f4116100f257806355d559f4146102dd5780635faac46b1461030d57806379aad60e1461033b5780639517e29f1461035b5780639cfd7cff1461037b57610135565b80630a664dba146101e0578063112d3a7d146102245780631626ba7e1461025457806319822f7c1461028d578063540fb4f9146102bb57610135565b3661013557005b600036606060003560e01c63bc197c81811463f23a6e6182141763150b7a028214171561016657806020526020603cf35b50336000818152600460209081526040808320546001600160e01b031984351680855260058452828520958552949092528220546001600160a01b0391821692911690806101b48484610557565b915091506101c28888610615565b95506101d084848484610728565b5050505050915050805190602001f35b3480156101ec57600080fd5b50336000908152600460205260409020546001600160a01b03165b6040516001600160a01b0390911681526020015b60405180910390f35b34801561023057600080fd5b5061024461023f366004613d49565b6107ab565b604051901515815260200161021b565b34801561026057600080fd5b5061027461026f366004613da4565b610820565b6040516001600160e01b0319909116815260200161021b565b34801561029957600080fd5b506102ad6102a8366004613e08565b610b96565b60405190815260200161021b565b3480156102c757600080fd5b506102db6102d6366004613eab565b610caa565b005b3480156102e957600080fd5b506102fd6102f8366004613fa7565b610d5a565b60405161021b9493929190614048565b34801561031957600080fd5b5061032d610328366004614094565b610fb7565b60405161021b9291906140c0565b34801561034757600080fd5b506102db610356366004614123565b610fd3565b34801561036757600080fd5b506102db610376366004613d49565b6110c8565b34801561038757600080fd5b50604080518082018252601a81527f7268696e6573746f6e652e73616665373537392e76312e302e300000000000006020820152905161021b9190614164565b3480156103d357600080fd5b506102db6103e2366004613d49565b611293565b3480156103f357600080fd5b506f71727de22e5e9d8baf0edac6f37da032610207565b34801561041657600080fd5b5061020761042536600461418d565b6001600160e01b03191660009081526005602090815260408083203384529091529020546001600160a01b031690565b34801561046157600080fd5b506102446104703660046141aa565b6115ba565b34801561048157600080fd5b50610495610490366004613da4565b611662565b60405161021b91906141c3565b3480156104ae57600080fd5b506102ad6104bd366004614228565b61174b565b3480156104ce57600080fd5b506102db6104dd366004613da4565b6117eb565b3480156104ee57600080fd5b5061032d6104fd366004614094565b611b88565b34801561050e57600080fd5b506102db61051d366004614267565b611b99565b34801561052e57600080fd5b5061024461053d3660046141aa565b611c08565b34801561054e57600080fd5b506102ad611c57565b6060806001600160a01b038416156105d7576105be33856000610578611cb0565b3460003660405160240161058f94939291906142f6565b60408051601f198184030181529190526020810180516001600160e01b031663d68f602560e01b179052611cbc565b9150818060200190518101906105d491906143df565b91505b6001600160a01b0383161561060e576105f533846000610578611cb0565b90508080602001905181019061060b91906143df565b90505b9250929050565b600080356001600160e01b031916815260036020908152604080832033845290915290208054606091906001600160a01b03811690600160a01b900460f81b8161068557604051632464e76d60e11b81526001600160e01b03196000351660048201526024015b60405180910390fd5b61069381607f60f91b611d65565b156106d7576106cd338388886106a7611cb0565b6040516020016106b993929190614413565b604051602081830303815290604052611d77565b9350505050610722565b6106e2816000611d65565b1561071e576106cd3383600089896106f8611cb0565b60405160200161070a93929190614413565b604051602081830303815290604052611cbc565b5050505b92915050565b6001600160a01b0384161561077e5761077e338560008560405160240161074f9190614164565b60408051601f198184030181529190526020810180516001600160e01b0316630b9dfbed60e11b179052611e4c565b6001600160a01b038316156107a5576107a5338460008460405160240161074f9190614164565b50505050565b6000600185036107dd57336001600160a01b038516036107cd57506001610818565b6107d684611eeb565b9050610818565b600285036107ee576107d684611ef9565b60038503610801576107d6848484611f07565b60048503610814576107d6848484611f53565b5060005b949350505050565b6000338282036109ba5760006108f1826001600160a01b031663f698da256040518163ffffffff1660e01b8152600401602060405180830381865afa15801561086d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108919190614439565b60408051602081018a90527f60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca91015b60408051601f1981840301815282825280516020918201209083015201604051602081830303815290604052611f8e565b8051602090910120604051635ae6bd3760e01b8152600481018290529091506001600160a01b03831690635ae6bd3790602401602060405180830381865afa158015610941573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109659190614439565b6000036109a85760405162461bcd60e51b815260206004820152601160248201527012185cda081b9bdd08185c1c1c9bdd9959607a1b604482015260640161067c565b50630b135d3f60e11b9150610b8f9050565b60006109c96014828688614452565b6109d29161447c565b60601c90508015806109ea57506109e881611eeb565b155b15610b14576000610a89836001600160a01b031663f698da256040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a569190614439565b60408051602081018b90527f60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca91016108c0565b805160208201209091506001600160a01b03841663934f3a118284610ab18a6014818e614452565b6040518563ffffffff1660e01b8152600401610ad094939291906144c9565b60006040518083038186803b158015610ae857600080fd5b505afa158015610afc573d6000803e3d6000fd5b50630b135d3f60e11b9750610b8f9650505050505050565b6000610b733383610b23611cb0565b8a610b318a6014818e614452565b604051602401610b4494939291906142f6565b60408051601f198184030181529190526020810180516001600160e01b0316637aa8f17760e11b179052611d77565b905080806020019051810190610b8991906144f5565b93505050505b9392505050565b60006f71727de22e5e9d8baf0edac6f37da032610bb1611cb0565b6001600160a01b031614610bd857604051635629665f60e11b815260040160405180910390fd5b6020840135606081901c90811580610bf65750610bf482611eeb565b155b15610c0b57610c0486612008565b9250610c70565b6000610c56338460008a8a604051602401610c27929190614557565b60408051601f198184030181529190526020810180516001600160e01b0316639700320360e01b179052611cbc565b905080806020019051810190610c6c9190614439565b9350505b8315610ca157610ca1336f71727de22e5e9d8baf0edac6f37da0328660405180602001604052806000815250611e4c565b50509392505050565b6f71727de22e5e9d8baf0edac6f37da032610cc3611cb0565b6001600160a01b03161480610cf05750610cdb611cb0565b6001600160a01b0316336001600160a01b0316145b610d0d57604051635629665f60e11b815260040160405180910390fd5b610d3f610d1d602083018361465b565b610d2a6020840184614678565b610d3a60608601604087016146c1565b6120b7565b610d4f898989898989898961216e565b505050505050505050565b6060600080823682610d706101008901896146dc565b9092509050610d83600660008385614452565b610d8c91614722565b60d01c9450610d9f600c60068385614452565b610da891614722565b60d01c9350610dba81600c8185614452565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250604080516101c081019091527fc03dfc11d8b10bf9cf703d558958c8c42777f785d998c62060d85a4f0ef6ea7f815294975095509293505060208084019250610e3691508a018a61465b565b6001600160a01b0316815260200188602001358152602001888060400190610e5e91906146dc565b604051610e6c929190614758565b6040519081900390208152602001610e8760608a018a6146dc565b604051610e95929190614758565b60405180910390208152602001610eab8961248d565b6001600160801b03168152602001610ec28961249d565b6001600160801b031681526020018860a001358152602001610ee3896124b2565b6001600160801b03168152602001610efa896124c1565b6001600160801b03168152602001610f1560e08a018a6146dc565b604051610f23929190614758565b6040805191829003909120825265ffffffffffff80881660208401528616908201526001600160a01b0388166060909101526101c08120909150601960f81b600160f81b610f6f611c57565b6040516001600160f81b031993841660208201529290911660218301526022820152604281018290526062016040516020818303038152906040529550505092959194509250565b60606000610fc860013386866124d6565b915091509250929050565b6f71727de22e5e9d8baf0edac6f37da032610fec611cb0565b6001600160a01b031614806110195750611004611cb0565b6001600160a01b0316336001600160a01b0316145b61103657604051635629665f60e11b815260040160405180910390fd5b61104160013361269a565b8060005b81811015611097573684848381811061106057611060614768565b9050602002810190611072919061477e565b905061108e33611085602084018461465b565b60019190612715565b50600101611045565b5060405133907ff48581d8a62b775b74f2fb67f1d5806a9a356fbcc598040ab3071d3e37af40c290600090a2505050565b336000908152600460209081526040808320547fd00bcad91b775f25eb0aee15808d12dfdaa6dc5a7d6b754e26f9c205d2a3b8df909252822054639517e29f60e01b926001600160a01b039283169290911690806111268484610557565b915091506111416f71727de22e5e9d8baf0edac6f37da03290565b6001600160a01b0316611152611cb0565b6001600160a01b0316148061117f575061116a611cb0565b6001600160a01b0316336001600160a01b0316145b61119c57604051635629665f60e11b815260040160405180910390fd5b606060018a036111b8576111b189898961280a565b905061121d565b60028a036111cb576111b1898989612866565b60038a036111de576111b1898989612881565b60048a036111f1576111b1898989612a00565b89611201576111b1898989612ba0565b60405163041c38b360e41b8152600481018b905260240161067c565b611286337f00000000000000000000000045d7deb1229e0254a0561a2769b4a28e4e67df008c8c856040516024016112579392919061479e565b60408051601f198184030181529190526020810180516001600160e01b0316639517e29f60e01b179052612d51565b50610d4f84848484610728565b336000908152600460209081526040808320547fc45a7acaba78a7b4c4432b604d301355b314c0a7811768a1ee2563c9d34ca4e09092529091205484916314e2ec7560e31b916001600160a01b0391821691908116908416821480159061130c5750806001600160a01b0316846001600160a01b031614155b156114925760008061131e8484610557565b915091506113396f71727de22e5e9d8baf0edac6f37da03290565b6001600160a01b031661134a611cb0565b6001600160a01b031614806113775750611362611cb0565b6001600160a01b0316336001600160a01b0316145b61139457604051635629665f60e11b815260040160405180910390fd5b606060018b036113b0576113a98a8a8a612dea565b9050611415565b60028b036113c3576113a98a8a8a612e13565b60038b036113d6576113a98a8a8a612e34565b60048b036113e9576113a98a8a8a612e80565b8a6113f9576113a98a8a8a612f38565b60405163041c38b360e41b8152600481018c905260240161067c565b61147e337f00000000000000000000000045d7deb1229e0254a0561a2769b4a28e4e67df008d8d8560405160240161144f9392919061479e565b60408051601f198184030181529190526020810180516001600160e01b0316637827252560e01b17905261308b565b5061148b84848484610728565b50506115b0565b6f71727de22e5e9d8baf0edac6f37da0326114ab611cb0565b6001600160a01b031614806114d857506114c3611cb0565b6001600160a01b0316336001600160a01b0316145b6114f557604051635629665f60e11b815260040160405180910390fd5b6060600189036115115761150a888888612dea565b9050611576565b600289036115245761150a888888612e13565b600389036115375761150a888888612e34565b6004890361154a5761150a888888612e80565b8861155a5761150a888888612f38565b60405163041c38b360e41b8152600481018a905260240161067c565b610d4f337f00000000000000000000000045d7deb1229e0254a0561a2769b4a28e4e67df008b8b8560405160240161144f9392919061479e565b5050505050505050565b600081600881901b6115d082600160f81b611d65565b156115de5760019250611621565b6115e9826000611d65565b156115f75760019250611621565b611609826001600160f81b0319611d65565b156116175760019250611621565b5060009392505050565b8280156116345750611634816000611d65565b15611640575050919050565b828015611656575061165681600160f81b611d65565b15611617575050919050565b606061167461166f611cb0565b611ef9565b6116a557611680611cb0565b604051635c93ff2f60e11b81526001600160a01b03909116600482015260240161067c565b336000908152600460209081526040808320547f4162c930ade27880570b0eb31efbc8a626b97e81a40ced27163509dfe16398829092528220546335a4725960e21b926001600160a01b039283169290911690806117038484610557565b9150915061170f611cb0565b600261171b8282613141565b8a600881901b61172d81838e8e6131cc565b99505050505061173f84848484610728565b50505050509392505050565b6000602082901b640100000000600160c01b03166f71727de22e5e9d8baf0edac6f37da032604051631aab3f0d60e11b81526001600160a01b0386811660048301526001600160c01b038416602483015291909116906335567e1a90604401602060405180830381865afa1580156117c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108189190614439565b336000908152600460209081526040808320547fccfe1b2bef70acd8e661aeb5430e147f4c75cb320972acba718a28da428e1a3090925282205463e9ae5c5360e01b926001600160a01b039283169290911690806118498484610557565b915091506118646f71727de22e5e9d8baf0edac6f37da03290565b6001600160a01b0316611875611cb0565b6001600160a01b031614806118a2575061188d611cb0565b6001600160a01b0316336001600160a01b0316145b6118bf57604051635629665f60e11b815260040160405180910390fd5b87600881901b336118d1826000611d65565b15611a2a576118e483600160f81b611d65565b156119055789358a016020810190356118fe838383613574565b5050611b79565b611910836000611d65565b15611978576000803660006119258e8e6135db565b935093509350935061196f85858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611e4c92505050565b50505050611b79565b61198a836001600160f81b0319611d65565b15611a0f57600061199e6014828c8e614452565b6119a79161447c565b60601c90503660008c8c60149080926119c293929190614452565b91509150611a07848484848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612d5192505050565b505050611b79565b82604051632e5bf3f960e21b815260040161067c91906147d1565b611a3882600160f81b611d65565b15611b5e57611a4b83600160f81b611d65565b15611a655789358a016020810190356118fe83838361362c565b611a70836000611d65565b15611acf57600080366000611a858e8e6135db565b935093509350935061196f85858585858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061369392505050565b611ae1836001600160f81b0319611d65565b15611a0f576000611af56014828c8e614452565b611afe9161447c565b60601c90503660008c8c6014908092611b1993929190614452565b91509150611a07848484848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061308b92505050565b816040516308c3ee0360e11b815260040161067c91906147d1565b5050506115b084848484610728565b60606000610fc860023386866124d6565b6f71727de22e5e9d8baf0edac6f37da032611bb2611cb0565b6001600160a01b03161480611bdf5750611bca611cb0565b6001600160a01b0316336001600160a01b0316145b611bfc57604051635629665f60e11b815260040160405180910390fd5b6107a5848484846120b7565b600060018203611c1a57506001919050565b60028203611c2a57506001919050565b60038203611c3a57506001919050565b60048203611c4a57506001919050565b506000919050565b919050565b604080517f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a794692186020820152469181019190915230606082015260009060800160405160208183030381529060405280519060200120905090565b60131936013560601c90565b60606000856001600160a01b0316635229073f86868660006040518563ffffffff1660e01b8152600401611cf3949392919061481e565b6000604051808303816000875af1158015611d12573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611d3a9190810190614864565b9250905080611d5c57604051632b3f6d1160e21b815260040160405180910390fd5b50949350505050565b6001600160f81b031990811691161490565b606060008383604051602401611d8e9291906148b1565b60408051601f198184030181529181526020820180516001600160e01b0316636a22165760e01b17905251909150600090611def907f00000000000000000000000045d7deb1229e0254a0561a2769b4a28e4e67df009084906024016148b1565b60408051601f19818403018152919052602080820180516001600160e01b031663b4faba0960e01b17815282519293509091600091895afa5060203d036040519350808401604052806020853e50600051610ca157825160208401fd5b60405163468721a760e01b81526000906001600160a01b0386169063468721a790611e8190879087908790879060040161481e565b6020604051808303816000875af1158015611ea0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ec491906148d5565b905080611ee457604051632b3f6d1160e21b815260040160405180910390fd5b5050505050565b600061072260013384613749565b600061072260023384613749565b600080611f168385018561418d565b6001600160e01b03191660009081526003602090815260408083203384529091529020546001600160a01b03908116908616149150509392505050565b60008080611f63848601866148ff565b915091506000611f73838361378d565b6001600160a01b039081169088161493505050509392505050565b6060601960f81b600160f81b858585604051602001611fae92919061492b565b60408051808303601f190181529082905280516020918201206001600160f81b0319958616918301919091529290931660218401526022830152604282015260620160405160208183030381529060405290509392505050565b600080808080612028866f71727de22e5e9d8baf0edac6f37da032610d5a565b8351602085012060405163934f3a1160e01b81529498509296509094509250339163934f3a119161205f9188908690600401614951565b60006040518083038186803b15801561207757600080fd5b505afa925050508015612088575060015b61209f5761209860018385613823565b94506120ae565b6120ab60008385613823565b94505b50505050919050565b33600090815260208190526040902080546001600160a01b0319166001600160a01b0386169081179091551561213257612132338560008487876040516024016121039392919061497c565b60408051601f198184030181529190526020810180516001600160e01b031663f05c04e160e01b179052611e4c565b6040516001600160a01b0385169033907f9452c8fb077c3ea8f28a77c87488af657b1e44d010ad9a5992d73870da040e9490600090a350505050565b3360009081527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f602052604090205460609088906001600160a01b0316612266576121ba60013361269a565b60005b8181101561226057368b8b838181106121d8576121d8614768565b90506020028101906121ea919061477e565b905061220e6121fc602083018361465b565b61220960208401846146dc565b61280a565b9350612257337f00000000000000000000000045d7deb1229e0254a0561a2769b4a28e4e67df006001612244602086018661465b565b886040516024016112579392919061479e565b506001016121bd565b50612287565b80156122875760405163d8e3ed1b60e01b815233600482015260240161067c565b61229260023361269a565b508560005b8181101561232757368989838181106122b2576122b2614768565b90506020028101906122c4919061477e565b90506122e86122d6602083018361465b565b6122e360208401846146dc565b612866565b935061231e337f00000000000000000000000045d7deb1229e0254a0561a2769b4a28e4e67df006002612244602086018661465b565b50600101612297565b5084905060005b818110156123be573687878381811061234957612349614768565b905060200281019061235b919061477e565b905061237f61236d602083018361465b565b61237a60208401846146dc565b612881565b93506123b5337f00000000000000000000000045d7deb1229e0254a0561a2769b4a28e4e67df006003612244602086018661465b565b5060010161232e565b5082905060005b8181101561245557368585838181106123e0576123e0614768565b90506020028101906123f2919061477e565b9050612416612404602083018361465b565b61241160208401846146dc565b612a00565b935061244c337f00000000000000000000000045d7deb1229e0254a0561a2769b4a28e4e67df006004612244602086018661465b565b506001016123c5565b5060405133907ff48581d8a62b775b74f2fb67f1d5806a9a356fbcc598040ab3071d3e37af40c290600090a250505050505050505050565b6000610722826080013560801c90565b60006001600160801b03608083013516610722565b600060c082013560801c610722565b60006001600160801b0360c083013516610722565b606060006001600160a01b0384166001148015906124fc57506124fa868686613749565b155b1561252557604051637c84ecfb60e01b81526001600160a01b038516600482015260240161067c565b826000036125465760405163f725081760e01b815260040160405180910390fd5b826001600160401b0381111561255e5761255e614328565b604051908082528060200260200182016040528015612587578160200160208202803683370190505b506001600160a01b038086166000908152602089815260408083208a85168452909152812054929450911691505b6001600160a01b038216158015906125d757506001600160a01b038216600114155b80156125e257508381105b1561264757818382815181106125fa576125fa614768565b6001600160a01b039283166020918202929092018101919091529281166000908152888452604080822089841683529094529290922054909116908061263f816149ea565b9150506125b5565b6001600160a01b0382166001148015906126615750600081115b1561268d5782612672600183614a03565b8151811061268257612682614768565b602002602001015191505b8083525094509492505050565b60016000908152602083815260408083206001600160a01b03808616855292529091205416156126dd576040516329e42f3360e11b815260040160405180910390fd5b60016000818152602093845260408082206001600160a01b0394909416825292909352912080546001600160a01b0319169091179055565b6001600160a01b038116158061273457506001600160a01b0381166001145b1561275d57604051637c84ecfb60e01b81526001600160a01b038216600482015260240161067c565b6001600160a01b03818116600090815260208581526040808320868516845290915290205416156127ac57604051631034f46960e21b81526001600160a01b038216600482015260240161067c565b60016000908152602084815260408083206001600160a01b039586168085528184528285208054968816808752988552838620918652908452919093208054949095166001600160a01b031994851617909455528154169091179055565b60608360016128198282613141565b61282560013388612715565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929998505050505050505050565b60608360026128758282613141565b61282560023388612715565b60608360036128908282613141565b600080806128a087890189614a67565b919450925090506001600160e01b031983166306d61fe760e41b14806128d657506001600160e01b03198316638a91b0e360e01b145b15612900576040516379bd117b60e01b81526001600160e01b03198416600482015260240161067c565b61290b826000611d65565b8015612920575061292082607f60f91b611d65565b1561294057816040516376087dc160e01b815260040161067c91906147d1565b6001600160e01b0319831660009081526003602090815260408083203384529091529020546001600160a01b031615612998576040516374420d1560e01b81526001600160e01b03198416600482015260240161067c565b6001600160e01b03199092166000908152600360209081526040808320338452909152902080546001600160a01b038a166001600160a01b031960f89490941c600160a01b02939093166001600160a81b031990911617919091179055925050509392505050565b6060836004612a0f8282613141565b60008080612a1f87890189614ad5565b91945092509050600080846001811115612a3b57612a3b6147e6565b148015612a5057506001600160e01b03198316155b15612ac65750336000908152600460205260409020546001600160a01b03168015612a995760405163741cbe0360e01b81526001600160a01b038216600482015260240161067c565b33600090815260046020526040902080546001600160a01b0319166001600160a01b038c16179055612b93565b6001846001811115612ada57612ada6147e6565b03612b7a57506001600160e01b0319821660009081526005602090815260408083203384529091529020546001600160a01b03168015612b385760405163741cbe0360e01b81526001600160a01b038216600482015260240161067c565b6001600160e01b031983166000908152600560209081526040808320338452909152902080546001600160a01b0319166001600160a01b038c16179055612b93565b604051635691922f60e01b815260040160405180910390fd5b5098975050505050505050565b60608235830160208181019135908581013586018082019190359060408801358801908101903584838114612be85760405163b4fa3fb360e01b815260040160405180910390fd5b60005b81811015612d0a576000888883818110612c0757612c07614768565b90506020020135905060018103612c4657612c408d888885818110612c2e57612c2e614768565b905060200281019061220991906146dc565b50612d01565b60028103612c7657612c408d888885818110612c6457612c64614768565b90506020028101906122e391906146dc565b60038103612ca657612c408d888885818110612c9457612c94614768565b905060200281019061237a91906146dc565b60048103612cd657612c408d888885818110612cc457612cc4614768565b905060200281019061241191906146dc565b60405163484d218160e01b81526001600160a01b038e1660048201526024810182905260440161067c565b50600101612beb565b5082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929e9d5050505050505050505050505050565b60405163468721a760e01b81526000906001600160a01b0385169063468721a790612d879086908590879060019060040161481e565b6020604051808303816000875af1158015612da6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dca91906148d5565b9050806107a557604051632b3f6d1160e21b815260040160405180910390fd5b60606000612dfa83850185614b03565b92509050612e0b600133838861385b565b509392505050565b60606000612e2383850185614b03565b92509050612e0b600233838861385b565b60606000612e4483850185614b48565b6001600160e01b03199091166000908152600360209081526040808320338452909152902080546001600160a01b031916905595945050505050565b6060600080612e9184860186614ad5565b945090925090506000826001811115612eac57612eac6147e6565b148015612ec157506001600160e01b03198116155b15612ee85733600090815260046020526040902080546001600160a01b0319169055610ca1565b6001826001811115612efc57612efc6147e6565b03612b7a576001600160e01b031981166000908152600560209081526040808320338452909152902080546001600160a01b0319169055610ca1565b60608235830160208181019135908581013586018082019190359060408801358801908101903584838114612f805760405163b4fa3fb360e01b815260040160405180910390fd5b60005b81811015612d0a576000888883818110612f9f57612f9f614768565b90506020020135905060018103612fe357612fdd8d888885818110612fc657612fc6614768565b9050602002810190612fd891906146dc565b612dea565b50613082565b6002810361301857612fdd8d88888581811061300157613001614768565b905060200281019061301391906146dc565b612e13565b6003810361304d57612fdd8d88888581811061303657613036614768565b905060200281019061304891906146dc565b612e34565b60048103612cd657612fdd8d88888581811061306b5761306b614768565b905060200281019061307d91906146dc565b612e80565b50600101612f83565b60405163468721a760e01b81526000906001600160a01b0385169063468721a7906130c19086908590879060019060040161481e565b6020604051808303816000875af11580156130e0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061310491906148d5565b9050806107a557604080516001600160a01b038616815260006020820152600080516020614dd9833981519152910160405180910390a150505050565b336000908152602081905260409020546001600160a01b031680156131c75760405163529562a160e01b81523360048201526001600160a01b0384811660248301526044820184905282169063529562a19060640160006040518083038186803b1580156131ae57600080fd5b505afa1580156131c2573d6000803e3d6000fd5b505050505b505050565b60606131d9856000611d65565b156133c5576131ec84600160f81b611d65565b1561320f5782358301602081019035613206338383613952565b92505050610818565b61321a846000611d65565b156132ce5760008036600061322f87876135db565b6040805160018082528183019092529498509296509094509250816020015b606081526020019060019003908161324e5790505094506132a733858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611cbc92505050565b856000815181106132ba576132ba614768565b602002602001018190525050505050610818565b6132e0846001600160f81b0319611d65565b156133aa5760006132f46014828587614452565b6132fd9161447c565b60601c90503660006133128560148189614452565b604080516001808252818301909252929450909250816020015b606081526020019060019003908161332c579050509350613384338484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506139ce92505050565b8460008151811061339757613397614768565b6020026020010181905250505050610818565b83604051632e5bf3f960e21b815260040161067c91906147d1565b6133d385600160f81b611d65565b15613559576133e684600160f81b611d65565b1561340b5782358301602081019035613400338383613a6f565b935061081892505050565b613416846000611d65565b156134a35760008036600061342b87876135db565b6040805160018082528183019092529498509296509094509250816020015b606081526020019060019003908161344a5790505094506132a733858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613b6592505050565b6134b5846001600160f81b0319611d65565b156133aa5760006134c96014828587614452565b6134d29161447c565b60601c90503660006134e78560148189614452565b604080516001808252818301909252929450909250816020015b6060815260200190600190039081613501579050509350613384338484848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613c2592505050565b846040516308c3ee0360e11b815260040161067c91906147d1565b6131c7837f00000000000000000000000045d7deb1229e0254a0561a2769b4a28e4e67df0084846040516024016135ac929190614b66565b60408051601f198184030181529190526020810180516001600160e01b0316633f707e6b60e01b179052612d51565b60008036816135ed6014828789614452565b6135f69161447c565b60601c9350613609603460148789614452565b61361291614c18565b92506136218560348189614452565b949793965094505050565b6131c7837f00000000000000000000000045d7deb1229e0254a0561a2769b4a28e4e67df008484604051602401613664929190614b66565b60408051601f198184030181529190526020810180516001600160e01b0316632864481160e11b17905261308b565b60405163468721a760e01b81526000906001600160a01b0386169063468721a7906136c890879087908790879060040161481e565b6020604051808303816000875af11580156136e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061370b91906148d5565b905080611ee457604080516001600160a01b038716815260006020820152600080516020614dd9833981519152910160405180910390a15050505050565b600060016001600160a01b038316148015906108185750506001600160a01b0390811660009081526020938452604080822093831682529290935291205416151590565b6000808360018111156137a2576137a26147e6565b1480156137b757506001600160e01b03198216155b156137d75750336000908152600460205260409020546001600160a01b03165b60018360018111156137eb576137eb6147e6565b0361072257506001600160e01b03191660009081526005602090815260408083203384529091529020546001600160a01b0316919050565b600060d08265ffffffffffff16901b60a08465ffffffffffff16901b8561384b57600061384e565b60015b60ff161717949350505050565b6001600160a01b038116158061387a57506001600160a01b0381166001145b156138a357604051637c84ecfb60e01b81526001600160a01b038316600482015260240161067c565b6001600160a01b0382811660009081526020868152604080832087851684529091529020548116908216146138f657604051637c84ecfb60e01b81526001600160a01b038216600482015260240161067c565b6001600160a01b039081166000908152602085815260408083209584168084528683528184208054968616855297835281842090845282529091208054939092166001600160a01b031993841617909155919091528154169055565b60606139bb847f00000000000000000000000045d7deb1229e0254a0561a2769b4a28e4e67df00858560405160240161398c929190614b66565b60408051601f198184030181529190526020810180516001600160e01b0316636108557360e01b1790526139ce565b8060200190518101906108189190614ce5565b60606000846001600160a01b0316635229073f8560008660016040518563ffffffff1660e01b8152600401613a06949392919061481e565b6000604051808303816000875af1158015613a25573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613a4d9190810190614864565b9250905080612e0b57604051632b3f6d1160e21b815260040160405180910390fd5b6060806000613adb867f00000000000000000000000045d7deb1229e0254a0561a2769b4a28e4e67df008787604051602401613aac929190614b66565b60408051601f198184030181529190526020810180516001600160e01b0316639abb6e1760e01b179052613c25565b905080806020019051810190613af19190614d19565b8151919450925060005b81811015613b5a57848181518110613b1557613b15614768565b6020026020010151613b5257604080516001600160a01b038a16815260208101839052600080516020614dd9833981519152910160405180910390a15b600101613afb565b505050935093915050565b60606000856001600160a01b0316635229073f86868660006040518563ffffffff1660e01b8152600401613b9c949392919061481e565b6000604051808303816000875af1158015613bbb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613be39190810190614864565b9250905080611d5c57604080516001600160a01b038816815260006020820152600080516020614dd9833981519152910160405180910390a150949350505050565b60606000846001600160a01b0316635229073f8560008660016040518563ffffffff1660e01b8152600401613c5d949392919061481e565b6000604051808303816000875af1158015613c7c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613ca49190810190614864565b9250905080612e0b57604080516001600160a01b038716815260006020820152600080516020614dd9833981519152910160405180910390a1509392505050565b6001600160a01b0381168114613cfa57600080fd5b50565b8035611c5281613ce5565b60008083601f840112613d1a57600080fd5b5081356001600160401b03811115613d3157600080fd5b60208301915083602082850101111561060e57600080fd5b60008060008060608587031215613d5f57600080fd5b843593506020850135613d7181613ce5565b925060408501356001600160401b03811115613d8c57600080fd5b613d9887828801613d08565b95989497509550505050565b600080600060408486031215613db957600080fd5b8335925060208401356001600160401b03811115613dd657600080fd5b613de286828701613d08565b9497909650939450505050565b60006101208284031215613e0257600080fd5b50919050565b600080600060608486031215613e1d57600080fd5b83356001600160401b03811115613e3357600080fd5b613e3f86828701613def565b9660208601359650604090950135949350505050565b60008083601f840112613e6757600080fd5b5081356001600160401b03811115613e7e57600080fd5b6020830191508360208260051b850101111561060e57600080fd5b600060608284031215613e0257600080fd5b600080600080600080600080600060a08a8c031215613ec957600080fd5b89356001600160401b03811115613edf57600080fd5b613eeb8c828d01613e55565b909a5098505060208a01356001600160401b03811115613f0a57600080fd5b613f168c828d01613e55565b90985096505060408a01356001600160401b03811115613f3557600080fd5b613f418c828d01613e55565b90965094505060608a01356001600160401b03811115613f6057600080fd5b613f6c8c828d01613e55565b90945092505060808a01356001600160401b03811115613f8b57600080fd5b613f978c828d01613e99565b9150509295985092959850929598565b60008060408385031215613fba57600080fd5b82356001600160401b03811115613fd057600080fd5b613fdc85828601613def565b9250506020830135613fed81613ce5565b809150509250929050565b60005b83811015614013578181015183820152602001613ffb565b50506000910152565b60008151808452614034816020860160208601613ff8565b601f01601f19169290920160200192915050565b60808152600061405b608083018761401c565b65ffffffffffff8616602084015265ffffffffffff851660408401528281036060840152614089818561401c565b979650505050505050565b600080604083850312156140a757600080fd5b82356140b281613ce5565b946020939093013593505050565b6040808252835190820181905260009060208501906060840190835b818110156141035783516001600160a01b03168352602093840193909201916001016140dc565b50506001600160a01b039490941660209390930192909252509092915050565b6000806020838503121561413657600080fd5b82356001600160401b0381111561414c57600080fd5b61415885828601613e55565b90969095509350505050565b602081526000610b8f602083018461401c565b6001600160e01b031981168114613cfa57600080fd5b60006020828403121561419f57600080fd5b8135610b8f81614177565b6000602082840312156141bc57600080fd5b5035919050565b6000602082016020835280845180835260408501915060408160051b86010192506020860160005b8281101561421c57603f1987860301845261420785835161401c565b945060209384019391909101906001016141eb565b50929695505050505050565b6000806040838503121561423b57600080fd5b823561424681613ce5565b91506020830135613fed81613ce5565b803560ff81168114611c5257600080fd5b6000806000806060858703121561427d57600080fd5b843561428881613ce5565b935060208501356001600160401b038111156142a357600080fd5b6142af87828801613e55565b90945092506142c2905060408601614256565b905092959194509250565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60018060a01b038516815283602082015260606040820152600061431e6060830184866142cd565b9695505050505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561436657614366614328565b604052919050565b60006001600160401b0382111561438757614387614328565b50601f01601f191660200190565b600082601f8301126143a657600080fd5b81516143b96143b48261436e565b61433e565b8181528460208386010111156143ce57600080fd5b610818826020830160208701613ff8565b6000602082840312156143f157600080fd5b81516001600160401b0381111561440757600080fd5b61081884828501614395565b8284823760609190911b6bffffffffffffffffffffffff19169101908152601401919050565b60006020828403121561444b57600080fd5b5051919050565b6000808585111561446257600080fd5b8386111561446f57600080fd5b5050820193919092039150565b80356bffffffffffffffffffffffff1981169060148410156144c2576bffffffffffffffffffffffff196bffffffffffffffffffffffff198560140360031b1b82161691505b5092915050565b8481526060602082015260006144e2606083018661401c565b82810360408401526140898185876142cd565b60006020828403121561450757600080fd5b8151610b8f81614177565b6000808335601e1984360301811261452957600080fd5b83016020810192503590506001600160401b0381111561454857600080fd5b80360382131561060e57600080fd5b604081526145786040820161456b85613cfd565b6001600160a01b03169052565b6020830135606082015260006145916040850185614512565b61012060808501526145a8610160850182846142cd565b9150506145b86060860186614512565b848303603f190160a08601526145cf8382846142cd565b608088013560c08781019190915260a089013560e0808901919091529089013561010088015290935061460792508701905086614512565b848303603f190161012086015261461f8382846142cd565b92505050614631610100860186614512565b848303603f19016101408601526146498382846142cd565b93505050508260208301529392505050565b60006020828403121561466d57600080fd5b8135610b8f81613ce5565b6000808335601e1984360301811261468f57600080fd5b8301803591506001600160401b038211156146a957600080fd5b6020019150600581901b360382131561060e57600080fd5b6000602082840312156146d357600080fd5b610b8f82614256565b6000808335601e198436030181126146f357600080fd5b8301803591506001600160401b0382111561470d57600080fd5b60200191503681900382131561060e57600080fd5b80356001600160d01b031981169060068410156144c2576001600160d01b031960069490940360031b84901b1690921692915050565b8183823760009101908152919050565b634e487b7160e01b600052603260045260246000fd5b60008235603e1983360301811261479457600080fd5b9190910192915050565b8381526001600160a01b03831660208201526060604082018190526000906147c89083018461401c565b95945050505050565b6001600160f81b031991909116815260200190565b634e487b7160e01b600052602160045260246000fd5b6002811061481a57634e487b7160e01b600052602160045260246000fd5b9052565b60018060a01b0385168152836020820152608060408201526000614845608083018561401c565b90506147c860608301846147fc565b80518015158114611c5257600080fd5b6000806040838503121561487757600080fd5b61488083614854565b915060208301516001600160401b0381111561489b57600080fd5b6148a785828601614395565b9150509250929050565b6001600160a01b03831681526040602082018190526000906108189083018461401c565b6000602082840312156148e757600080fd5b610b8f82614854565b803560028110611c5257600080fd5b6000806040838503121561491257600080fd5b61491b836148f0565b91506020830135613fed81614177565b82815260008251614943816020850160208701613ff8565b919091016020019392505050565b83815260606020820152600061496a606083018561401c565b828103604084015261431e818561401c565b60ff84168152604060208201819052810182905260008360608301825b858110156149c95782356149ac81613ce5565b6001600160a01b0316825260209283019290910190600101614999565b509695505050505050565b634e487b7160e01b600052601160045260246000fd5b6000600182016149fc576149fc6149d4565b5060010190565b81810381811115610722576107226149d4565b600082601f830112614a2757600080fd5b8135614a356143b48261436e565b818152846020838601011115614a4a57600080fd5b816020850160208301376000918101602001919091529392505050565b600080600060608486031215614a7c57600080fd5b8335614a8781614177565b925060208401356001600160f81b031981168114614aa457600080fd5b915060408401356001600160401b03811115614abf57600080fd5b614acb86828701614a16565b9150509250925092565b600080600060608486031215614aea57600080fd5b614af3846148f0565b92506020840135614aa481614177565b60008060408385031215614b1657600080fd5b8235614b2181613ce5565b915060208301356001600160401b03811115614b3c57600080fd5b6148a785828601614a16565b60008060408385031215614b5b57600080fd5b8235614b2181614177565b6020808252810182905260006040600584901b830181019083018583605e1936839003015b87821015614c0b57868503603f190184528235818112614baa57600080fd5b89018035614bb781613ce5565b6001600160a01b0316865260208181013590870152614bd96040820182614512565b915060606040880152614bf06060880183836142cd565b96505050602083019250602084019350600182019150614b8b565b5092979650505050505050565b8035602083101561072257600019602084900360031b1b1692915050565b60006001600160401b03821115614c4f57614c4f614328565b5060051b60200190565b600082601f830112614c6a57600080fd5b8151614c786143b482614c36565b8082825260208201915060208360051b860101925085831115614c9a57600080fd5b602085015b83811015614cdb5780516001600160401b03811115614cbd57600080fd5b614ccc886020838a0101614395565b84525060209283019201614c9f565b5095945050505050565b600060208284031215614cf757600080fd5b81516001600160401b03811115614d0d57600080fd5b61081884828501614c59565b60008060408385031215614d2c57600080fd5b82516001600160401b03811115614d4257600080fd5b8301601f81018513614d5357600080fd5b8051614d616143b482614c36565b8082825260208201915060208360051b850101925087831115614d8357600080fd5b6020840193505b82841015614dac57614d9b84614854565b825260209384019390910190614d8a565b8095505050505060208301516001600160401b03811115614dcc57600080fd5b6148a785828601614c5956feb8bc84bd77f5eb08210b8eb20fd63b3ec6a7992d277ab94663bae0e066f792aca2646970667358221220ea7603497be0a0e1ddc106666c75af038c8db18b0f9143f6418b0e8b232c109164736f6c634300081a0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.