Source Code
Latest 8 from a total of 8 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Initialize | 24212059 | 49 days ago | IN | 0 ETH | 0.00031984 | ||||
| Withdraw | 24113116 | 63 days ago | IN | 0 ETH | 0.0000009 | ||||
| Withdraw | 24113114 | 63 days ago | IN | 0 ETH | 0.00000083 | ||||
| Withdraw | 24113071 | 63 days ago | IN | 0 ETH | 0.0000009 | ||||
| Withdraw | 24113067 | 63 days ago | IN | 0 ETH | 0.00000083 | ||||
| Withdraw All | 24113062 | 63 days ago | IN | 0 ETH | 0.00000084 | ||||
| Initialize | 24110617 | 63 days ago | IN | 0 ETH | 0.00000066 | ||||
| Transfer | 21873900 | 376 days ago | IN | 0.00193 ETH | 0.00004055 |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
CoinbaseSmartWallet
Compiler Version
v0.8.23+commit.f704f362
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {IAccount} from "account-abstraction/interfaces/IAccount.sol";
import {UserOperation, UserOperationLib} from "account-abstraction/interfaces/UserOperation.sol";
import {Receiver} from "solady/accounts/Receiver.sol";
import {SignatureCheckerLib} from "solady/utils/SignatureCheckerLib.sol";
import {UUPSUpgradeable} from "solady/utils/UUPSUpgradeable.sol";
import {WebAuthn} from "webauthn-sol/WebAuthn.sol";
import {ERC1271} from "./ERC1271.sol";
import {MultiOwnable} from "./MultiOwnable.sol";
/// @title Coinbase Smart Wallet
///
/// @notice ERC-4337-compatible smart account, based on Solady's ERC4337 account implementation
/// with inspiration from Alchemy's LightAccount and Daimo's DaimoAccount. Verified by z0r0z.eth from (⌘) NANI.eth
///
/// @author Coinbase (https://github.com/coinbase/smart-wallet)
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/ERC4337.sol)
contract CoinbaseSmartWallet is ERC1271, IAccount, MultiOwnable, UUPSUpgradeable, Receiver {
/// @notice A wrapper struct used for signature validation so that callers
/// can identify the owner that signed.
struct SignatureWrapper {
/// @dev The index of the owner that signed, see `MultiOwnable.ownerAtIndex`
uint256 ownerIndex;
/// @dev If `MultiOwnable.ownerAtIndex` is an Ethereum address, this should be `abi.encodePacked(r, s, v)`
/// If `MultiOwnable.ownerAtIndex` is a public key, this should be `abi.encode(WebAuthnAuth)`.
bytes signatureData;
}
/// @notice Represents a call to make.
struct Call {
/// @dev The address to call.
address target;
/// @dev The value to send when making the call.
uint256 value;
/// @dev The data of the call.
bytes data;
}
/// @notice Reserved nonce key (upper 192 bits of `UserOperation.nonce`) for cross-chain replayable
/// transactions.
///
/// @dev MUST BE the `UserOperation.nonce` key when `UserOperation.calldata` is calling
/// `executeWithoutChainIdValidation`and MUST NOT BE `UserOperation.nonce` key when `UserOperation.calldata` is
/// NOT calling `executeWithoutChainIdValidation`.
///
/// @dev Helps enforce sequential sequencing of replayable transactions.
uint256 public constant REPLAYABLE_NONCE_KEY = 8453;
/// @notice Thrown when `initialize` is called but the account already has had at least one owner.
error Initialized();
/// @notice Thrown when a call is passed to `executeWithoutChainIdValidation` that is not allowed by
/// `canSkipChainIdValidation`
///
/// @param selector The selector of the call.
error SelectorNotAllowed(bytes4 selector);
/// @notice Thrown in validateUserOp if the key of `UserOperation.nonce` does not match the calldata.
///
/// @dev Calls to `this.executeWithoutChainIdValidation` MUST use `REPLAYABLE_NONCE_KEY` and
/// calls NOT to `this.executeWithoutChainIdValidation` MUST NOT use `REPLAYABLE_NONCE_KEY`.
///
/// @param key The invalid `UserOperation.nonce` key.
error InvalidNonceKey(uint256 key);
/// @notice Reverts if the caller is not the EntryPoint.
modifier onlyEntryPoint() virtual {
if (msg.sender != entryPoint()) {
revert Unauthorized();
}
_;
}
/// @notice Reverts if the caller is neither the EntryPoint, the owner, nor the account itself.
modifier onlyEntryPointOrOwner() virtual {
if (msg.sender != entryPoint()) {
_checkOwner();
}
_;
}
/// @notice Sends to the EntryPoint (i.e. `msg.sender`) the missing funds for this transaction.
///
/// @dev 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).
///
/// @param missingAccountFunds 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 {
_;
assembly ("memory-safe") {
if missingAccountFunds {
// Ignore failure (it's EntryPoint's job to verify, not the account's).
pop(call(gas(), caller(), missingAccountFunds, codesize(), 0x00, codesize(), 0x00))
}
}
}
constructor() {
// Implementation should not be initializable (does not affect proxies which use their own storage).
bytes[] memory owners = new bytes[](1);
owners[0] = abi.encode(address(0));
_initializeOwners(owners);
}
/// @notice Initializes the account with the `owners`.
///
/// @dev Reverts if the account has had at least one owner, i.e. has been initialized.
///
/// @param owners Array of initial owners for this account. Each item should be
/// an ABI encoded Ethereum address, i.e. 32 bytes with 12 leading 0 bytes,
/// or a 64 byte public key.
function initialize(bytes[] calldata owners) external payable virtual {
if (nextOwnerIndex() != 0) {
revert Initialized();
}
_initializeOwners(owners);
}
/// @inheritdoc IAccount
///
/// @notice ERC-4337 `validateUserOp` method. The EntryPoint will
/// call `UserOperation.sender.call(UserOperation.callData)` only if this validation call returns
/// successfully.
///
/// @dev Signature failure should be reported by returning 1 (see: `this._isValidSignature`). This
/// allows making a "simulation call" without a valid signature. Other failures (e.g. invalid signature format)
/// should still revert to signal failure.
/// @dev Reverts if the `UserOperation.nonce` key is invalid for `UserOperation.calldata`.
/// @dev Reverts if the signature format is incorrect or invalid for owner type.
///
/// @param userOp The `UserOperation` to validate.
/// @param userOpHash The `UserOperation` hash, as computed by `EntryPoint.getUserOpHash(UserOperation)`.
/// @param missingAccountFunds The missing account funds that must be deposited on the Entrypoint.
///
/// @return validationData The encoded `ValidationData` structure:
/// `(uint256(validAfter) << (160 + 48)) | (uint256(validUntil) << 160) | (success ? 0 : 1)`
/// where `validUntil` is 0 (indefinite) and `validAfter` is 0.
function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
external
virtual
onlyEntryPoint
payPrefund(missingAccountFunds)
returns (uint256 validationData)
{
uint256 key = userOp.nonce >> 64;
if (bytes4(userOp.callData) == this.executeWithoutChainIdValidation.selector) {
userOpHash = getUserOpHashWithoutChainId(userOp);
if (key != REPLAYABLE_NONCE_KEY) {
revert InvalidNonceKey(key);
}
} else {
if (key == REPLAYABLE_NONCE_KEY) {
revert InvalidNonceKey(key);
}
}
// Return 0 if the recovered address matches the owner.
if (_isValidSignature(userOpHash, userOp.signature)) {
return 0;
}
// Else return 1
return 1;
}
/// @notice Executes `calls` on this account (i.e. self call).
///
/// @dev Can only be called by the Entrypoint.
/// @dev Reverts if the given call is not authorized to skip the chain ID validtion.
/// @dev `validateUserOp()` will recompute the `userOpHash` without the chain ID before validating
/// it if the `UserOperation.calldata` is calling this function. This allows certain UserOperations
/// to be replayed for all accounts sharing the same address across chains. E.g. This may be
/// useful for syncing owner changes.
///
/// @param calls An array of calldata to use for separate self calls.
function executeWithoutChainIdValidation(bytes[] calldata calls) external payable virtual onlyEntryPoint {
for (uint256 i; i < calls.length; i++) {
bytes calldata call = calls[i];
bytes4 selector = bytes4(call);
if (!canSkipChainIdValidation(selector)) {
revert SelectorNotAllowed(selector);
}
_call(address(this), 0, call);
}
}
/// @notice Executes the given call from this account.
///
/// @dev Can only be called by the Entrypoint or an owner of this account (including itself).
///
/// @param target The address to call.
/// @param value The value to send with the call.
/// @param data The data of the call.
function execute(address target, uint256 value, bytes calldata data)
external
payable
virtual
onlyEntryPointOrOwner
{
_call(target, value, data);
}
/// @notice Executes batch of `Call`s.
///
/// @dev Can only be called by the Entrypoint or an owner of this account (including itself).
///
/// @param calls The list of `Call`s to execute.
function executeBatch(Call[] calldata calls) external payable virtual onlyEntryPointOrOwner {
for (uint256 i; i < calls.length; i++) {
_call(calls[i].target, calls[i].value, calls[i].data);
}
}
/// @notice Returns the address of the EntryPoint v0.6.
///
/// @return The address of the EntryPoint v0.6
function entryPoint() public view virtual returns (address) {
return 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789;
}
/// @notice Computes the hash of the `UserOperation` in the same way as EntryPoint v0.6, but
/// leaves out the chain ID.
///
/// @dev This allows accounts to sign a hash that can be used on many chains.
///
/// @param userOp The `UserOperation` to compute the hash for.
///
/// @return The `UserOperation` hash, which does not depend on chain ID.
function getUserOpHashWithoutChainId(UserOperation calldata userOp) public view virtual returns (bytes32) {
return keccak256(abi.encode(UserOperationLib.hash(userOp), entryPoint()));
}
/// @notice Returns the implementation of the ERC1967 proxy.
///
/// @return $ The address of implementation contract.
function implementation() public view returns (address $) {
assembly {
$ := sload(_ERC1967_IMPLEMENTATION_SLOT)
}
}
/// @notice Returns whether `functionSelector` can be called in `executeWithoutChainIdValidation`.
///
/// @param functionSelector The function selector to check.
////
/// @return `true` is the function selector is allowed to skip the chain ID validation, else `false`.
function canSkipChainIdValidation(bytes4 functionSelector) public pure returns (bool) {
if (
functionSelector == MultiOwnable.addOwnerPublicKey.selector
|| functionSelector == MultiOwnable.addOwnerAddress.selector
|| functionSelector == MultiOwnable.removeOwnerAtIndex.selector
|| functionSelector == MultiOwnable.removeLastOwner.selector
|| functionSelector == UUPSUpgradeable.upgradeToAndCall.selector
) {
return true;
}
return false;
}
/// @notice Executes the given call from this account.
///
/// @dev Reverts if the call reverted.
/// @dev Implementation taken from
/// https://github.com/alchemyplatform/light-account/blob/43f625afdda544d5e5af9c370c9f4be0943e4e90/src/common/BaseLightAccount.sol#L125
///
/// @param target The target call address.
/// @param value The call value to user.
/// @param data The raw call data.
function _call(address target, uint256 value, bytes memory data) internal {
(bool success, bytes memory result) = target.call{value: value}(data);
if (!success) {
assembly ("memory-safe") {
revert(add(result, 32), mload(result))
}
}
}
/// @inheritdoc ERC1271
///
/// @dev Used by both `ERC1271.isValidSignature` AND `IAccount.validateUserOp` signature validation.
/// @dev Reverts if owner at `ownerIndex` is not compatible with `signature` format.
///
/// @param signature ABI encoded `SignatureWrapper`.
function _isValidSignature(bytes32 hash, bytes calldata signature) internal view virtual override returns (bool) {
SignatureWrapper memory sigWrapper = abi.decode(signature, (SignatureWrapper));
bytes memory ownerBytes = ownerAtIndex(sigWrapper.ownerIndex);
if (ownerBytes.length == 32) {
if (uint256(bytes32(ownerBytes)) > type(uint160).max) {
// technically should be impossible given owners can only be added with
// addOwnerAddress and addOwnerPublicKey, but we leave incase of future changes.
revert InvalidEthereumAddressOwner(ownerBytes);
}
address owner;
assembly ("memory-safe") {
owner := mload(add(ownerBytes, 32))
}
return SignatureCheckerLib.isValidSignatureNow(owner, hash, sigWrapper.signatureData);
}
if (ownerBytes.length == 64) {
(uint256 x, uint256 y) = abi.decode(ownerBytes, (uint256, uint256));
WebAuthn.WebAuthnAuth memory auth = abi.decode(sigWrapper.signatureData, (WebAuthn.WebAuthnAuth));
return WebAuthn.verify({challenge: abi.encode(hash), requireUV: false, webAuthnAuth: auth, x: x, y: y});
}
revert InvalidOwnerBytesLength(ownerBytes);
}
/// @inheritdoc UUPSUpgradeable
///
/// @dev Authorization logic is only based on the `msg.sender` being an owner of this account,
/// or `address(this)`.
function _authorizeUpgrade(address) internal view virtual override(UUPSUpgradeable) onlyOwner {}
/// @inheritdoc ERC1271
function _domainNameAndVersion() internal pure override(ERC1271) returns (string memory, string memory) {
return ("Coinbase Smart Wallet", "1");
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
import "./UserOperation.sol";
interface IAccount {
/**
* Validate user's signature and nonce
* the entryPoint will make the call to the recipient only if this validation call returns successfully.
* signature failure should be reported by returning SIG_VALIDATION_FAILED (1).
* This allows making a "simulation call" without a valid signature
* Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure.
*
* @dev Must validate caller is the entryPoint.
* Must validate the signature and nonce
* @param userOp the operation that is about to be executed.
* @param userOpHash hash of the user's request data. can be used as the basis for signature.
* @param missingAccountFunds missing funds on the account's deposit in the entrypoint.
* This is the minimum amount to transfer to the sender(entryPoint) to be able to make the call.
* The excess is left as a deposit in the entrypoint, for future calls.
* can be withdrawn anytime using "entryPoint.withdrawTo()"
* In case there is a paymaster in the request (or the current deposit is high enough), this value will be zero.
* @return validationData packaged ValidationData structure. use `_packValidationData` and `_unpackValidationData` to encode and decode
* <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure,
* otherwise, an address of an "authorizer" contract.
* <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite"
* <6-byte> validAfter - first timestamp this operation is valid
* If an account doesn't use time-range, it is enough to return SIG_VALIDATION_FAILED value (1) for signature failure.
* Note that the validation code cannot use block.timestamp (or block.number) directly.
*/
function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
external returns (uint256 validationData);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
/* solhint-disable no-inline-assembly */
import {calldataKeccak} from "../core/Helpers.sol";
/**
* 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 callGasLimit the gas limit passed to the callData method call.
* @param verificationGasLimit gas used for validateUserOp and validatePaymasterUserOp.
* @param preVerificationGas gas not calculated by the handleOps method, but added to the gas paid. Covers batch overhead.
* @param maxFeePerGas same as EIP-1559 gas parameter.
* @param maxPriorityFeePerGas same as EIP-1559 gas parameter.
* @param paymasterAndData if set, this field holds the paymaster address and paymaster-specific 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 UserOperation {
address sender;
uint256 nonce;
bytes initCode;
bytes callData;
uint256 callGasLimit;
uint256 verificationGasLimit;
uint256 preVerificationGas;
uint256 maxFeePerGas;
uint256 maxPriorityFeePerGas;
bytes paymasterAndData;
bytes signature;
}
/**
* Utility functions helpful when working with UserOperation structs.
*/
library UserOperationLib {
function getSender(UserOperation 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.
function gasPrice(UserOperation calldata userOp) internal view returns (uint256) {
unchecked {
uint256 maxFeePerGas = userOp.maxFeePerGas;
uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
if (maxFeePerGas == maxPriorityFeePerGas) {
//legacy mode (for networks that don't support basefee opcode)
return maxFeePerGas;
}
return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
}
}
function pack(UserOperation 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);
uint256 callGasLimit = userOp.callGasLimit;
uint256 verificationGasLimit = userOp.verificationGasLimit;
uint256 preVerificationGas = userOp.preVerificationGas;
uint256 maxFeePerGas = userOp.maxFeePerGas;
uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
return abi.encode(
sender, nonce,
hashInitCode, hashCallData,
callGasLimit, verificationGasLimit, preVerificationGas,
maxFeePerGas, maxPriorityFeePerGas,
hashPaymasterAndData
);
}
function hash(UserOperation calldata userOp) internal pure returns (bytes32) {
return keccak256(pack(userOp));
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Receiver mixin for ETH and safe-transferred ERC721 and ERC1155 tokens.
/// @author Solady (https://github.com/Vectorized/solady/blob/main/src/accounts/Receiver.sol)
///
/// @dev Note:
/// - Handles all ERC721 and ERC1155 token safety callbacks.
/// - Collapses function table gas overhead and code size.
/// - Utilizes fallback so unknown calldata will pass on.
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.4;
/// @notice Signature verification helper that supports both ECDSA signatures from EOAs
/// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol)
///
/// @dev Note:
/// - The signature checking functions use the ecrecover precompile (0x1).
/// - The `bytes memory signature` variants use the identity precompile (0x4)
/// to copy memory internally.
/// - Unlike ECDSA signatures, contract signatures are revocable.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
/// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
/// See: https://eips.ethereum.org/EIPS/eip-2098
/// This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT use signatures as unique identifiers:
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
/// EIP-712 also enables readable signing of typed data for better user safety.
/// This implementation does NOT check if a signature is non-malleable.
library SignatureCheckerLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIGNATURE CHECKING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `signature` is valid for `signer` and `hash`.
/// If `signer` is a smart contract, the signature is validated with ERC1271.
/// Otherwise, the signature is validated with `ECDSA.recover`.
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits of `signer` in case they are dirty.
for { signer := shr(96, shl(96, signer)) } signer {} {
let m := mload(0x40)
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
if eq(mload(signature), 64) {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
let t :=
staticcall(
gas(), // Amount of gas left for the transaction.
1, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x01, // Start of output.
0x20 // Size of output.
)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
isValid := 1
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
if eq(mload(signature), 65) {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
let t :=
staticcall(
gas(), // Amount of gas left for the transaction.
1, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x01, // Start of output.
0x20 // Size of output.
)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
isValid := 1
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
// Copy the `signature` over.
let n := add(0x20, mload(signature))
pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n))
// forgefmt: disable-next-item
isValid := and(
// Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
eq(mload(d), f),
// Whether the staticcall does not revert.
// This must be placed at the end of the `and` clause,
// as the arguments are evaluated from right to left.
staticcall(
gas(), // Remaining gas.
signer, // The `signer` address.
m, // Offset of calldata in memory.
add(returndatasize(), 0x44), // Length of calldata in memory.
d, // Offset of returndata.
0x20 // Length of returndata to write.
)
)
break
}
}
}
/// @dev Returns whether `signature` is valid for `signer` and `hash`.
/// If `signer` is a smart contract, the signature is validated with ERC1271.
/// Otherwise, the signature is validated with `ECDSA.recover`.
function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits of `signer` in case they are dirty.
for { signer := shr(96, shl(96, signer)) } signer {} {
let m := mload(0x40)
mstore(0x00, hash)
if eq(signature.length, 64) {
let vs := calldataload(add(signature.offset, 0x20))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, calldataload(signature.offset)) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
let t :=
staticcall(
gas(), // Amount of gas left for the transaction.
1, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x01, // Start of output.
0x20 // Size of output.
)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
isValid := 1
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
if eq(signature.length, 65) {
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`.
let t :=
staticcall(
gas(), // Amount of gas left for the transaction.
1, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x01, // Start of output.
0x20 // Size of output.
)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
isValid := 1
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), signature.length)
// Copy the `signature` over.
calldatacopy(add(m, 0x64), signature.offset, signature.length)
// forgefmt: disable-next-item
isValid := and(
// Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
eq(mload(d), f),
// Whether the staticcall does not revert.
// This must be placed at the end of the `and` clause,
// as the arguments are evaluated from right to left.
staticcall(
gas(), // Remaining gas.
signer, // The `signer` address.
m, // Offset of calldata in memory.
add(signature.length, 0x64), // Length of calldata in memory.
d, // Offset of returndata.
0x20 // Length of returndata to write.
)
)
break
}
}
}
/// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`.
/// If `signer` is a smart contract, the signature is validated with ERC1271.
/// Otherwise, the signature is validated with `ECDSA.recover`.
function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits of `signer` in case they are dirty.
for { signer := shr(96, shl(96, signer)) } signer {} {
let m := mload(0x40)
mstore(0x00, hash)
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, r) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
let t :=
staticcall(
gas(), // Amount of gas left for the transaction.
1, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x01, // Start of output.
0x20 // Size of output.
)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
isValid := 1
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), mload(0x60)) // `s`.
mstore8(add(m, 0xa4), mload(0x20)) // `v`.
// forgefmt: disable-next-item
isValid := and(
// Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
eq(mload(d), f),
// Whether the staticcall does not revert.
// This must be placed at the end of the `and` clause,
// as the arguments are evaluated from right to left.
staticcall(
gas(), // Remaining gas.
signer, // The `signer` address.
m, // Offset of calldata in memory.
0xa5, // Length of calldata in memory.
d, // Offset of returndata.
0x20 // Length of returndata to write.
)
)
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`.
/// If `signer` is a smart contract, the signature is validated with ERC1271.
/// Otherwise, the signature is validated with `ECDSA.recover`.
function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits of `signer` in case they are dirty.
for { signer := shr(96, shl(96, signer)) } signer {} {
let m := mload(0x40)
mstore(0x00, hash)
mstore(0x20, and(v, 0xff)) // `v`.
mstore(0x40, r) // `r`.
mstore(0x60, s) // `s`.
let t :=
staticcall(
gas(), // Amount of gas left for the transaction.
1, // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x01, // Start of output.
0x20 // Size of output.
)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
isValid := 1
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), s) // `s`.
mstore8(add(m, 0xa4), v) // `v`.
// forgefmt: disable-next-item
isValid := and(
// Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
eq(mload(d), f),
// Whether the staticcall does not revert.
// This must be placed at the end of the `and` clause,
// as the arguments are evaluated from right to left.
staticcall(
gas(), // Remaining gas.
signer, // The `signer` address.
m, // Offset of calldata in memory.
0xa5, // Length of calldata in memory.
d, // Offset of returndata.
0x20 // Length of returndata to write.
)
)
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1271 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
// Copy the `signature` over.
let n := add(0x20, mload(signature))
pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n))
// forgefmt: disable-next-item
isValid := and(
// Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
eq(mload(d), f),
// Whether the staticcall does not revert.
// This must be placed at the end of the `and` clause,
// as the arguments are evaluated from right to left.
staticcall(
gas(), // Remaining gas.
signer, // The `signer` address.
m, // Offset of calldata in memory.
add(returndatasize(), 0x44), // Length of calldata in memory.
d, // Offset of returndata.
0x20 // Length of returndata to write.
)
)
}
}
/// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
function isValidERC1271SignatureNowCalldata(
address signer,
bytes32 hash,
bytes calldata signature
) internal view returns (bool isValid) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), signature.length)
// Copy the `signature` over.
calldatacopy(add(m, 0x64), signature.offset, signature.length)
// forgefmt: disable-next-item
isValid := and(
// Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
eq(mload(d), f),
// Whether the staticcall does not revert.
// This must be placed at the end of the `and` clause,
// as the arguments are evaluated from right to left.
staticcall(
gas(), // Remaining gas.
signer, // The `signer` address.
m, // Offset of calldata in memory.
add(signature.length, 0x64), // Length of calldata in memory.
d, // Offset of returndata.
0x20 // Length of returndata to write.
)
)
}
}
/// @dev Returns whether the signature (`r`, `vs`) is valid for `hash`
/// for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
// forgefmt: disable-next-item
isValid := and(
// Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
eq(mload(d), f),
// Whether the staticcall does not revert.
// This must be placed at the end of the `and` clause,
// as the arguments are evaluated from right to left.
staticcall(
gas(), // Remaining gas.
signer, // The `signer` address.
m, // Offset of calldata in memory.
0xa5, // Length of calldata in memory.
d, // Offset of returndata.
0x20 // Length of returndata to write.
)
)
}
}
/// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash`
/// for an ERC1271 `signer` contract.
function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let f := shl(224, 0x1626ba7e)
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
let d := add(m, 0x24)
mstore(d, 0x40) // The offset of the `signature` in the calldata.
mstore(add(m, 0x44), 65) // Length of the signature.
mstore(add(m, 0x64), r) // `r`.
mstore(add(m, 0x84), s) // `s`.
mstore8(add(m, 0xa4), v) // `v`.
// forgefmt: disable-next-item
isValid := and(
// Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
eq(mload(d), f),
// Whether the staticcall does not revert.
// This must be placed at the end of the `and` clause,
// as the arguments are evaluated from right to left.
staticcall(
gas(), // Remaining gas.
signer, // The `signer` address.
m, // Offset of calldata in memory.
0xa5, // Length of calldata in memory.
d, // Offset of returndata.
0x20 // Length of returndata to write.
)
)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an Ethereum Signed Message, created from a `hash`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
/// JSON-RPC method as part of EIP-191.
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, hash) // Store into scratch space for keccak256.
mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
}
}
/// @dev Returns an Ethereum Signed Message, created from `s`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
/// JSON-RPC method as part of EIP-191.
/// Note: Supports lengths of `s` up to 999999 bytes.
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let sLength := mload(s)
let o := 0x20
mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
mstore(0x00, 0x00)
// Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
for { let temp := sLength } 1 {} {
o := sub(o, 1)
mstore8(o, add(48, mod(temp, 10)))
temp := div(temp, 10)
if iszero(temp) { break }
}
let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
// Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
mstore(s, sLength) // Restore the length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EMPTY CALLDATA HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an empty calldata bytes.
function emptySignature() internal pure returns (bytes calldata signature) {
/// @solidity memory-safe-assembly
assembly {
signature.length := 0
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice UUPS proxy mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/UUPSUpgradeable.sol)
/// @author Modified from OpenZeppelin
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol)
///
/// Note:
/// - This implementation is intended to be used with ERC1967 proxies.
/// See: `LibClone.deployERC1967` and related functions.
/// - This implementation is NOT compatible with legacy OpenZeppelin proxies
/// which do not store the implementation at `_ERC1967_IMPLEMENTATION_SLOT`.
abstract contract UUPSUpgradeable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The upgrade failed.
error UpgradeFailed();
/// @dev The call is from an unauthorized call context.
error UnauthorizedCallContext();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev For checking if the context is a delegate call.
uint256 private immutable __self = uint256(uint160(address(this)));
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when the proxy's implementation is upgraded.
event Upgraded(address indexed implementation);
/// @dev `keccak256(bytes("Upgraded(address)"))`.
uint256 private constant _UPGRADED_EVENT_SIGNATURE =
0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ERC-1967 storage slot for the implementation in the proxy.
/// `uint256(keccak256("eip1967.proxy.implementation")) - 1`.
bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UUPS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to check if `msg.sender` is authorized
/// to upgrade the proxy to `newImplementation`, reverting if not.
/// ```
/// function _authorizeUpgrade(address) internal override onlyOwner {}
/// ```
function _authorizeUpgrade(address newImplementation) internal virtual;
/// @dev Returns the storage slot used by the implementation,
/// as specified in [ERC1822](https://eips.ethereum.org/EIPS/eip-1822).
///
/// Note: The `notDelegated` modifier prevents accidental upgrades to
/// an implementation that is a proxy contract.
function proxiableUUID() public view virtual notDelegated returns (bytes32) {
// This function must always return `_ERC1967_IMPLEMENTATION_SLOT` to comply with ERC1967.
return _ERC1967_IMPLEMENTATION_SLOT;
}
/// @dev Upgrades the proxy's implementation to `newImplementation`.
/// Emits a {Upgraded} event.
///
/// Note: Passing in empty `data` skips the delegatecall to `newImplementation`.
function upgradeToAndCall(address newImplementation, bytes calldata data)
public
payable
virtual
onlyProxy
{
_authorizeUpgrade(newImplementation);
/// @solidity memory-safe-assembly
assembly {
newImplementation := shr(96, shl(96, newImplementation)) // Clears upper 96 bits.
mstore(0x01, 0x52d1902d) // `proxiableUUID()`.
let s := _ERC1967_IMPLEMENTATION_SLOT
// Check if `newImplementation` implements `proxiableUUID` correctly.
if iszero(eq(mload(staticcall(gas(), newImplementation, 0x1d, 0x04, 0x01, 0x20)), s)) {
mstore(0x01, 0x55299b49) // `UpgradeFailed()`.
revert(0x1d, 0x04)
}
// Emit the {Upgraded} event.
log2(codesize(), 0x00, _UPGRADED_EVENT_SIGNATURE, newImplementation)
sstore(s, newImplementation) // Updates the implementation.
// Perform a delegatecall to `newImplementation` if `data` is non-empty.
if data.length {
// Forwards the `data` to `newImplementation` via delegatecall.
let m := mload(0x40)
calldatacopy(m, data.offset, data.length)
if iszero(delegatecall(gas(), newImplementation, m, data.length, codesize(), 0x00))
{
// Bubble up the revert if the call reverts.
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
}
}
}
/// @dev Requires that the execution is performed through a proxy.
modifier onlyProxy() {
uint256 s = __self;
/// @solidity memory-safe-assembly
assembly {
// To enable use cases with an immutable default implementation in the bytecode,
// (see: ERC6551Proxy), we don't require that the proxy address must match the
// value stored in the implementation slot, which may not be initialized.
if eq(s, address()) {
mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
revert(0x1c, 0x04)
}
}
_;
}
/// @dev Requires that the execution is NOT performed via delegatecall.
/// This is the opposite of `onlyProxy`.
modifier notDelegated() {
uint256 s = __self;
/// @solidity memory-safe-assembly
assembly {
if iszero(eq(s, address())) {
mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
revert(0x1c, 0x04)
}
}
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {FCL_ecdsa} from "FreshCryptoLib/FCL_ecdsa.sol";
import {FCL_Elliptic_ZZ} from "FreshCryptoLib/FCL_elliptic.sol";
import {Base64} from "openzeppelin-contracts/contracts/utils/Base64.sol";
import {LibString} from "solady/utils/LibString.sol";
/// @title WebAuthn
///
/// @notice A library for verifying WebAuthn Authentication Assertions, built off the work
/// of Daimo.
///
/// @dev Attempts to use the RIP-7212 precompile for signature verification.
/// If precompile verification fails, it falls back to FreshCryptoLib.
///
/// @author Coinbase (https://github.com/base-org/webauthn-sol)
/// @author Daimo (https://github.com/daimo-eth/p256-verifier/blob/master/src/WebAuthn.sol)
library WebAuthn {
using LibString for string;
struct WebAuthnAuth {
/// @dev The WebAuthn authenticator data.
/// See https://www.w3.org/TR/webauthn-2/#dom-authenticatorassertionresponse-authenticatordata.
bytes authenticatorData;
/// @dev The WebAuthn client data JSON.
/// See https://www.w3.org/TR/webauthn-2/#dom-authenticatorresponse-clientdatajson.
string clientDataJSON;
/// @dev The index at which "challenge":"..." occurs in `clientDataJSON`.
uint256 challengeIndex;
/// @dev The index at which "type":"..." occurs in `clientDataJSON`.
uint256 typeIndex;
/// @dev The r value of secp256r1 signature
uint256 r;
/// @dev The s value of secp256r1 signature
uint256 s;
}
/// @dev Bit 0 of the authenticator data struct, corresponding to the "User Present" bit.
/// See https://www.w3.org/TR/webauthn-2/#flags.
bytes1 private constant _AUTH_DATA_FLAGS_UP = 0x01;
/// @dev Bit 2 of the authenticator data struct, corresponding to the "User Verified" bit.
/// See https://www.w3.org/TR/webauthn-2/#flags.
bytes1 private constant _AUTH_DATA_FLAGS_UV = 0x04;
/// @dev Secp256r1 curve order / 2 used as guard to prevent signature malleability issue.
uint256 private constant _P256_N_DIV_2 = FCL_Elliptic_ZZ.n / 2;
/// @dev The precompiled contract address to use for signature verification in the “secp256r1” elliptic curve.
/// See https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md.
address private constant _VERIFIER = address(0x100);
/// @dev The expected type (hash) in the client data JSON when verifying assertion signatures.
/// See https://www.w3.org/TR/webauthn-2/#dom-collectedclientdata-type
bytes32 private constant _EXPECTED_TYPE_HASH = keccak256('"type":"webauthn.get"');
///
/// @notice Verifies a Webauthn Authentication Assertion as described
/// in https://www.w3.org/TR/webauthn-2/#sctn-verifying-assertion.
///
/// @dev We do not verify all the steps as described in the specification, only ones relevant to our context.
/// Please carefully read through this list before usage.
///
/// Specifically, we do verify the following:
/// - Verify that authenticatorData (which comes from the authenticator, such as iCloud Keychain) indicates
/// a well-formed assertion with the user present bit set. If `requireUV` is set, checks that the authenticator
/// enforced user verification. User verification should be required if, and only if, options.userVerification
/// is set to required in the request.
/// - Verifies that the client JSON is of type "webauthn.get", i.e. the client was responding to a request to
/// assert authentication.
/// - Verifies that the client JSON contains the requested challenge.
/// - Verifies that (r, s) constitute a valid signature over both the authenicatorData and client JSON, for public
/// key (x, y).
///
/// We make some assumptions about the particular use case of this verifier, so we do NOT verify the following:
/// - Does NOT verify that the origin in the `clientDataJSON` matches the Relying Party's origin: tt is considered
/// the authenticator's responsibility to ensure that the user is interacting with the correct RP. This is
/// enforced by most high quality authenticators properly, particularly the iCloud Keychain and Google Password
/// Manager were tested.
/// - Does NOT verify That `topOrigin` in `clientDataJSON` is well-formed: We assume it would never be present, i.e.
/// the credentials are never used in a cross-origin/iframe context. The website/app set up should disallow
/// cross-origin usage of the credentials. This is the default behaviour for created credentials in common settings.
/// - Does NOT verify that the `rpIdHash` in `authenticatorData` is the SHA-256 hash of the RP ID expected by the Relying
/// Party: this means that we rely on the authenticator to properly enforce credentials to be used only by the correct RP.
/// This is generally enforced with features like Apple App Site Association and Google Asset Links. To protect from
/// edge cases in which a previously-linked RP ID is removed from the authorised RP IDs, we recommend that messages
/// signed by the authenticator include some expiry mechanism.
/// - Does NOT verify the credential backup state: this assumes the credential backup state is NOT used as part of Relying
/// Party business logic or policy.
/// - Does NOT verify the values of the client extension outputs: this assumes that the Relying Party does not use client
/// extension outputs.
/// - Does NOT verify the signature counter: signature counters are intended to enable risk scoring for the Relying Party.
/// This assumes risk scoring is not used as part of Relying Party business logic or policy.
/// - Does NOT verify the attestation object: this assumes that response.attestationObject is NOT present in the response,
/// i.e. the RP does not intend to verify an attestation.
///
/// @param challenge The challenge that was provided by the relying party.
/// @param requireUV A boolean indicating whether user verification is required.
/// @param webAuthnAuth The `WebAuthnAuth` struct.
/// @param x The x coordinate of the public key.
/// @param y The y coordinate of the public key.
///
/// @return `true` if the authentication assertion passed validation, else `false`.
function verify(bytes memory challenge, bool requireUV, WebAuthnAuth memory webAuthnAuth, uint256 x, uint256 y)
internal
view
returns (bool)
{
if (webAuthnAuth.s > _P256_N_DIV_2) {
// guard against signature malleability
return false;
}
// 11. Verify that the value of C.type is the string webauthn.get.
// bytes("type":"webauthn.get").length = 21
string memory _type = webAuthnAuth.clientDataJSON.slice(webAuthnAuth.typeIndex, webAuthnAuth.typeIndex + 21);
if (keccak256(bytes(_type)) != _EXPECTED_TYPE_HASH) {
return false;
}
// 12. Verify that the value of C.challenge equals the base64url encoding of options.challenge.
bytes memory expectedChallenge = bytes(string.concat('"challenge":"', Base64.encodeURL(challenge), '"'));
string memory actualChallenge =
webAuthnAuth.clientDataJSON.slice(webAuthnAuth.challengeIndex, webAuthnAuth.challengeIndex + expectedChallenge.length);
if (keccak256(bytes(actualChallenge)) != keccak256(expectedChallenge)) {
return false;
}
// Skip 13., 14., 15.
// 16. Verify that the UP bit of the flags in authData is set.
if (webAuthnAuth.authenticatorData[32] & _AUTH_DATA_FLAGS_UP != _AUTH_DATA_FLAGS_UP) {
return false;
}
// 17. If user verification is required for this assertion, verify that the User Verified bit of the flags in
// authData is set.
if (requireUV && (webAuthnAuth.authenticatorData[32] & _AUTH_DATA_FLAGS_UV) != _AUTH_DATA_FLAGS_UV) {
return false;
}
// skip 18.
// 19. Let hash be the result of computing a hash over the cData using SHA-256.
bytes32 clientDataJSONHash = sha256(bytes(webAuthnAuth.clientDataJSON));
// 20. Using credentialPublicKey, verify that sig is a valid signature over the binary concatenation of authData
// and hash.
bytes32 messageHash = sha256(abi.encodePacked(webAuthnAuth.authenticatorData, clientDataJSONHash));
bytes memory args = abi.encode(messageHash, webAuthnAuth.r, webAuthnAuth.s, x, y);
// try the RIP-7212 precompile address
(bool success, bytes memory ret) = _VERIFIER.staticcall(args);
// staticcall will not revert if address has no code
// check return length
// note that even if precompile exists, ret.length is 0 when verification returns false
// so an invalid signature will be checked twice: once by the precompile and once by FCL.
// Ideally this signature failure is simulated offchain and no one actually pay this gas.
bool valid = ret.length > 0;
if (success && valid) return abi.decode(ret, (uint256)) == 1;
return FCL_ecdsa.ecdsa_verify(messageHash, webAuthnAuth.r, webAuthnAuth.s, x, y);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @title ERC-1271
///
/// @notice Abstract ERC-1271 implementation (based on Solady's) with guards to handle the same
/// signer being used on multiple accounts.
///
/// @dev To prevent the same signature from being validated on different accounts owned by the samer signer,
/// we introduce an anti cross-account-replay layer: the original hash is input into a new EIP-712 compliant
/// hash. The domain separator of this outer hash contains the chain id and address of this contract, so that
/// it cannot be used on two accounts (see `replaySafeHash()` for the implementation details).
///
/// @author Coinbase (https://github.com/coinbase/smart-wallet)
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/ERC1271.sol)
abstract contract ERC1271 {
/// @dev Precomputed `typeHash` used to produce EIP-712 compliant hash when applying the anti
/// cross-account-replay layer.
///
/// The original hash must either be:
/// - An EIP-191 hash: keccak256("\x19Ethereum Signed Message:\n" || len(someMessage) || someMessage)
/// - An EIP-712 hash: keccak256("\x19\x01" || someDomainSeparator || hashStruct(someStruct))
bytes32 private constant _MESSAGE_TYPEHASH = keccak256("CoinbaseSmartWalletMessage(bytes32 hash)");
/// @notice Returns information about the `EIP712Domain` used to create EIP-712 compliant hashes.
///
/// @dev Follows ERC-5267 (see https://eips.ethereum.org/EIPS/eip-5267).
///
/// @return fields The bitmap of used fields.
/// @return name The value of the `EIP712Domain.name` field.
/// @return version The value of the `EIP712Domain.version` field.
/// @return chainId The value of the `EIP712Domain.chainId` field.
/// @return verifyingContract The value of the `EIP712Domain.verifyingContract` field.
/// @return salt The value of the `EIP712Domain.salt` field.
/// @return extensions The list of EIP numbers, that extends EIP-712 with new domain fields.
function eip712Domain()
external
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
fields = hex"0f"; // `0b1111`.
(name, version) = _domainNameAndVersion();
chainId = block.chainid;
verifyingContract = address(this);
salt = salt; // `bytes32(0)`.
extensions = extensions; // `new uint256[](0)`.
}
/// @notice Validates the `signature` against the given `hash`.
///
/// @dev This implementation follows ERC-1271. See https://eips.ethereum.org/EIPS/eip-1271.
/// @dev IMPORTANT: Signature verification is performed on the hash produced AFTER applying the anti
/// cross-account-replay layer on the given `hash` (i.e., verification is run on the replay-safe
/// hash version).
///
/// @param hash The original hash.
/// @param signature The signature of the replay-safe hash to validate.
///
/// @return result `0x1626ba7e` if validation succeeded, else `0xffffffff`.
function isValidSignature(bytes32 hash, bytes calldata signature) public view virtual returns (bytes4 result) {
if (_isValidSignature({hash: replaySafeHash(hash), signature: signature})) {
// bytes4(keccak256("isValidSignature(bytes32,bytes)"))
return 0x1626ba7e;
}
return 0xffffffff;
}
/// @notice Wrapper around `_eip712Hash()` to produce a replay-safe hash fron the given `hash`.
///
/// @dev The returned EIP-712 compliant replay-safe hash is the result of:
/// keccak256(
/// \x19\x01 ||
/// this.domainSeparator ||
/// hashStruct(CoinbaseSmartWalletMessage({ hash: `hash`}))
/// )
///
/// @param hash The original hash.
///
/// @return The corresponding replay-safe hash.
function replaySafeHash(bytes32 hash) public view virtual returns (bytes32) {
return _eip712Hash(hash);
}
/// @notice Returns the `domainSeparator` used to create EIP-712 compliant hashes.
///
/// @dev Implements domainSeparator = hashStruct(eip712Domain).
/// See https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator.
///
/// @return The 32 bytes domain separator result.
function domainSeparator() public view returns (bytes32) {
(string memory name, string memory version) = _domainNameAndVersion();
return keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256(bytes(version)),
block.chainid,
address(this)
)
);
}
/// @notice Returns the EIP-712 typed hash of the `CoinbaseSmartWalletMessage(bytes32 hash)` data structure.
///
/// @dev Implements encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01" || domainSeparator ||
/// hashStruct(message).
/// @dev See https://eips.ethereum.org/EIPS/eip-712#specification.
///
/// @param hash The `CoinbaseSmartWalletMessage.hash` field to hash.
////
/// @return The resulting EIP-712 hash.
function _eip712Hash(bytes32 hash) internal view virtual returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator(), _hashStruct(hash)));
}
/// @notice Returns the EIP-712 `hashStruct` result of the `CoinbaseSmartWalletMessage(bytes32 hash)` data
/// structure.
///
/// @dev Implements hashStruct(s : 𝕊) = keccak256(typeHash || encodeData(s)).
/// @dev See https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
///
/// @param hash The `CoinbaseSmartWalletMessage.hash` field.
///
/// @return The EIP-712 `hashStruct` result.
function _hashStruct(bytes32 hash) internal view virtual returns (bytes32) {
return keccak256(abi.encode(_MESSAGE_TYPEHASH, hash));
}
/// @notice Returns the domain name and version to use when creating EIP-712 signatures.
///
/// @dev MUST be defined by the implementation.
///
/// @return name The user readable name of signing domain.
/// @return version The current major version of the signing domain.
function _domainNameAndVersion() internal view virtual returns (string memory name, string memory version);
/// @notice Validates the `signature` against the given `hash`.
///
/// @dev MUST be defined by the implementation.
///
/// @param hash The hash whose signature has been performed on.
/// @param signature The signature associated with `hash`.
///
/// @return `true` is the signature is valid, else `false`.
function _isValidSignature(bytes32 hash, bytes calldata signature) internal view virtual returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
/// @notice Storage layout used by this contract.
///
/// @custom:storage-location erc7201:coinbase.storage.MultiOwnable
struct MultiOwnableStorage {
/// @dev Tracks the index of the next owner to add.
uint256 nextOwnerIndex;
/// @dev Tracks number of owners that have been removed.
uint256 removedOwnersCount;
/// @dev Maps index to owner bytes, used to idenfitied owners via a uint256 index.
///
/// Some uses—-such as signature validation for secp256r1 public key owners—-
/// requires the caller to assert the public key of the caller. To economize calldata,
/// we allow an index to identify an owner, so that the full owner bytes do
/// not need to be passed.
///
/// The `owner` bytes should either be
/// - An ABI encoded Ethereum address
/// - An ABI encoded public key
mapping(uint256 index => bytes owner) ownerAtIndex;
/// @dev Mapping of bytes to booleans indicating whether or not
/// bytes_ is an owner of this contract.
mapping(bytes bytes_ => bool isOwner_) isOwner;
}
/// @title Multi Ownable
///
/// @notice Auth contract allowing multiple owners, each identified as bytes.
///
/// @author Coinbase (https://github.com/coinbase/smart-wallet)
contract MultiOwnable {
/// @dev Slot for the `MultiOwnableStorage` struct in storage.
/// Computed from
/// keccak256(abi.encode(uint256(keccak256("coinbase.storage.MultiOwnable")) - 1)) & ~bytes32(uint256(0xff))
/// Follows ERC-7201 (see https://eips.ethereum.org/EIPS/eip-7201).
bytes32 private constant MUTLI_OWNABLE_STORAGE_LOCATION =
0x97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00;
/// @notice Thrown when the `msg.sender` is not an owner and is trying to call a privileged function.
error Unauthorized();
/// @notice Thrown when trying to add an already registered owner.
///
/// @param owner The owner bytes.
error AlreadyOwner(bytes owner);
/// @notice Thrown when trying to remove an owner from an index that is empty.
///
/// @param index The targeted index for removal.
error NoOwnerAtIndex(uint256 index);
/// @notice Thrown when `owner` argument does not match owner found at index.
///
/// @param index The index of the owner to be removed.
/// @param expectedOwner The owner passed in the remove call.
/// @param actualOwner The actual owner at `index`.
error WrongOwnerAtIndex(uint256 index, bytes expectedOwner, bytes actualOwner);
/// @notice Thrown when a provided owner is neither 64 bytes long (for public key)
/// nor a ABI encoded address.
///
/// @param owner The invalid owner.
error InvalidOwnerBytesLength(bytes owner);
/// @notice Thrown if a provided owner is 32 bytes long but does not fit in an `address` type.
///
/// @param owner The invalid owner.
error InvalidEthereumAddressOwner(bytes owner);
/// @notice Thrown when removeOwnerAtIndex is called and there is only one current owner.
error LastOwner();
/// @notice Thrown when removeLastOwner is called and there is more than one current owner.
///
/// @param ownersRemaining The number of current owners.
error NotLastOwner(uint256 ownersRemaining);
/// @notice Emitted when a new owner is registered.
///
/// @param index The owner index of the owner added.
/// @param owner The owner added.
event AddOwner(uint256 indexed index, bytes owner);
/// @notice Emitted when an owner is removed.
///
/// @param index The owner index of the owner removed.
/// @param owner The owner removed.
event RemoveOwner(uint256 indexed index, bytes owner);
/// @notice Access control modifier ensuring the caller is an authorized owner
modifier onlyOwner() virtual {
_checkOwner();
_;
}
/// @notice Adds a new Ethereum-address owner.
///
/// @param owner The owner address.
function addOwnerAddress(address owner) external virtual onlyOwner {
_addOwnerAtIndex(abi.encode(owner), _getMultiOwnableStorage().nextOwnerIndex++);
}
/// @notice Adds a new public-key owner.
///
/// @param x The owner public key x coordinate.
/// @param y The owner public key y coordinate.
function addOwnerPublicKey(bytes32 x, bytes32 y) external virtual onlyOwner {
_addOwnerAtIndex(abi.encode(x, y), _getMultiOwnableStorage().nextOwnerIndex++);
}
/// @notice Removes owner at the given `index`.
///
/// @dev Reverts if the owner is not registered at `index`.
/// @dev Reverts if there is currently only one owner.
/// @dev Reverts if `owner` does not match bytes found at `index`.
///
/// @param index The index of the owner to be removed.
/// @param owner The ABI encoded bytes of the owner to be removed.
function removeOwnerAtIndex(uint256 index, bytes calldata owner) external virtual onlyOwner {
if (ownerCount() == 1) {
revert LastOwner();
}
_removeOwnerAtIndex(index, owner);
}
/// @notice Removes owner at the given `index`, which should be the only current owner.
///
/// @dev Reverts if the owner is not registered at `index`.
/// @dev Reverts if there is currently more than one owner.
/// @dev Reverts if `owner` does not match bytes found at `index`.
///
/// @param index The index of the owner to be removed.
/// @param owner The ABI encoded bytes of the owner to be removed.
function removeLastOwner(uint256 index, bytes calldata owner) external virtual onlyOwner {
uint256 ownersRemaining = ownerCount();
if (ownersRemaining > 1) {
revert NotLastOwner(ownersRemaining);
}
_removeOwnerAtIndex(index, owner);
}
/// @notice Checks if the given `account` address is registered as owner.
///
/// @param account The account address to check.
///
/// @return `true` if the account is an owner else `false`.
function isOwnerAddress(address account) public view virtual returns (bool) {
return _getMultiOwnableStorage().isOwner[abi.encode(account)];
}
/// @notice Checks if the given `x`, `y` public key is registered as owner.
///
/// @param x The public key x coordinate.
/// @param y The public key y coordinate.
///
/// @return `true` if the account is an owner else `false`.
function isOwnerPublicKey(bytes32 x, bytes32 y) public view virtual returns (bool) {
return _getMultiOwnableStorage().isOwner[abi.encode(x, y)];
}
/// @notice Checks if the given `account` bytes is registered as owner.
///
/// @param account The account, should be ABI encoded address or public key.
///
/// @return `true` if the account is an owner else `false`.
function isOwnerBytes(bytes memory account) public view virtual returns (bool) {
return _getMultiOwnableStorage().isOwner[account];
}
/// @notice Returns the owner bytes at the given `index`.
///
/// @param index The index to lookup.
///
/// @return The owner bytes (empty if no owner is registered at this `index`).
function ownerAtIndex(uint256 index) public view virtual returns (bytes memory) {
return _getMultiOwnableStorage().ownerAtIndex[index];
}
/// @notice Returns the next index that will be used to add a new owner.
///
/// @return The next index that will be used to add a new owner.
function nextOwnerIndex() public view virtual returns (uint256) {
return _getMultiOwnableStorage().nextOwnerIndex;
}
/// @notice Returns the current number of owners
///
/// @return The current owner count
function ownerCount() public view virtual returns (uint256) {
MultiOwnableStorage storage $ = _getMultiOwnableStorage();
return $.nextOwnerIndex - $.removedOwnersCount;
}
/// @notice Tracks the number of owners removed
///
/// @dev Used with `this.nextOwnerIndex` to avoid removing all owners
///
/// @return The number of owners that have been removed.
function removedOwnersCount() public view virtual returns (uint256) {
return _getMultiOwnableStorage().removedOwnersCount;
}
/// @notice Initialize the owners of this contract.
///
/// @dev Intended to be called contract is first deployed and never again.
/// @dev Reverts if a provided owner is neither 64 bytes long (for public key) nor a valid address.
///
/// @param owners The initial set of owners.
function _initializeOwners(bytes[] memory owners) internal virtual {
MultiOwnableStorage storage $ = _getMultiOwnableStorage();
uint256 nextOwnerIndex_ = $.nextOwnerIndex;
for (uint256 i; i < owners.length; i++) {
if (owners[i].length != 32 && owners[i].length != 64) {
revert InvalidOwnerBytesLength(owners[i]);
}
if (owners[i].length == 32 && uint256(bytes32(owners[i])) > type(uint160).max) {
revert InvalidEthereumAddressOwner(owners[i]);
}
_addOwnerAtIndex(owners[i], nextOwnerIndex_++);
}
$.nextOwnerIndex = nextOwnerIndex_;
}
/// @notice Adds an owner at the given `index`.
///
/// @dev Reverts if `owner` is already registered as an owner.
///
/// @param owner The owner raw bytes to register.
/// @param index The index to write to.
function _addOwnerAtIndex(bytes memory owner, uint256 index) internal virtual {
if (isOwnerBytes(owner)) revert AlreadyOwner(owner);
MultiOwnableStorage storage $ = _getMultiOwnableStorage();
$.isOwner[owner] = true;
$.ownerAtIndex[index] = owner;
emit AddOwner(index, owner);
}
/// @notice Removes owner at the given `index`.
///
/// @dev Reverts if the owner is not registered at `index`.
/// @dev Reverts if `owner` does not match bytes found at `index`.
///
/// @param index The index of the owner to be removed.
/// @param owner The ABI encoded bytes of the owner to be removed.
function _removeOwnerAtIndex(uint256 index, bytes calldata owner) internal virtual {
bytes memory owner_ = ownerAtIndex(index);
if (owner_.length == 0) revert NoOwnerAtIndex(index);
if (keccak256(owner_) != keccak256(owner)) {
revert WrongOwnerAtIndex({index: index, expectedOwner: owner, actualOwner: owner_});
}
MultiOwnableStorage storage $ = _getMultiOwnableStorage();
delete $.isOwner[owner];
delete $.ownerAtIndex[index];
$.removedOwnersCount++;
emit RemoveOwner(index, owner);
}
/// @notice Checks if the sender is an owner of this contract or the contract itself.
///
/// @dev Revert if the sender is not an owner fo the contract itself.
function _checkOwner() internal view virtual {
if (isOwnerAddress(msg.sender) || (msg.sender == address(this))) {
return;
}
revert Unauthorized();
}
/// @notice Helper function to get a storage reference to the `MultiOwnableStorage` struct.
///
/// @return $ A storage reference to the `MultiOwnableStorage` struct.
function _getMultiOwnableStorage() internal pure returns (MultiOwnableStorage storage $) {
assembly ("memory-safe") {
$.slot := MUTLI_OWNABLE_STORAGE_LOCATION
}
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
/* solhint-disable no-inline-assembly */
/**
* returned data from validateUserOp.
* validateUserOp returns a uint256, with 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
function _parseValidationData(uint 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);
}
// intersect account and paymaster ranges.
function _intersectTimeRange(uint256 validationData, uint256 paymasterValidationData) pure returns (ValidationData memory) {
ValidationData memory accountValidationData = _parseValidationData(validationData);
ValidationData memory pmValidationData = _parseValidationData(paymasterValidationData);
address aggregator = accountValidationData.aggregator;
if (aggregator == address(0)) {
aggregator = pmValidationData.aggregator;
}
uint48 validAfter = accountValidationData.validAfter;
uint48 validUntil = accountValidationData.validUntil;
uint48 pmValidAfter = pmValidationData.validAfter;
uint48 pmValidUntil = pmValidationData.validUntil;
if (validAfter < pmValidAfter) validAfter = pmValidAfter;
if (validUntil > pmValidUntil) validUntil = pmValidUntil;
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 {
let mem := mload(0x40)
let len := data.length
calldatacopy(mem, data.offset, len)
ret := keccak256(mem, len)
}
}//********************************************************************************************/
// ___ _ ___ _ _ _ _
// | __| _ ___ __| |_ / __|_ _ _ _ _ __| |_ ___ | | (_) |__
// | _| '_/ -_|_-< ' \ | (__| '_| || | '_ \ _/ _ \ | |__| | '_ \
// |_||_| \___/__/_||_| \___|_| \_, | .__/\__\___/ |____|_|_.__/
// |__/|_|
///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project
///* License: This software is licensed under MIT License
///* This Code may be reused including license and copyright notice.
///* See LICENSE file at the root folder of the project.
///* FILE: FCL_ecdsa.sol
///*
///*
///* DESCRIPTION: ecdsa verification implementation
///*
//**************************************************************************************/
//* WARNING: this code SHALL not be used for non prime order curves for security reasons.
// Code is optimized for a=-3 only curves with prime order, constant like -1, -2 shall be replaced
// if ever used for other curve than sec256R1
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19 <0.9.0;
import {FCL_Elliptic_ZZ} from "./FCL_elliptic.sol";
library FCL_ecdsa {
// Set parameters for curve sec256r1.public
//curve order (number of points)
uint256 constant n = FCL_Elliptic_ZZ.n;
/**
* @dev ECDSA verification, given , signature, and public key.
*/
/**
* @dev ECDSA verification, given , signature, and public key, no calldata version
*/
function ecdsa_verify(bytes32 message, uint256 r, uint256 s, uint256 Qx, uint256 Qy) internal view returns (bool){
if (r == 0 || r >= FCL_Elliptic_ZZ.n || s == 0 || s >= FCL_Elliptic_ZZ.n) {
return false;
}
if (!FCL_Elliptic_ZZ.ecAff_isOnCurve(Qx, Qy)) {
return false;
}
uint256 sInv = FCL_Elliptic_ZZ.FCL_nModInv(s);
uint256 scalar_u = mulmod(uint256(message), sInv, FCL_Elliptic_ZZ.n);
uint256 scalar_v = mulmod(r, sInv, FCL_Elliptic_ZZ.n);
uint256 x1;
x1 = FCL_Elliptic_ZZ.ecZZ_mulmuladd_S_asm(Qx, Qy, scalar_u, scalar_v);
x1= addmod(x1, n-r,n );
return x1 == 0;
}
function ec_recover_r1(uint256 h, uint256 v, uint256 r, uint256 s) internal view returns (address)
{
if (r == 0 || r >= FCL_Elliptic_ZZ.n || s == 0 || s >= FCL_Elliptic_ZZ.n) {
return address(0);
}
uint256 y=FCL_Elliptic_ZZ.ec_Decompress(r, v-27);
uint256 rinv=FCL_Elliptic_ZZ.FCL_nModInv(r);
uint256 u1=mulmod(FCL_Elliptic_ZZ.n-addmod(0,h,FCL_Elliptic_ZZ.n), rinv,FCL_Elliptic_ZZ.n);//-hr^-1
uint256 u2=mulmod(s, rinv,FCL_Elliptic_ZZ.n);//sr^-1
uint256 Qx;
uint256 Qy;
(Qx,Qy)=FCL_Elliptic_ZZ.ecZZ_mulmuladd(r,y, u1, u2);
return address(uint160(uint256(keccak256(abi.encodePacked(Qx, Qy)))));
}
function ecdsa_precomputed_verify(bytes32 message, uint256 r, uint256 s, address Shamir8)
internal view
returns (bool)
{
if (r == 0 || r >= n || s == 0 || s >= n) {
return false;
}
/* Q is pushed via the contract at address Shamir8 assumed to be correct
if (!isOnCurve(Q[0], Q[1])) {
return false;
}*/
uint256 sInv = FCL_Elliptic_ZZ.FCL_nModInv(s);
uint256 X;
//Shamir 8 dimensions
X = FCL_Elliptic_ZZ.ecZZ_mulmuladd_S8_extcode(mulmod(uint256(message), sInv, n), mulmod(r, sInv, n), Shamir8);
X= addmod(X, n-r,n );
return X == 0;
} //end ecdsa_precomputed_verify()
function ecdsa_precomputed_verify(bytes32 message, uint256[2] calldata rs, address Shamir8)
internal view
returns (bool)
{
uint256 r = rs[0];
uint256 s = rs[1];
if (r == 0 || r >= n || s == 0 || s >= n) {
return false;
}
/* Q is pushed via the contract at address Shamir8 assumed to be correct
if (!isOnCurve(Q[0], Q[1])) {
return false;
}*/
uint256 sInv = FCL_Elliptic_ZZ.FCL_nModInv(s);
uint256 X;
//Shamir 8 dimensions
X = FCL_Elliptic_ZZ.ecZZ_mulmuladd_S8_extcode(mulmod(uint256(message), sInv, n), mulmod(r, sInv, n), Shamir8);
X= addmod(X, n-r,n );
return X == 0;
} //end ecdsa_precomputed_verify()
}//********************************************************************************************/
// ___ _ ___ _ _ _ _
// | __| _ ___ __| |_ / __|_ _ _ _ _ __| |_ ___ | | (_) |__
// | _| '_/ -_|_-< ' \ | (__| '_| || | '_ \ _/ _ \ | |__| | '_ \
// |_||_| \___/__/_||_| \___|_| \_, | .__/\__\___/ |____|_|_.__/
// |__/|_|
///* Copyright (C) 2022 - Renaud Dubois - This file is part of FCL (Fresh CryptoLib) project
///* License: This software is licensed under MIT License
///* This Code may be reused including license and copyright notice.
///* See LICENSE file at the root folder of the project.
///* FILE: FCL_elliptic.sol
///*
///*
///* DESCRIPTION: modified XYZZ system coordinates for EVM elliptic point multiplication
///* optimization
///*
//**************************************************************************************/
//* WARNING: this code SHALL not be used for non prime order curves for security reasons.
// Code is optimized for a=-3 only curves with prime order, constant like -1, -2 shall be replaced
// if ever used for other curve than sec256R1
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19 <0.9.0;
library FCL_Elliptic_ZZ {
// Set parameters for curve sec256r1.
// address of the ModExp precompiled contract (Arbitrary-precision exponentiation under modulo)
address constant MODEXP_PRECOMPILE = 0x0000000000000000000000000000000000000005;
//curve prime field modulus
uint256 constant p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
//short weierstrass first coefficient
uint256 constant a = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC;
//short weierstrass second coefficient
uint256 constant b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B;
//generating point affine coordinates
uint256 constant gx = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296;
uint256 constant gy = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5;
//curve order (number of points)
uint256 constant n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551;
/* -2 mod p constant, used to speed up inversion and doubling (avoid negation)*/
uint256 constant minus_2 = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD;
/* -2 mod n constant, used to speed up inversion*/
uint256 constant minus_2modn = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F;
uint256 constant minus_1 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
//P+1 div 4
uint256 constant pp1div4=0x3fffffffc0000000400000000000000000000000400000000000000000000000;
//arbitrary constant to express no quadratic residuosity
uint256 constant _NOTSQUARE=0xFFFFFFFF00000002000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
uint256 constant _NOTONCURVE=0xFFFFFFFF00000003000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
/**
* /* inversion mod n via a^(n-2), use of precompiled using little Fermat theorem
*/
function FCL_nModInv(uint256 u) internal view returns (uint256 result) {
assembly {
let pointer := mload(0x40)
// Define length of base, exponent and modulus. 0x20 == 32 bytes
mstore(pointer, 0x20)
mstore(add(pointer, 0x20), 0x20)
mstore(add(pointer, 0x40), 0x20)
// Define variables base, exponent and modulus
mstore(add(pointer, 0x60), u)
mstore(add(pointer, 0x80), minus_2modn)
mstore(add(pointer, 0xa0), n)
// Call the precompiled contract 0x05 = ModExp
if iszero(staticcall(not(0), 0x05, pointer, 0xc0, pointer, 0x20)) { revert(0, 0) }
result := mload(pointer)
}
}
/**
* /* @dev inversion mod nusing little Fermat theorem via a^(n-2), use of precompiled
*/
function FCL_pModInv(uint256 u) internal view returns (uint256 result) {
assembly {
let pointer := mload(0x40)
// Define length of base, exponent and modulus. 0x20 == 32 bytes
mstore(pointer, 0x20)
mstore(add(pointer, 0x20), 0x20)
mstore(add(pointer, 0x40), 0x20)
// Define variables base, exponent and modulus
mstore(add(pointer, 0x60), u)
mstore(add(pointer, 0x80), minus_2)
mstore(add(pointer, 0xa0), p)
// Call the precompiled contract 0x05 = ModExp
if iszero(staticcall(not(0), 0x05, pointer, 0xc0, pointer, 0x20)) { revert(0, 0) }
result := mload(pointer)
}
}
//Coron projective shuffling, take as input alpha as blinding factor
function ecZZ_Coronize(uint256 alpha, uint256 x, uint256 y, uint256 zz, uint256 zzz) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3)
{
uint256 alpha2=mulmod(alpha,alpha,p);
x3=mulmod(alpha2, x,p); //alpha^-2.x
y3=mulmod(mulmod(alpha, alpha2,p), y,p);
zz3=mulmod(zz,alpha2,p);//alpha^2 zz
zzz3=mulmod(zzz,mulmod(alpha, alpha2,p),p);//alpha^3 zzz
return (x3, y3, zz3, zzz3);
}
function ecZZ_Add(uint256 x1, uint256 y1, uint256 zz1, uint256 zzz1, uint256 x2, uint256 y2, uint256 zz2, uint256 zzz2) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3)
{
uint256 u1=mulmod(x1,zz2,p); // U1 = X1*ZZ2
uint256 u2=mulmod(x2, zz1,p); // U2 = X2*ZZ1
u2=addmod(u2, p-u1, p);// P = U2-U1
x1=mulmod(u2, u2, p);//PP
x2=mulmod(x1, u2, p);//PPP
zz3=mulmod(x1, mulmod(zz1, zz2, p),p);//ZZ3 = ZZ1*ZZ2*PP
zzz3=mulmod(zzz1, mulmod(zzz2, x2, p),p);//ZZZ3 = ZZZ1*ZZZ2*PPP
zz1=mulmod(y1, zzz2,p); // S1 = Y1*ZZZ2
zz2=mulmod(y2, zzz1, p); // S2 = Y2*ZZZ1
zz2=addmod(zz2, p-zz1, p);//R = S2-S1
zzz1=mulmod(u1, x1,p); //Q = U1*PP
x3= addmod(addmod(mulmod(zz2, zz2, p), p-x2,p), mulmod(minus_2, zzz1,p),p); //X3 = R2-PPP-2*Q
y3=addmod( mulmod(zz2, addmod(zzz1, p-x3, p),p), p-mulmod(zz1, x2, p),p);//R*(Q-X3)-S1*PPP
return (x3, y3, zz3, zzz3);
}
/// @notice Calculate one modular square root of a given integer. Assume that p=3 mod 4.
/// @dev Uses the ModExp precompiled contract at address 0x05 for fast computation using little Fermat theorem
/// @param self The integer of which to find the modular inverse
/// @return result The modular inverse of the input integer. If the modular inverse doesn't exist, it revert the tx
function SqrtMod(uint256 self) internal view returns (uint256 result){
assembly ("memory-safe") {
// load the free memory pointer value
let pointer := mload(0x40)
// Define length of base (Bsize)
mstore(pointer, 0x20)
// Define the exponent size (Esize)
mstore(add(pointer, 0x20), 0x20)
// Define the modulus size (Msize)
mstore(add(pointer, 0x40), 0x20)
// Define variables base (B)
mstore(add(pointer, 0x60), self)
// Define the exponent (E)
mstore(add(pointer, 0x80), pp1div4)
// We save the point of the last argument, it will be override by the result
// of the precompile call in order to avoid paying for the memory expansion properly
let _result := add(pointer, 0xa0)
// Define the modulus (M)
mstore(_result, p)
// Call the precompiled ModExp (0x05) https://www.evm.codes/precompiled#0x05
if iszero(
staticcall(
not(0), // amount of gas to send
MODEXP_PRECOMPILE, // target
pointer, // argsOffset
0xc0, // argsSize (6 * 32 bytes)
_result, // retOffset (we override M to avoid paying for the memory expansion)
0x20 // retSize (32 bytes)
)
) { revert(0, 0) }
result := mload(_result)
// result :=addmod(result,0,p)
}
if(mulmod(result,result,p)!=self){
result=_NOTSQUARE;
}
return result;
}
/**
* /* @dev Convert from affine rep to XYZZ rep
*/
function ecAff_SetZZ(uint256 x0, uint256 y0) internal pure returns (uint256[4] memory P) {
unchecked {
P[2] = 1; //ZZ
P[3] = 1; //ZZZ
P[0] = x0;
P[1] = y0;
}
}
function ec_Decompress(uint256 x, uint256 parity) internal view returns(uint256 y){
uint256 y2=mulmod(x,mulmod(x,x,p),p);//x3
y2=addmod(b,addmod(y2,mulmod(x,a,p),p),p);//x3+ax+b
y=SqrtMod(y2);
if(y==_NOTSQUARE){
return _NOTONCURVE;
}
if((y&1)!=(parity&1)){
y=p-y;
}
}
/**
* /* @dev Convert from XYZZ rep to affine rep
*/
/* https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz-3.html#addition-add-2008-s*/
function ecZZ_SetAff(uint256 x, uint256 y, uint256 zz, uint256 zzz) internal view returns (uint256 x1, uint256 y1) {
uint256 zzzInv = FCL_pModInv(zzz); //1/zzz
y1 = mulmod(y, zzzInv, p); //Y/zzz
uint256 _b = mulmod(zz, zzzInv, p); //1/z
zzzInv = mulmod(_b, _b, p); //1/zz
x1 = mulmod(x, zzzInv, p); //X/zz
}
/**
* /* @dev Sutherland2008 doubling
*/
/* The "dbl-2008-s-1" doubling formulas */
function ecZZ_Dbl(uint256 x, uint256 y, uint256 zz, uint256 zzz)
internal
pure
returns (uint256 P0, uint256 P1, uint256 P2, uint256 P3)
{
unchecked {
assembly {
P0 := mulmod(2, y, p) //U = 2*Y1
P2 := mulmod(P0, P0, p) // V=U^2
P3 := mulmod(x, P2, p) // S = X1*V
P1 := mulmod(P0, P2, p) // W=UV
P2 := mulmod(P2, zz, p) //zz3=V*ZZ1
zz := mulmod(3, mulmod(addmod(x, sub(p, zz), p), addmod(x, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
P0 := addmod(mulmod(zz, zz, p), mulmod(minus_2, P3, p), p) //X3=M^2-2S
x := mulmod(zz, addmod(P3, sub(p, P0), p), p) //M(S-X3)
P3 := mulmod(P1, zzz, p) //zzz3=W*zzz1
P1 := addmod(x, sub(p, mulmod(P1, y, p)), p) //Y3= M(S-X3)-W*Y1
}
}
return (P0, P1, P2, P3);
}
/**
* @dev Sutherland2008 add a ZZ point with a normalized point and greedy formulae
* warning: assume that P1(x1,y1)!=P2(x2,y2), true in multiplication loop with prime order (cofactor 1)
*/
function ecZZ_AddN(uint256 x1, uint256 y1, uint256 zz1, uint256 zzz1, uint256 x2, uint256 y2)
internal
pure
returns (uint256 P0, uint256 P1, uint256 P2, uint256 P3)
{
unchecked {
if (y1 == 0) {
return (x2, y2, 1, 1);
}
assembly {
y1 := sub(p, y1)
y2 := addmod(mulmod(y2, zzz1, p), y1, p)
x2 := addmod(mulmod(x2, zz1, p), sub(p, x1), p)
P0 := mulmod(x2, x2, p) //PP = P^2
P1 := mulmod(P0, x2, p) //PPP = P*PP
P2 := mulmod(zz1, P0, p) ////ZZ3 = ZZ1*PP
P3 := mulmod(zzz1, P1, p) ////ZZZ3 = ZZZ1*PPP
zz1 := mulmod(x1, P0, p) //Q = X1*PP
P0 := addmod(addmod(mulmod(y2, y2, p), sub(p, P1), p), mulmod(minus_2, zz1, p), p) //R^2-PPP-2*Q
P1 := addmod(mulmod(addmod(zz1, sub(p, P0), p), y2, p), mulmod(y1, P1, p), p) //R*(Q-X3)
}
//end assembly
} //end unchecked
return (P0, P1, P2, P3);
}
/**
* @dev Return the zero curve in XYZZ coordinates.
*/
function ecZZ_SetZero() internal pure returns (uint256 x, uint256 y, uint256 zz, uint256 zzz) {
return (0, 0, 0, 0);
}
/**
* @dev Check if point is the neutral of the curve
*/
// uint256 x0, uint256 y0, uint256 zz0, uint256 zzz0
function ecZZ_IsZero(uint256, uint256 y0, uint256, uint256) internal pure returns (bool) {
return y0 == 0;
}
/**
* @dev Return the zero curve in affine coordinates. Compatible with the double formulae (no special case)
*/
function ecAff_SetZero() internal pure returns (uint256 x, uint256 y) {
return (0, 0);
}
/**
* @dev Check if the curve is the zero curve in affine rep.
*/
// uint256 x, uint256 y)
function ecAff_IsZero(uint256, uint256 y) internal pure returns (bool flag) {
return (y == 0);
}
/**
* @dev Check if a point in affine coordinates is on the curve (reject Neutral that is indeed on the curve).
*/
function ecAff_isOnCurve(uint256 x, uint256 y) internal pure returns (bool) {
if (x >= p || y >= p || ((x == 0) && (y == 0))) {
return false;
}
unchecked {
uint256 LHS = mulmod(y, y, p); // y^2
uint256 RHS = addmod(mulmod(mulmod(x, x, p), x, p), mulmod(x, a, p), p); // x^3+ax
RHS = addmod(RHS, b, p); // x^3 + a*x + b
return LHS == RHS;
}
}
/**
* @dev Add two elliptic curve points in affine coordinates. Deal with P=Q
*/
function ecAff_add(uint256 x0, uint256 y0, uint256 x1, uint256 y1) internal view returns (uint256, uint256) {
uint256 zz0;
uint256 zzz0;
if (ecAff_IsZero(x0, y0)) return (x1, y1);
if (ecAff_IsZero(x1, y1)) return (x0, y0);
if((x0==x1)&&(y0==y1)) {
(x0, y0, zz0, zzz0) = ecZZ_Dbl(x0, y0,1,1);
}
else{
(x0, y0, zz0, zzz0) = ecZZ_AddN(x0, y0, 1, 1, x1, y1);
}
return ecZZ_SetAff(x0, y0, zz0, zzz0);
}
/**
* @dev Computation of uG+vQ using Strauss-Shamir's trick, G basepoint, Q public key
* Returns only x for ECDSA use
* */
function ecZZ_mulmuladd_S_asm(
uint256 Q0,
uint256 Q1, //affine rep for input point Q
uint256 scalar_u,
uint256 scalar_v
) internal view returns (uint256 X) {
uint256 zz;
uint256 zzz;
uint256 Y;
uint256 index = 255;
uint256 H0;
uint256 H1;
unchecked {
if (scalar_u == 0 && scalar_v == 0) return 0;
(H0, H1) = ecAff_add(gx, gy, Q0, Q1);
if((H0==0)&&(H1==0))//handling Q=-G
{
scalar_u=addmod(scalar_u, n-scalar_v, n);
scalar_v=0;
if (scalar_u == 0 && scalar_v == 0) return 0;
}
assembly {
for { let T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) } eq(T4, 0) {
index := sub(index, 1)
T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
} {}
zz := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
if eq(zz, 1) {
X := gx
Y := gy
}
if eq(zz, 2) {
X := Q0
Y := Q1
}
if eq(zz, 3) {
X := H0
Y := H1
}
index := sub(index, 1)
zz := 1
zzz := 1
for {} gt(minus_1, index) { index := sub(index, 1) } {
// inlined EcZZ_Dbl
let T1 := mulmod(2, Y, p) //U = 2*Y1, y free
let T2 := mulmod(T1, T1, p) // V=U^2
let T3 := mulmod(X, T2, p) // S = X1*V
T1 := mulmod(T1, T2, p) // W=UV
let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
T2 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S)
Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd
{
//value of dibit
T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
if iszero(T4) {
Y := sub(p, Y) //restore the -Y inversion
continue
} // if T4!=0
if eq(T4, 1) {
T1 := gx
T2 := gy
}
if eq(T4, 2) {
T1 := Q0
T2 := Q1
}
if eq(T4, 3) {
T1 := H0
T2 := H1
}
if iszero(zz) {
X := T1
Y := T2
zz := 1
zzz := 1
continue
}
// inlined EcZZ_AddN
//T3:=sub(p, Y)
//T3:=Y
let y2 := addmod(mulmod(T2, zzz, p), Y, p) //R
T2 := addmod(mulmod(T1, zz, p), sub(p, X), p) //P
//special extremely rare case accumulator where EcAdd is replaced by EcDbl, no need to optimize this
//todo : construct edge vector case
if iszero(y2) {
if iszero(T2) {
T1 := mulmod(minus_2, Y, p) //U = 2*Y1, y free
T2 := mulmod(T1, T1, p) // V=U^2
T3 := mulmod(X, T2, p) // S = X1*V
T1 := mulmod(T1, T2, p) // W=UV
y2 := mulmod(addmod(X, zz, p), addmod(X, sub(p, zz), p), p) //(X-ZZ)(X+ZZ)
T4 := mulmod(3, y2, p) //M=3*(X-ZZ)(X+ZZ)
zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3)
Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1
continue
}
}
T4 := mulmod(T2, T2, p) //PP
let TT1 := mulmod(T4, T2, p) //PPP, this one could be spared, but adding this register spare gas
zz := mulmod(zz, T4, p)
zzz := mulmod(zzz, TT1, p) //zz3=V*ZZ1
let TT2 := mulmod(X, T4, p)
T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, TT1), p), mulmod(minus_2, TT2, p), p)
Y := addmod(mulmod(addmod(TT2, sub(p, T4), p), y2, p), mulmod(Y, TT1, p), p)
X := T4
}
} //end loop
let T := mload(0x40)
mstore(add(T, 0x60), zz)
//(X,Y)=ecZZ_SetAff(X,Y,zz, zzz);
//T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile:
// Define length of base, exponent and modulus. 0x20 == 32 bytes
mstore(T, 0x20)
mstore(add(T, 0x20), 0x20)
mstore(add(T, 0x40), 0x20)
// Define variables base, exponent and modulus
//mstore(add(pointer, 0x60), u)
mstore(add(T, 0x80), minus_2)
mstore(add(T, 0xa0), p)
// Call the precompiled contract 0x05 = ModExp
if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) }
//Y:=mulmod(Y,zzz,p)//Y/zzz
//zz :=mulmod(zz, mload(T),p) //1/z
//zz:= mulmod(zz,zz,p) //1/zz
X := mulmod(X, mload(T), p) //X/zz
} //end assembly
} //end unchecked
return X;
}
/**
* @dev Computation of uG+vQ using Strauss-Shamir's trick, G basepoint, Q public key
* Returns affine representation of point (normalized)
* */
function ecZZ_mulmuladd(
uint256 Q0,
uint256 Q1, //affine rep for input point Q
uint256 scalar_u,
uint256 scalar_v
) internal view returns (uint256 X, uint256 Y) {
uint256 zz;
uint256 zzz;
uint256 index = 255;
uint256[6] memory T;
uint256[2] memory H;
unchecked {
if (scalar_u == 0 && scalar_v == 0) return (0,0);
(H[0], H[1]) = ecAff_add(gx, gy, Q0, Q1); //will not work if Q=P, obvious forbidden private key
assembly {
for { let T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1)) } eq(T4, 0) {
index := sub(index, 1)
T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
} {}
zz := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
if eq(zz, 1) {
X := gx
Y := gy
}
if eq(zz, 2) {
X := Q0
Y := Q1
}
if eq(zz, 3) {
Y := mload(add(H,32))
X := mload(H)
}
index := sub(index, 1)
zz := 1
zzz := 1
for {} gt(minus_1, index) { index := sub(index, 1) } {
// inlined EcZZ_Dbl
let T1 := mulmod(2, Y, p) //U = 2*Y1, y free
let T2 := mulmod(T1, T1, p) // V=U^2
let T3 := mulmod(X, T2, p) // S = X1*V
T1 := mulmod(T1, T2, p) // W=UV
let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
T2 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S)
Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd
{
//value of dibit
T4 := add(shl(1, and(shr(index, scalar_v), 1)), and(shr(index, scalar_u), 1))
if iszero(T4) {
Y := sub(p, Y) //restore the -Y inversion
continue
} // if T4!=0
if eq(T4, 1) {
T1 := gx
T2 := gy
}
if eq(T4, 2) {
T1 := Q0
T2 := Q1
}
if eq(T4, 3) {
T1 := mload(H)
T2 := mload(add(H,32))
}
if iszero(zz) {
X := T1
Y := T2
zz := 1
zzz := 1
continue
}
// inlined EcZZ_AddN
//T3:=sub(p, Y)
//T3:=Y
let y2 := addmod(mulmod(T2, zzz, p), Y, p) //R
T2 := addmod(mulmod(T1, zz, p), sub(p, X), p) //P
//special extremely rare case accumulator where EcAdd is replaced by EcDbl, no need to optimize this
//todo : construct edge vector case
if iszero(y2) {
if iszero(T2) {
T1 := mulmod(minus_2, Y, p) //U = 2*Y1, y free
T2 := mulmod(T1, T1, p) // V=U^2
T3 := mulmod(X, T2, p) // S = X1*V
T1 := mulmod(T1, T2, p) // W=UV
y2 := mulmod(addmod(X, zz, p), addmod(X, sub(p, zz), p), p) //(X-ZZ)(X+ZZ)
T4 := mulmod(3, y2, p) //M=3*(X-ZZ)(X+ZZ)
zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3)
Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1
continue
}
}
T4 := mulmod(T2, T2, p) //PP
let TT1 := mulmod(T4, T2, p) //PPP, this one could be spared, but adding this register spare gas
zz := mulmod(zz, T4, p)
zzz := mulmod(zzz, TT1, p) //zz3=V*ZZ1
let TT2 := mulmod(X, T4, p)
T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, TT1), p), mulmod(minus_2, TT2, p), p)
Y := addmod(mulmod(addmod(TT2, sub(p, T4), p), y2, p), mulmod(Y, TT1, p), p)
X := T4
}
} //end loop
mstore(add(T, 0x60), zzz)
//(X,Y)=ecZZ_SetAff(X,Y,zz, zzz);
//T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile:
// Define length of base, exponent and modulus. 0x20 == 32 bytes
mstore(T, 0x20)
mstore(add(T, 0x20), 0x20)
mstore(add(T, 0x40), 0x20)
// Define variables base, exponent and modulus
//mstore(add(pointer, 0x60), u)
mstore(add(T, 0x80), minus_2)
mstore(add(T, 0xa0), p)
// Call the precompiled contract 0x05 = ModExp
if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) }
Y:=mulmod(Y,mload(T),p)//Y/zzz
zz :=mulmod(zz, mload(T),p) //1/z
zz:= mulmod(zz,zz,p) //1/zz
X := mulmod(X, zz, p) //X/zz
} //end assembly
} //end unchecked
return (X,Y);
}
//8 dimensions Shamir's trick, using precomputations stored in Shamir8, stored as Bytecode of an external
//contract at given address dataPointer
//(thx to Lakhdar https://github.com/Kelvyne for EVM storage explanations and tricks)
// the external tool to generate tables from public key is in the /sage directory
function ecZZ_mulmuladd_S8_extcode(uint256 scalar_u, uint256 scalar_v, address dataPointer)
internal view
returns (uint256 X /*, uint Y*/ )
{
unchecked {
uint256 zz; // third and coordinates of the point
uint256[6] memory T;
zz = 256; //start index
while (T[0] == 0) {
zz = zz - 1;
//tbd case of msb octobit is null
T[0] = 64
* (
128 * ((scalar_v >> zz) & 1) + 64 * ((scalar_v >> (zz - 64)) & 1)
+ 32 * ((scalar_v >> (zz - 128)) & 1) + 16 * ((scalar_v >> (zz - 192)) & 1)
+ 8 * ((scalar_u >> zz) & 1) + 4 * ((scalar_u >> (zz - 64)) & 1)
+ 2 * ((scalar_u >> (zz - 128)) & 1) + ((scalar_u >> (zz - 192)) & 1)
);
}
assembly {
extcodecopy(dataPointer, T, mload(T), 64)
let index := sub(zz, 1)
X := mload(T)
let Y := mload(add(T, 32))
let zzz := 1
zz := 1
//loop over 1/4 of scalars thx to Shamir's trick over 8 points
for {} gt(index, 191) { index := add(index, 191) } {
//inline Double
{
let TT1 := mulmod(2, Y, p) //U = 2*Y1, y free
let T2 := mulmod(TT1, TT1, p) // V=U^2
let T3 := mulmod(X, T2, p) // S = X1*V
let T1 := mulmod(TT1, T2, p) // W=UV
let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
//T2:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3)
let T5 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S)
//Y:= addmod(T2, sub(p, mulmod(T1, Y ,p)),p )//Y3= M(S-X3)-W*Y1
Y := addmod(mulmod(T1, Y, p), T5, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd
/* compute element to access in precomputed table */
}
{
let T4 := add(shl(13, and(shr(index, scalar_v), 1)), shl(9, and(shr(index, scalar_u), 1)))
let index2 := sub(index, 64)
let T3 :=
add(T4, add(shl(12, and(shr(index2, scalar_v), 1)), shl(8, and(shr(index2, scalar_u), 1))))
let index3 := sub(index2, 64)
let T2 :=
add(T3, add(shl(11, and(shr(index3, scalar_v), 1)), shl(7, and(shr(index3, scalar_u), 1))))
index := sub(index3, 64)
let T1 :=
add(T2, add(shl(10, and(shr(index, scalar_v), 1)), shl(6, and(shr(index, scalar_u), 1))))
//tbd: check validity of formulae with (0,1) to remove conditional jump
if iszero(T1) {
Y := sub(p, Y)
continue
}
extcodecopy(dataPointer, T, T1, 64)
}
{
/* Access to precomputed table using extcodecopy hack */
// inlined EcZZ_AddN
if iszero(zz) {
X := mload(T)
Y := mload(add(T, 32))
zz := 1
zzz := 1
continue
}
let y2 := addmod(mulmod(mload(add(T, 32)), zzz, p), Y, p)
let T2 := addmod(mulmod(mload(T), zz, p), sub(p, X), p)
//special case ecAdd(P,P)=EcDbl
if iszero(y2) {
if iszero(T2) {
let T1 := mulmod(minus_2, Y, p) //U = 2*Y1, y free
T2 := mulmod(T1, T1, p) // V=U^2
let T3 := mulmod(X, T2, p) // S = X1*V
T1 := mulmod(T1, T2, p) // W=UV
y2 := mulmod(addmod(X, zz, p), addmod(X, sub(p, zz), p), p) //(X-ZZ)(X+ZZ)
let T4 := mulmod(3, y2, p) //M=3*(X-ZZ)(X+ZZ)
zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
T2 := mulmod(T4, addmod(T3, sub(p, X), p), p) //M(S-X3)
Y := addmod(T2, mulmod(T1, Y, p), p) //Y3= M(S-X3)-W*Y1
continue
}
}
let T4 := mulmod(T2, T2, p)
let T1 := mulmod(T4, T2, p) //
zz := mulmod(zz, T4, p)
//zzz3=V*ZZ1
zzz := mulmod(zzz, T1, p) // W=UV/
let zz1 := mulmod(X, T4, p)
X := addmod(addmod(mulmod(y2, y2, p), sub(p, T1), p), mulmod(minus_2, zz1, p), p)
Y := addmod(mulmod(addmod(zz1, sub(p, X), p), y2, p), mulmod(Y, T1, p), p)
}
} //end loop
mstore(add(T, 0x60), zz)
//(X,Y)=ecZZ_SetAff(X,Y,zz, zzz);
//T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile:
// Define length of base, exponent and modulus. 0x20 == 32 bytes
mstore(T, 0x20)
mstore(add(T, 0x20), 0x20)
mstore(add(T, 0x40), 0x20)
// Define variables base, exponent and modulus
//mstore(add(pointer, 0x60), u)
mstore(add(T, 0x80), minus_2)
mstore(add(T, 0xa0), p)
// Call the precompiled contract 0x05 = ModExp
if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) }
zz := mload(T)
X := mulmod(X, zz, p) //X/zz
}
} //end unchecked
}
// improving the extcodecopy trick : append array at end of contract
function ecZZ_mulmuladd_S8_hackmem(uint256 scalar_u, uint256 scalar_v, uint256 dataPointer)
internal view
returns (uint256 X /*, uint Y*/ )
{
uint256 zz; // third and coordinates of the point
uint256[6] memory T;
zz = 256; //start index
unchecked {
while (T[0] == 0) {
zz = zz - 1;
//tbd case of msb octobit is null
T[0] = 64
* (
128 * ((scalar_v >> zz) & 1) + 64 * ((scalar_v >> (zz - 64)) & 1)
+ 32 * ((scalar_v >> (zz - 128)) & 1) + 16 * ((scalar_v >> (zz - 192)) & 1)
+ 8 * ((scalar_u >> zz) & 1) + 4 * ((scalar_u >> (zz - 64)) & 1)
+ 2 * ((scalar_u >> (zz - 128)) & 1) + ((scalar_u >> (zz - 192)) & 1)
);
}
assembly {
codecopy(T, add(mload(T), dataPointer), 64)
X := mload(T)
let Y := mload(add(T, 32))
let zzz := 1
zz := 1
//loop over 1/4 of scalars thx to Shamir's trick over 8 points
for { let index := 254 } gt(index, 191) { index := add(index, 191) } {
let T1 := mulmod(2, Y, p) //U = 2*Y1, y free
let T2 := mulmod(T1, T1, p) // V=U^2
let T3 := mulmod(X, T2, p) // S = X1*V
T1 := mulmod(T1, T2, p) // W=UV
let T4 := mulmod(3, mulmod(addmod(X, sub(p, zz), p), addmod(X, zz, p), p), p) //M=3*(X1-ZZ1)*(X1+ZZ1)
zzz := mulmod(T1, zzz, p) //zzz3=W*zzz1
zz := mulmod(T2, zz, p) //zz3=V*ZZ1, V free
X := addmod(mulmod(T4, T4, p), mulmod(minus_2, T3, p), p) //X3=M^2-2S
//T2:=mulmod(T4,addmod(T3, sub(p, X),p),p)//M(S-X3)
T2 := mulmod(T4, addmod(X, sub(p, T3), p), p) //-M(S-X3)=M(X3-S)
//Y:= addmod(T2, sub(p, mulmod(T1, Y ,p)),p )//Y3= M(S-X3)-W*Y1
Y := addmod(mulmod(T1, Y, p), T2, p) //-Y3= W*Y1-M(S-X3), we replace Y by -Y to avoid a sub in ecAdd
/* compute element to access in precomputed table */
T4 := add(shl(13, and(shr(index, scalar_v), 1)), shl(9, and(shr(index, scalar_u), 1)))
index := sub(index, 64)
T4 := add(T4, add(shl(12, and(shr(index, scalar_v), 1)), shl(8, and(shr(index, scalar_u), 1))))
index := sub(index, 64)
T4 := add(T4, add(shl(11, and(shr(index, scalar_v), 1)), shl(7, and(shr(index, scalar_u), 1))))
index := sub(index, 64)
T4 := add(T4, add(shl(10, and(shr(index, scalar_v), 1)), shl(6, and(shr(index, scalar_u), 1))))
//index:=add(index,192), restore index, interleaved with loop
//tbd: check validity of formulae with (0,1) to remove conditional jump
if iszero(T4) {
Y := sub(p, Y)
continue
}
{
/* Access to precomputed table using extcodecopy hack */
codecopy(T, add(T4, dataPointer), 64)
// inlined EcZZ_AddN
let y2 := addmod(mulmod(mload(add(T, 32)), zzz, p), Y, p)
T2 := addmod(mulmod(mload(T), zz, p), sub(p, X), p)
T4 := mulmod(T2, T2, p)
T1 := mulmod(T4, T2, p)
T2 := mulmod(zz, T4, p) // W=UV
zzz := mulmod(zzz, T1, p) //zz3=V*ZZ1
let zz1 := mulmod(X, T4, p)
T4 := addmod(addmod(mulmod(y2, y2, p), sub(p, T1), p), mulmod(minus_2, zz1, p), p)
Y := addmod(mulmod(addmod(zz1, sub(p, T4), p), y2, p), mulmod(Y, T1, p), p)
zz := T2
X := T4
}
} //end loop
mstore(add(T, 0x60), zz)
//(X,Y)=ecZZ_SetAff(X,Y,zz, zzz);
//T[0] = inverseModp_Hard(T[0], p); //1/zzz, inline modular inversion using precompile:
// Define length of base, exponent and modulus. 0x20 == 32 bytes
mstore(T, 0x20)
mstore(add(T, 0x20), 0x20)
mstore(add(T, 0x40), 0x20)
// Define variables base, exponent and modulus
//mstore(add(pointer, 0x60), u)
mstore(add(T, 0x80), minus_2)
mstore(add(T, 0xa0), p)
// Call the precompiled contract 0x05 = ModExp
if iszero(staticcall(not(0), 0x05, T, 0xc0, T, 0x20)) { revert(0, 0) }
zz := mload(T)
X := mulmod(X, zz, p) //X/zz
}
} //end unchecked
}
/**
* @dev ECDSA verification using a precomputed table of multiples of P and Q stored in contract at address Shamir8
* generation of contract bytecode for precomputations is done using sagemath code
* (see sage directory, WebAuthn_precompute.sage)
*/
/**
* @dev ECDSA verification using a precomputed table of multiples of P and Q appended at end of contract at address endcontract
* generation of contract bytecode for precomputations is done using sagemath code
* (see sage directory, WebAuthn_precompute.sage)
*/
function ecdsa_precomputed_hackmem(bytes32 message, uint256[2] calldata rs, uint256 endcontract)
internal view
returns (bool)
{
uint256 r = rs[0];
uint256 s = rs[1];
if (r == 0 || r >= n || s == 0 || s >= n) {
return false;
}
/* Q is pushed via bytecode assumed to be correct
if (!isOnCurve(Q[0], Q[1])) {
return false;
}*/
uint256 sInv = FCL_nModInv(s);
uint256 X;
//Shamir 8 dimensions
X = ecZZ_mulmuladd_S8_hackmem(mulmod(uint256(message), sInv, n), mulmod(r, sInv, n), endcontract);
assembly {
X := addmod(X, sub(n, r), n)
}
return X == 0;
} //end ecdsa_precomputed_verify()
} //EOF// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.2) (utils/Base64.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides a set of functions to operate with Base64 strings.
*/
library Base64 {
/**
* @dev Base64 Encoding/Decoding Table
* See sections 4 and 5 of https://datatracker.ietf.org/doc/html/rfc4648
*/
string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
string internal constant _TABLE_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
/**
* @dev Converts a `bytes` to its Bytes64 `string` representation.
*/
function encode(bytes memory data) internal pure returns (string memory) {
return _encode(data, _TABLE, true);
}
/**
* @dev Converts a `bytes` to its Bytes64Url `string` representation.
*/
function encodeURL(bytes memory data) internal pure returns (string memory) {
return _encode(data, _TABLE_URL, false);
}
/**
* @dev Internal table-agnostic conversion
*/
function _encode(bytes memory data, string memory table, bool withPadding) private pure returns (string memory) {
/**
* Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
* https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
*/
if (data.length == 0) return "";
// If padding is enabled, the final length should be `bytes` data length divided by 3 rounded up and then
// multiplied by 4 so that it leaves room for padding the last chunk
// - `data.length + 2` -> Round up
// - `/ 3` -> Number of 3-bytes chunks
// - `4 *` -> 4 characters for each chunk
// If padding is disabled, the final length should be `bytes` data length multiplied by 4/3 rounded up as
// opposed to when padding is required to fill the last chunk.
// - `4 *` -> 4 characters for each chunk
// - `data.length + 2` -> Round up
// - `/ 3` -> Number of 3-bytes chunks
uint256 resultLength = withPadding ? 4 * ((data.length + 2) / 3) : (4 * data.length + 2) / 3;
string memory result = new string(resultLength);
/// @solidity memory-safe-assembly
assembly {
// Prepare the lookup table (skip the first "length" byte)
let tablePtr := add(table, 1)
// Prepare result pointer, jump over length
let resultPtr := add(result, 0x20)
let dataPtr := data
let endPtr := add(data, mload(data))
// In some cases, the last iteration will read bytes after the end of the data. We cache the value, and
// set it to zero to make sure no dirty bytes are read in that section.
let afterPtr := add(endPtr, 0x20)
let afterCache := mload(afterPtr)
mstore(afterPtr, 0x00)
// Run over the input, 3 bytes at a time
for {
} lt(dataPtr, endPtr) {
} {
// Advance 3 bytes
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
// To write each character, shift the 3 byte (24 bits) chunk
// 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
// and apply logical AND with 0x3F to bitmask the least significant 6 bits.
// Use this as an index into the lookup table, mload an entire word
// so the desired character is in the least significant byte, and
// mstore8 this least significant byte into the result and continue.
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
}
// Reset the value that was cached
mstore(afterPtr, afterCache)
if withPadding {
// When data `bytes` is not exactly 3 bytes long
// it is padded with `=` characters at the end
switch mod(mload(data), 3)
case 1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case 2 {
mstore8(sub(resultPtr, 1), 0x3d)
}
}
}
return result;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The length of the output is too small to contain all the hex digits.
error HexLengthInsufficient();
/// @dev The length of the string is more than 32 bytes.
error TooBigForSmallString();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the base 10 decimal representation of `value`.
function toString(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits.
str := add(mload(0x40), 0x80)
// Update the free memory pointer to allocate.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
let w := not(0) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 1)`.
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp := div(temp, 10)
if iszero(temp) { break }
}
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 0x20)
// Store the length.
mstore(str, length)
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) {
return toString(uint256(value));
}
unchecked {
str = toString(~uint256(value) + 1);
}
/// @solidity memory-safe-assembly
assembly {
// We still have some spare memory space on the left,
// as we have allocated 3 words (96 bytes) for up to 78 digits.
let length := mload(str) // Load the string length.
mstore(str, 0x2d) // Store the '-' character.
str := sub(str, 1) // Move back the string pointer by a byte.
mstore(str, add(length, 1)) // Update the string length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HEXADECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2 + 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value, length);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexStringNoPrefix(uint256 value, uint256 length)
internal
pure
returns (string memory str)
{
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
// for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
// We add 0x20 to the total and round down to a multiple of 0x20.
// (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(str, add(length, length))
let w := not(1) // Tsk.
let temp := value
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for {} 1 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(str, start)) { break }
}
if temp {
mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
revert(0x1c, 0x04)
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2 + 2` bytes.
function toHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x".
/// The output excludes leading "0" from the `toHexString` output.
/// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := add(mload(str), 2) // Compute the length.
mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero.
str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output excludes leading "0" from the `toHexStringNoPrefix` output.
/// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := mload(str) // Get the length.
str := add(str, o) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2` bytes.
function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x40 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
str := add(mload(0x40), 0x80)
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let w := not(1) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
/// and the alphabets are capitalized conditionally according to
/// https://eips.ethereum.org/EIPS/eip-55
function toHexStringChecksummed(address value) internal pure returns (string memory str) {
str = toHexString(value);
/// @solidity memory-safe-assembly
assembly {
let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
let o := add(str, 0x22)
let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
let t := shl(240, 136) // `0b10001000 << 240`
for { let i := 0 } 1 {} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i := add(i, 1)
if eq(i, 20) { break }
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o := add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
function toHexString(address value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
str := mload(0x40)
// Allocate the memory.
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x28 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
mstore(0x40, add(str, 0x80))
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
str := add(str, 2)
mstore(str, 40)
let o := add(str, 0x20)
mstore(add(o, 40), 0)
value := shl(96, value)
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let i := 0 } 1 {} {
let p := add(o, add(i, i))
let temp := byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i := add(i, 1)
if eq(i, 20) { break }
}
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexString(bytes memory raw) internal pure returns (string memory str) {
str = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
let length := mload(raw)
str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
mstore(str, add(length, length)) // Store the length of the output.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let o := add(str, 0x20)
let end := add(raw, length)
for {} iszero(eq(raw, end)) {} {
raw := add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o := add(o, 2)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RUNE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of UTF characters in the string.
function runeCount(string memory s) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
mstore(0x00, div(not(0), 255))
mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
let o := add(s, 0x20)
let end := add(o, mload(s))
for { result := 1 } 1 { result := add(result, 1) } {
o := add(o, byte(0, mload(shr(250, mload(o)))))
if iszero(lt(o, end)) { break }
}
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string.
/// (i.e. all characters codes are in [0..127])
function is7BitASCII(string memory s) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let mask := shl(7, div(not(0), 255))
result := 1
let n := mload(s)
if n {
let o := add(s, 0x20)
let end := add(o, n)
let last := mload(end)
mstore(end, 0)
for {} 1 {} {
if and(mask, mload(o)) {
result := 0
break
}
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(end, last)
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance and bytecode compactness, byte string operations are restricted
// to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
// Usage of byte string operations on charsets with runes spanning two or more bytes
// can lead to undefined behavior.
/// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
function replace(string memory subject, string memory search, string memory replacement)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
let replacementLength := mload(replacement)
subject := add(subject, 0x20)
search := add(search, 0x20)
replacement := add(replacement, 0x20)
result := add(mload(0x40), 0x20)
let subjectEnd := add(subject, subjectLength)
if iszero(gt(searchLength, subjectLength)) {
let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Copy the `replacement` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(result, o), mload(add(replacement, o)))
o := add(o, 0x20)
if iszero(lt(o, replacementLength)) { break }
}
result := add(result, replacementLength)
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
}
let resultRemainder := result
result := add(mload(0x40), 0x20)
let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
// Copy the rest of the string one word at a time.
for {} lt(subject, subjectEnd) {} {
mstore(resultRemainder, mload(subject))
resultRemainder := add(resultRemainder, 0x20)
subject := add(subject, 0x20)
}
result := sub(result, 0x20)
let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
mstore(last, 0)
mstore(0x40, add(last, 0x20)) // Allocate the memory.
mstore(result, k) // Store the length.
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for { let subjectLength := mload(subject) } 1 {} {
if iszero(mload(search)) {
if iszero(gt(from, subjectLength)) {
result := from
break
}
result := subjectLength
break
}
let searchLength := mload(search)
let subjectStart := add(subject, 0x20)
result := not(0) // Initialize to `NOT_FOUND`.
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(add(search, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }
if iszero(lt(searchLength, 0x20)) {
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = indexOf(subject, search, 0);
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := not(0) // Initialize to `NOT_FOUND`.
let searchLength := mload(search)
if gt(searchLength, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), searchLength)
if iszero(gt(fromMax, from)) { from := fromMax }
let end := add(add(subject, 0x20), w)
subject := add(add(subject, 0x20), from)
if iszero(gt(subject, end)) { break }
// As this function is not too often used,
// we shall simply use keccak256 for smaller bytecode size.
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, add(end, 1))
break
}
subject := add(subject, w) // `sub(subject, 1)`.
if iszero(gt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = lastIndexOf(subject, search, uint256(int256(-1)));
}
/// @dev Returns true if `search` is found in `subject`, false otherwise.
function contains(string memory subject, string memory search) internal pure returns (bool) {
return indexOf(subject, search) != NOT_FOUND;
}
/// @dev Returns whether `subject` starts with `search`.
function startsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
iszero(gt(searchLength, mload(subject))),
eq(
keccak256(add(subject, 0x20), searchLength),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns whether `subject` ends with `search`.
function endsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
let subjectLength := mload(subject)
// Whether `search` is not longer than `subject`.
let withinRange := iszero(gt(searchLength, subjectLength))
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
withinRange,
eq(
keccak256(
// `subject + 0x20 + max(subjectLength - searchLength, 0)`.
add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
searchLength
),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns `subject` repeated `times`.
function repeat(string memory subject, uint256 times)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(or(iszero(times), iszero(subjectLength))) {
subject := add(subject, 0x20)
result := mload(0x40)
let output := add(result, 0x20)
for {} 1 {} {
// Copy the `subject` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(output, o), mload(add(subject, o)))
o := add(o, 0x20)
if iszero(lt(o, subjectLength)) { break }
}
output := add(output, subjectLength)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(output, 0) // Zeroize the slot after the string.
let resultLength := sub(output, add(result, 0x20))
mstore(result, resultLength) // Store the length.
// Allocate the memory.
mstore(0x40, add(result, add(resultLength, 0x20)))
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(string memory subject, uint256 start, uint256 end)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(gt(subjectLength, end)) { end := subjectLength }
if iszero(gt(subjectLength, start)) { start := subjectLength }
if lt(start, end) {
result := mload(0x40)
let resultLength := sub(end, start)
mstore(result, resultLength)
subject := add(subject, start)
let w := not(0x1f)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
mstore(add(result, o), mload(add(subject, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(result, 0x20), resultLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
/// `start` is a byte offset.
function slice(string memory subject, uint256 start)
internal
pure
returns (string memory result)
{
result = slice(subject, start, uint256(int256(-1)));
}
/// @dev Returns all the indices of `search` in `subject`.
/// The indices are byte offsets.
function indicesOf(string memory subject, string memory search)
internal
pure
returns (uint256[] memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
if iszero(gt(searchLength, subjectLength)) {
subject := add(subject, 0x20)
search := add(search, 0x20)
result := add(mload(0x40), 0x20)
let subjectStart := subject
let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Append to `result`.
mstore(result, sub(subject, subjectStart))
result := add(result, 0x20)
// Advance `subject` by `searchLength`.
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
let resultEnd := result
// Assign `result` to the free memory pointer.
result := mload(0x40)
// Store the length of `result`.
mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
// Allocate memory for result.
// We allocate one more word, so this array can be recycled for {split}.
mstore(0x40, add(resultEnd, 0x20))
}
}
}
/// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
function split(string memory subject, string memory delimiter)
internal
pure
returns (string[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(add(indicesEnd, w), mload(subject))
mstore(indices, add(mload(indices), 1))
let prevIndex := 0
for {} 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let elementLength := sub(index, prevIndex)
mstore(element, elementLength)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(element, 0x20), elementLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
// Store the `element` into the array.
mstore(indexPtr, element)
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
/// @dev Returns a concatenated string of `a` and `b`.
/// Cheaper than `string.concat()` and does not de-align the free memory pointer.
function concat(string memory a, string memory b)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
result := mload(0x40)
let aLength := mload(a)
// Copy `a` one word at a time, backwards.
for { let o := and(add(aLength, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let bLength := mload(b)
let output := add(result, aLength)
// Copy `b` one word at a time, backwards.
for { let o := and(add(bLength, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let totalLength := add(aLength, bLength)
let last := add(add(result, 0x20), totalLength)
// Zeroize the slot after the string.
mstore(last, 0)
// Stores the length.
mstore(result, totalLength)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, and(add(last, 0x1f), w))
}
}
/// @dev Returns a copy of the string in either lowercase or UPPERCASE.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function toCase(string memory subject, bool toUpper)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let length := mload(subject)
if length {
result := add(mload(0x40), 0x20)
subject := add(subject, 1)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
let w := not(0)
for { let o := length } 1 {} {
o := add(o, w)
let b := and(0xff, mload(add(subject, o)))
mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
if iszero(o) { break }
}
result := mload(0x40)
mstore(result, length) // Store the length.
let last := add(add(result, 0x20), length)
mstore(last, 0) // Zeroize the slot after the string.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
}
/// @dev Returns a string from a small bytes32 string.
/// `s` must be null-terminated, or behavior will be undefined.
function fromSmallString(bytes32 s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let n := 0
for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
mstore(result, n)
let o := add(result, 0x20)
mstore(o, s)
mstore(add(o, n), 0)
mstore(0x40, add(result, 0x40))
}
}
/// @dev Returns the small string, with all bytes after the first null byte zeroized.
function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
mstore(0x00, s)
mstore(result, 0x00)
result := mload(0x00)
}
}
/// @dev Returns the string as a normalized null-terminated small string.
function toSmallString(string memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(s)
if iszero(lt(result, 33)) {
mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
revert(0x1c, 0x04)
}
result := shl(shl(3, sub(32, result)), mload(add(s, result)))
}
}
/// @dev Returns a lowercased copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function lower(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, false);
}
/// @dev Returns an UPPERCASED copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function upper(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, true);
}
/// @dev Escapes the string to be used within HTML tags.
function escapeHTML(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
// Store the bytes of the packed offsets and strides into the scratch space.
// `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
mstore(0x1f, 0x900094)
mstore(0x08, 0xc0000000a6ab)
// Store ""&'<>" into the scratch space.
mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// Not in `["\"","'","&","<",">"]`.
if iszero(and(shl(c, 1), 0x500000c400000000)) {
mstore8(result, c)
result := add(result, 1)
continue
}
let t := shr(248, mload(c))
mstore(result, mload(and(t, 0x1f)))
result := add(result, shr(5, t))
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
/// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
function escapeJSON(string memory s, bool addDoubleQuotes)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
// Store "\\u0000" in scratch space.
// Store "0123456789abcdef" in scratch space.
// Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
// into the scratch space.
mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
// Bitmask for detecting `["\"","\\"]`.
let e := or(shl(0x22, 1), shl(0x5c, 1))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(lt(c, 0x20)) {
if iszero(and(shl(c, 1), e)) {
// Not in `["\"","\\"]`.
mstore8(result, c)
result := add(result, 1)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), c)
result := add(result, 2)
continue
}
if iszero(and(shl(c, 1), 0x3700)) {
// Not in `["\b","\t","\n","\f","\d"]`.
mstore8(0x1d, mload(shr(4, c))) // Hex value.
mstore8(0x1e, mload(and(c, 15))) // Hex value.
mstore(result, mload(0x19)) // "\\u00XX".
result := add(result, 6)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), mload(add(c, 8)))
result := add(result, 2)
}
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
function escapeJSON(string memory s) internal pure returns (string memory result) {
result = escapeJSON(s, false);
}
/// @dev Returns whether `a` equals `b`.
function eq(string memory a, string memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Packs a single string with its length into a single word.
/// Returns `bytes32(0)` if the length is zero or greater than 31.
function packOne(string memory a) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
// We don't need to zero right pad the string,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes.
mload(add(a, 0x1f)),
// `length != 0 && length < 32`. Abuses underflow.
// Assumes that the length is valid and within the block gas limit.
lt(sub(mload(a), 1), 0x1f)
)
}
}
/// @dev Unpacks a string packed using {packOne}.
/// Returns the empty string if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packOne}, the output behavior is undefined.
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
result := mload(0x40)
// Allocate 2 words (1 for the length, 1 for the bytes).
mstore(0x40, add(result, 0x40))
// Zeroize the length slot.
mstore(result, 0)
// Store the length and bytes.
mstore(add(result, 0x1f), packed)
// Right pad with zeroes.
mstore(add(add(result, 0x20), mload(result)), 0)
}
}
/// @dev Packs two strings with their lengths into a single word.
/// Returns `bytes32(0)` if combined length is zero or greater than 30.
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let aLength := mload(a)
// We don't need to zero right pad the strings,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes of `a` and `b`.
or(
shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
mload(sub(add(b, 0x1e), aLength))
),
// `totalLength != 0 && totalLength < 31`. Abuses underflow.
// Assumes that the lengths are valid and within the block gas limit.
lt(sub(add(aLength, mload(b)), 1), 0x1e)
)
}
}
/// @dev Unpacks strings packed using {packTwo}.
/// Returns the empty strings if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packTwo}, the output behavior is undefined.
function unpackTwo(bytes32 packed)
internal
pure
returns (string memory resultA, string memory resultB)
{
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
resultA := mload(0x40)
resultB := add(resultA, 0x40)
// Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
mstore(0x40, add(resultB, 0x40))
// Zeroize the length slots.
mstore(resultA, 0)
mstore(resultB, 0)
// Store the lengths and bytes.
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
// Right pad with zeroes.
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
/// @dev Directly returns `a` without copying.
function directReturn(string memory a) internal pure {
assembly {
// Assumes that the string does not start from the scratch space.
let retStart := sub(a, 0x20)
let retSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the string is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retSize), 0)
// Store the return offset.
mstore(retStart, 0x20)
// End the transaction, returning the string.
return(retStart, retSize)
}
}
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"FreshCryptoLib/=lib/webauthn-sol/lib/FreshCryptoLib/solidity/src/",
"account-abstraction/=lib/account-abstraction/contracts/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"p256-verifier/=lib/p256-verifier/",
"safe-singleton-deployer-sol/=lib/safe-singleton-deployer-sol/",
"solady/=lib/solady/src/",
"webauthn-sol/=lib/webauthn-sol/src/"
],
"optimizer": {
"enabled": true,
"runs": 9999999
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes","name":"owner","type":"bytes"}],"name":"AlreadyOwner","type":"error"},{"inputs":[],"name":"Initialized","type":"error"},{"inputs":[{"internalType":"bytes","name":"owner","type":"bytes"}],"name":"InvalidEthereumAddressOwner","type":"error"},{"inputs":[{"internalType":"uint256","name":"key","type":"uint256"}],"name":"InvalidNonceKey","type":"error"},{"inputs":[{"internalType":"bytes","name":"owner","type":"bytes"}],"name":"InvalidOwnerBytesLength","type":"error"},{"inputs":[],"name":"LastOwner","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"NoOwnerAtIndex","type":"error"},{"inputs":[{"internalType":"uint256","name":"ownersRemaining","type":"uint256"}],"name":"NotLastOwner","type":"error"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"SelectorNotAllowed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnauthorizedCallContext","type":"error"},{"inputs":[],"name":"UpgradeFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes","name":"expectedOwner","type":"bytes"},{"internalType":"bytes","name":"actualOwner","type":"bytes"}],"name":"WrongOwnerAtIndex","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"owner","type":"bytes"}],"name":"AddOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"owner","type":"bytes"}],"name":"RemoveOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"REPLAYABLE_NONCE_KEY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"addOwnerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"x","type":"bytes32"},{"internalType":"bytes32","name":"y","type":"bytes32"}],"name":"addOwnerPublicKey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"functionSelector","type":"bytes4"}],"name":"canSkipChainIdValidation","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"entryPoint","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct CoinbaseSmartWallet.Call[]","name":"calls","type":"tuple[]"}],"name":"executeBatch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"calls","type":"bytes[]"}],"name":"executeWithoutChainIdValidation","outputs":[],"stateMutability":"payable","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":"uint256","name":"callGasLimit","type":"uint256"},{"internalType":"uint256","name":"verificationGasLimit","type":"uint256"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"uint256","name":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct UserOperation","name":"userOp","type":"tuple"}],"name":"getUserOpHashWithoutChainId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"$","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"owners","type":"bytes[]"}],"name":"initialize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isOwnerAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"account","type":"bytes"}],"name":"isOwnerBytes","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"x","type":"bytes32"},{"internalType":"bytes32","name":"y","type":"bytes32"}],"name":"isOwnerPublicKey","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"result","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextOwnerIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"ownerAtIndex","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownerCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes","name":"owner","type":"bytes"}],"name":"removeLastOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes","name":"owner","type":"bytes"}],"name":"removeOwnerAtIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"removedOwnersCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"replaySafeHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","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":"uint256","name":"callGasLimit","type":"uint256"},{"internalType":"uint256","name":"verificationGasLimit","type":"uint256"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"uint256","name":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct UserOperation","name":"userOp","type":"tuple"},{"internalType":"bytes32","name":"userOpHash","type":"bytes32"},{"internalType":"uint256","name":"missingAccountFunds","type":"uint256"}],"name":"validateUserOp","outputs":[{"internalType":"uint256","name":"validationData","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60a0604052306080523480156200001557600080fd5b50604080516001808252818301909252600091816020015b60608152602001906001900390816200002d57905050604080516000602082015291925001604051602081830303815290604052816000815181106200007757620000776200036b565b60209081029190910101526200008d8162000094565b50620005a1565b60008051602062004c16833981519152805460005b83518110156200022357838181518110620000c857620000c86200036b565b602002602001015151602014158015620001005750838181518110620000f257620000f26200036b565b602002602001015151604014155b1562000148578381815181106200011b576200011b6200036b565b60200260200101516040516327755b9160e11b81526004016200013f9190620003a7565b60405180910390fd5b8381815181106200015d576200015d6200036b565b6020026020010151516020148015620001a557506001600160a01b0380168482815181106200019057620001906200036b565b6020026020010151620001a390620003dc565b115b15620001e457838181518110620001c057620001c06200036b565b602002602001015160405163bff1ac6560e01b81526004016200013f9190620003a7565b6200021a848281518110620001fd57620001fd6200036b565b60200260200101518380620002129062000404565b945062000229565b600101620000a9565b50905550565b620002348262000317565b1562000257578160405163468b12ad60e11b81526004016200013f9190620003a7565b60405160008051602062004c16833981519152906001907f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f03906200029d9086906200042c565b9081526040805160209281900383019020805460ff19169315159390931790925560008481526002840190915220620002d78482620004d5565b50817f38109edc26e166b5579352ce56a50813177eb25208fd90d61f2f378386220220846040516200030a9190620003a7565b60405180910390a2505050565b600060008051602062004c16833981519152600301826040516200033c91906200042c565b9081526040519081900360200190205460ff1692915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60005b838110156200039e57818101518382015260200162000384565b50506000910152565b6020815260008251806020840152620003c881604085016020870162000381565b601f01601f19169190910160400192915050565b80516020808301519190811015620003fe576000198160200360031b1b821691505b50919050565b6000600182016200042557634e487b7160e01b600052601160045260246000fd5b5060010190565b600082516200044081846020870162000381565b9190910192915050565b600181811c908216806200045f57607f821691505b602082108103620003fe57634e487b7160e01b600052602260045260246000fd5b601f821115620004d0576000816000526020600020601f850160051c81016020861015620004ab5750805b601f850160051c820191505b81811015620004cc57828155600101620004b7565b5050505b505050565b81516001600160401b03811115620004f157620004f162000355565b62000509816200050284546200044a565b8462000480565b602080601f831160018114620005415760008415620005285750858301515b600019600386901b1c1916600185901b178555620004cc565b600085815260208120601f198616915b82811015620005725788860151825594840194600190910190840162000551565b5085821015620005915787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b608051614652620005c460003960008181610bbe0152610cf501526146526000f3fe6080604052600436106101b05760003560e01c80635c60da1b116100ec578063a2e1a8d81161008a578063b819736711610064578063b81973671461051d578063ce1506be1461053d578063d948fd2e1461055d578063f698da2514610591576101b7565b8063a2e1a8d8146104c3578063b0d691fe146104e3578063b61d27f61461050a576101b7565b806388ce4c7c116100c657806388ce4c7c1461044057806389625b57146104565780638ea69029146104765780639f9bcb34146104a3576101b7565b80635c60da1b146103b05780636f2de70e1461040557806384b0196e14610418576101b7565b80632c2abd1e116101595780633a871cdd116101335780633a871cdd146103485780634f1ef286146103685780634f6e7f221461037b57806352d1902d1461039b576101b7565b80632c2abd1e146102ee57806334fcd5be1461030157806336d9cf9b14610314576101b7565b80631626ba7e1161018a5780631626ba7e1461025d5780631ca5393f146102ae57806329565e3b146102ce576101b7565b8063066a1eb7146101e55780630db026221461021a5780630f0f3f241461023d576101b7565b366101b757005b60003560e01c63bc197c81811463f23a6e6182141763150b7a02821417156101e357806020526020603cf35b005b3480156101f157600080fd5b5061020561020036600461388d565b6105a6565b60405190151581526020015b60405180910390f35b34801561022657600080fd5b5061022f610633565b604051908152602001610211565b34801561024957600080fd5b506101e36102583660046138d8565b61068a565b34801561026957600080fd5b5061027d61027836600461393c565b610708565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610211565b3480156102ba57600080fd5b506102056102c9366004613acb565b610773565b3480156102da57600080fd5b506101e36102e936600461388d565b6107c0565b6101e36102fc366004613b45565b6107e9565b6101e361030f366004613b45565b610935565b34801561032057600080fd5b507f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f015461022f565b34801561035457600080fd5b5061022f610363366004613ba0565b610a34565b6101e3610376366004613bee565b610bbc565b34801561038757600080fd5b5061022f610396366004613c28565b610ca0565b3480156103a757600080fd5b5061022f610cf1565b3480156103bc57600080fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610211565b6101e3610413366004613b45565b610d51565b34801561042457600080fd5b5061042d610dbc565b6040516102119796959493929190613ccb565b34801561044c57600080fd5b5061022f61210581565b34801561046257600080fd5b506101e361047136600461393c565b610e65565b34801561048257600080fd5b50610496610491366004613d8d565b610eb9565b6040516102119190613da6565b3480156104af57600080fd5b506102056104be366004613db9565b610f7a565b3480156104cf57600080fd5b506102056104de3660046138d8565b611106565b3480156104ef57600080fd5b50735ff137d4b0fdcd49dca30c7cf57e578a026d27896103e0565b6101e3610518366004613dfb565b611189565b34801561052957600080fd5b506101e361053836600461393c565b6111ed565b34801561054957600080fd5b5061022f610558366004613d8d565b61124a565b34801561056957600080fd5b507f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f005461022f565b34801561059d57600080fd5b5061022f611255565b60408051602081018490529081018290526000907f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f0390606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905261061791613e55565b9081526040519081900360200190205460ff1690505b92915050565b7f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f01547f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00805460009261068491613ea0565b91505090565b610692611341565b6040805173ffffffffffffffffffffffffffffffffffffffff8316602082015261070591015b6040516020818303038152906040526106ee7f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f0090565b80549060006106fc83613eb3565b9190505561138d565b50565b600061071d6107168561124a565b84846114b8565b1561074957507f1626ba7e0000000000000000000000000000000000000000000000000000000061076c565b507fffffffff000000000000000000000000000000000000000000000000000000005b9392505050565b60007f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00600301826040516107a79190613e55565b9081526040519081900360200190205460ff1692915050565b6107c8611341565b60408051602081018490529081018290526107e5906060016106b8565b5050565b33735ff137d4b0fdcd49dca30c7cf57e578a026d278914610836576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156109305736600084848481811061085657610856613eeb565b90506020028101906108689190613f1a565b909250905060006108798284613f7f565b905061088481610f7a565b6108e3576040517f3b06e1460000000000000000000000000000000000000000000000000000000081527fffffffff00000000000000000000000000000000000000000000000000000000821660048201526024015b60405180910390fd5b61092530600085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061160c92505050565b505050600101610839565b505050565b33735ff137d4b0fdcd49dca30c7cf57e578a026d27891461095857610958611341565b60005b8181101561093057610a2c83838381811061097857610978613eeb565b905060200281019061098a9190613fc7565b6109989060208101906138d8565b8484848181106109aa576109aa613eeb565b90506020028101906109bc9190613fc7565b602001358585858181106109d2576109d2613eeb565b90506020028101906109e49190613fc7565b6109f2906040810190613f1a565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061160c92505050565b60010161095b565b600033735ff137d4b0fdcd49dca30c7cf57e578a026d278914610a83576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81602085013560401c7f2c2abd1e00000000000000000000000000000000000000000000000000000000610aba6060880188613f1a565b610ac391613f7f565b7fffffffff000000000000000000000000000000000000000000000000000000001603610b3857610af386610ca0565b94506121058114610b33576040517f2ef37813000000000000000000000000000000000000000000000000000000008152600481018290526024016108da565b610b76565b6121058103610b76576040517f2ef37813000000000000000000000000000000000000000000000000000000008152600481018290526024016108da565b610b8d85610b88610140890189613f1a565b6114b8565b15610b9c576000925050610ba2565b60019250505b8015610bb45760003860003884335af1505b509392505050565b7f0000000000000000000000000000000000000000000000000000000000000000308103610bf257639f03a0266000526004601cfd5b610bfb84611689565b8360601b60601c93506352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80602060016004601d895afa5114610c4d576355299b496001526004601dfd5b847fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b600038a28490558115610c9a57604051828482376000388483885af4610c98573d6000823e3d81fd5b505b50505050565b6000610cab82611691565b604080516020810192909252735ff137d4b0fdcd49dca30c7cf57e578a026d2789908201526060015b604051602081830303815290604052805190602001209050919050565b60007f0000000000000000000000000000000000000000000000000000000000000000308114610d2957639f03a0266000526004601cfd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc91505b5090565b7f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f005415610daa576040517f5daa87a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107e5610db78284613ffb565b6116aa565b7f0f000000000000000000000000000000000000000000000000000000000000006060806000808083610e53604080518082018252601581527f436f696e6261736520536d6172742057616c6c657400000000000000000000006020808301919091528251808401909352600183527f31000000000000000000000000000000000000000000000000000000000000009083015291565b97989097965046955030945091925090565b610e6d611341565b610e75610633565b600103610eae576040517f948bf89700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610930838383611861565b60008181527f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f0260205260409020805460609190610ef590614081565b80601f0160208091040260200160405190810160405280929190818152602001828054610f2190614081565b8015610f6e5780601f10610f4357610100808354040283529160200191610f6e565b820191906000526020600020905b815481529060010190602001808311610f5157829003601f168201915b50505050509050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f29565e3b00000000000000000000000000000000000000000000000000000000148061100d57507fffffffff0000000000000000000000000000000000000000000000000000000082167f0f0f3f2400000000000000000000000000000000000000000000000000000000145b8061105957507fffffffff0000000000000000000000000000000000000000000000000000000082167f89625b5700000000000000000000000000000000000000000000000000000000145b806110a557507fffffffff0000000000000000000000000000000000000000000000000000000082167fb819736700000000000000000000000000000000000000000000000000000000145b806110f157507fffffffff0000000000000000000000000000000000000000000000000000000082167f4f1ef28600000000000000000000000000000000000000000000000000000000145b156110fe57506001919050565b506000919050565b60007f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f006040805173ffffffffffffffffffffffffffffffffffffffff851660208201526003929092019101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526107a791613e55565b33735ff137d4b0fdcd49dca30c7cf57e578a026d2789146111ac576111ac611341565b610c9a848484848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061160c92505050565b6111f5611341565b60006111ff610633565b9050600181111561123f576040517f823f1aa8000000000000000000000000000000000000000000000000000000008152600481018290526024016108da565b610c9a848484611861565b600061062d82611a05565b60008060006112c8604080518082018252601581527f436f696e6261736520536d6172742057616c6c657400000000000000000000006020808301919091528251808401909352600183527f31000000000000000000000000000000000000000000000000000000000000009083015291565b8151602080840191909120825182840120604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f9481019490945283019190915260608201524660808201523060a0820152919350915060c001604051602081830303815290604052805190602001209250505090565b61134a33611106565b8061135457503330145b1561135b57565b6040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61139682610773565b156113cf57816040517f8d16255a0000000000000000000000000000000000000000000000000000000081526004016108da9190613da6565b6040517f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00906001907f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f0390611424908690613e55565b908152604080516020928190038301902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016931515939093179092556000848152600284019091522061147a848261411e565b50817f38109edc26e166b5579352ce56a50813177eb25208fd90d61f2f378386220220846040516114ab9190613da6565b60405180910390a2505050565b6000806114c783850185614238565b905060006114d88260000151610eb9565b9050805160200361155d5773ffffffffffffffffffffffffffffffffffffffff611501826142c5565b111561153b57806040517fbff1ac650000000000000000000000000000000000000000000000000000000081526004016108da9190613da6565b60006020820151905061155381888560200151611a56565b935050505061076c565b80516040036115d8576000808280602001905181019061157d9190614307565b915091506000846020015180602001905181019061159b9190614370565b90506115cc896040516020016115b391815260200190565b6040516020818303038152906040526000838686611b68565b9550505050505061076c565b806040517f4eeab7220000000000000000000000000000000000000000000000000000000081526004016108da9190613da6565b6000808473ffffffffffffffffffffffffffffffffffffffff1684846040516116359190613e55565b60006040518083038185875af1925050503d8060008114611672576040519150601f19603f3d011682016040523d82523d6000602084013e611677565b606091505b509150915081610c9857805160208201fd5b610705611341565b600061169c82611f72565b805190602001209050919050565b7f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00805460005b835181101561185b578381815181106116eb576116eb613eeb565b60200260200101515160201415801561171f575083818151811061171157611711613eeb565b602002602001015151604014155b156117715783818151811061173657611736613eeb565b60200260200101516040517f4eeab7220000000000000000000000000000000000000000000000000000000081526004016108da9190613da6565b83818151811061178357611783613eeb565b60200260200101515160201480156117d2575073ffffffffffffffffffffffffffffffffffffffff80168482815181106117bf576117bf613eeb565b60200260200101516117d0906142c5565b115b15611824578381815181106117e9576117e9613eeb565b60200260200101516040517fbff1ac650000000000000000000000000000000000000000000000000000000081526004016108da9190613da6565b61185384828151811061183957611839613eeb565b6020026020010151838061184c90613eb3565b945061138d565b6001016116d0565b50905550565b600061186c84610eb9565b905080516000036118ac576040517f68188e7a000000000000000000000000000000000000000000000000000000008152600481018590526024016108da565b82826040516118bc92919061442c565b604051809103902081805190602001201461190b57838383836040517f781f2e390000000000000000000000000000000000000000000000000000000081526004016108da9493929190614485565b6040517f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00907f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f039061195f908690869061442c565b908152604080516020928190038301902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905560008781526002840190925281206119ad91613843565b6001810180549060006119bf83613eb3565b9190505550847fcf95bbfe6f870f8cc40482dc3dccdafd268f0e9ce0a4f24ea1bea9be64e505ff85856040516119f69291906144bc565b60405180910390a25050505050565b6000611a0f611255565b611a1883612052565b6040517f1901000000000000000000000000000000000000000000000000000000000000602082015260228101929092526042820152606201610cd4565b73ffffffffffffffffffffffffffffffffffffffff909216916000831561076c576040518360005260208301516040526040835103611ad3576040830151601b8160ff1c016020528060011b60011c60605250602060016080600060015afa805186183d1517611ad15750600060605260405250600161076c565b505b6041835103611b1957606083015160001a6020526040830151606052602060016080600060015afa805186183d1517611b175750600060605260405250600161076c565b505b600060605280604052631626ba7e60e01b808252846004830152602482016040815284516020018060448501828860045afa505060208160443d01858a5afa9051909114169150509392505050565b6000611b9560027fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325516144d0565b8460a001511115611ba857506000611f69565b6060840151600090611bcb90611bbf81601561450b565b6020880151919061208d565b90507fff1a2a9176d650e4a99dedb58f1793003935130579fe17b5a3f698ac5b00e634818051906020012014611c05576000915050611f69565b6000611c1088612111565b604051602001611c20919061451e565b60405160208183030381529060405290506000611c58876040015183518960400151611c4c919061450b565b60208a0151919061208d565b90508180519060200120818051906020012014611c7b5760009350505050611f69565b865180517f01000000000000000000000000000000000000000000000000000000000000009182916020908110611cb457611cb4613eeb565b0160200151167fff000000000000000000000000000000000000000000000000000000000000001614611ced5760009350505050611f69565b878015611d595750865180517f04000000000000000000000000000000000000000000000000000000000000009182916020908110611d2e57611d2e613eeb565b0160200151167fff000000000000000000000000000000000000000000000000000000000000001614155b15611d6a5760009350505050611f69565b600060028860200151604051611d809190613e55565b602060405180830381855afa158015611d9d573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190611dc0919061458a565b905060006002896000015183604051602001611ddd9291906145a3565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052611e1591613e55565b602060405180830381855afa158015611e32573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190611e55919061458a565b6080808b015160a0808d015160408051602081018790529081019390935260608301529181018b905290810189905290915060009060c001604051602081830303815290604052905060008061010073ffffffffffffffffffffffffffffffffffffffff1683604051611ec89190613e55565b600060405180830381855afa9150503d8060008114611f03576040519150601f19603f3d011682016040523d82523d6000602084013e611f08565b606091505b50805191935091501515828015611f1c5750805b15611f485781806020019051810190611f35919061458a565b6001149950505050505050505050611f69565b611f5d858e608001518f60a001518f8f612137565b99505050505050505050505b95945050505050565b6060813560208301356000611f92611f8d6040870187613f1a565b612286565b90506000611fa6611f8d6060880188613f1a565b9050608086013560a087013560c088013560e08901356101008a01356000611fd5611f8d6101208e018e613f1a565b6040805173ffffffffffffffffffffffffffffffffffffffff9c909c1660208d01528b81019a909a5260608b019890985250608089019590955260a088019390935260c087019190915260e08601526101008501526101208401526101408084019190915281518084039091018152610160909201905292915050565b604080517f9b493d222105fee7df163ab5d57f0bf1ffd2da04dd5fafbe10b54c41c1adc6576020820152908101829052600090606001610cd4565b6060835182811161209c578092505b8381116120a7578093505b508183101561076c5750604051828203808252938301937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f820181165b86810151848201528101806120e65750600083830160200152603f9091011681016040529392505050565b606061062d826040518060600160405280604081526020016145dd604091396000612299565b600084158061216657507fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325518510155b8061216f575083155b8061219a57507fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325518410155b156121a757506000611f69565b6121b18383612419565b6121bd57506000611f69565b60006121c885612592565b905060007fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551828909905060007fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325518389099050600061222887878585612616565b90507fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325516122758a7fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551613ea0565b8208159a9950505050505050505050565b6000604051828085833790209392505050565b606083516000036122b9575060408051602081019091526000815261076c565b6000826122ea576003855160046122d091906145c5565b6122db90600261450b565b6122e591906144d0565b61230f565b6003855160026122fa919061450b565b61230491906144d0565b61230f9060046145c5565b905060008167ffffffffffffffff81111561232c5761232c613988565b6040519080825280601f01601f191660200182016040528015612356576020820181803683370190505b50905060018501602082018788518901602081018051600082525b828410156123cc576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450612371565b90525050851561240d576003885106600181146123f057600281146124035761240b565b603d6001830353603d600283035361240b565b603d60018303535b505b50909695505050505050565b60007fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8310158061246a57507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8210155b8061247c57508215801561247c575081155b156124895750600061062d565b60007fffffffff00000001000000000000000000000000ffffffffffffffffffffffff838409905060007fffffffff00000001000000000000000000000000ffffffffffffffffffffffff807fffffffff00000001000000000000000000000000fffffffffffffffffffffffc87097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff877fffffffff00000001000000000000000000000000ffffffffffffffffffffffff898a09090890507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff7f5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b820891909114949350505050565b600060405160208152602080820152602060408201528260608201527fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254f60808201527fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63255160a082015260208160c0836005600019fa61260f57600080fd5b5192915050565b600080808060ff81808815801561262b575087155b1561263f576000965050505050505061312c565b61268b7f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2967f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f58d8d613134565b90925090508115801561269c575080155b1561270d577fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551887fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551038a08985060009750881580156126f9575087155b1561270d576000965050505050505061312c565b600189841c16600189851c1660011b015b806127405760018403935060018a851c1660018a861c1660011b01905061271e565b50600189841c16600189851c1660011b019550600186036127a2577f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c29696507f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f593505b600286036127b1578a96508993505b600386036127c0578196508093505b60018303925060019550600194505b827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1115613082577fffffffff00000001000000000000000000000000ffffffffffffffffffffffff846002097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8182097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff818a097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff82840992507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff807fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8b8d087fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8c7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038e08096003097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff89850998507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8a840999507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80837fffffffff00000001000000000000000000000000fffffffffffffffffffffffd097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff838409089a507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80837fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038d08820992507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff837fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8a870908975060018d881c1660018d891c1660011b01905080612aab57877fffffffff00000001000000000000000000000000ffffffffffffffffffffffff03975050505050613077565b60018103612afa577f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c29693507f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f592505b60028103612b09578e93508d92505b60038103612b18578593508492505b89612b3157509198506001975087965094506130779050565b7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff887fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8b8609087fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8c7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff037fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8d880908935080612e705783612e70577fffffffff00000001000000000000000000000000ffffffffffffffffffffffff897fffffffff00000001000000000000000000000000fffffffffffffffffffffffd0994507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff85860993507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff848d0992507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff84860994507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff808c7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038e087fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8d8f080990507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8160030991507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8a860999507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8b85099a507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80847fffffffff00000001000000000000000000000000fffffffffffffffffffffffd097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff848509089b507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff808d7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038508830993507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff808a8709850898505050505050613077565b7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff84850991507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8483097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff838d099b507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff818c099a507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff838e097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80827fffffffff00000001000000000000000000000000fffffffffffffffffffffffd097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff847fffffffff00000001000000000000000000000000ffffffffffffffffffffffff037fffffffff00000001000000000000000000000000ffffffffffffffffffffffff878809080893507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80838d097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff857fffffffff00000001000000000000000000000000ffffffffffffffffffffffff887fffffffff00000001000000000000000000000000ffffffffffffffffffffffff03860809089a50505050809a50505050505b6001830392506127cf565b60405186606082015260208152602080820152602060408201527fffffffff00000001000000000000000000000000fffffffffffffffffffffffd60808201527fffffffff00000001000000000000000000000000ffffffffffffffffffffffff60a082015260208160c0836005600019fa6130fd57600080fd5b7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff815189099750505050505050505b949350505050565b60008080808661314b5785859350935050506131b9565b8461315d5787879350935050506131b9565b858814801561316b57508487145b1561318c5761317d88886001806131c2565b929a50909850925090506131a6565b61319b88886001808a8a61344c565b929a50909850925090505b6131b288888484613711565b9350935050505b94509492505050565b6000806000807fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8760020993507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff84850991507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff82890990507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff82850992507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff86830991507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff807fffffffff00000001000000000000000000000000ffffffffffffffffffffffff888b087fffffffff00000001000000000000000000000000ffffffffffffffffffffffff897fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038c080960030995507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80827fffffffff00000001000000000000000000000000fffffffffffffffffffffffd097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8889090893507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80857fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038308870997507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff85840990507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff808885097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff0389089250945094509450949050565b6000806000808860000361346b57508492508391506001905080613704565b7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff9889039889818988090894507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8a7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff037fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8a89090895507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff86870993507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff86850992507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff84890991507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff83880990507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff848b0997507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80897fffffffff00000001000000000000000000000000fffffffffffffffffffffffd097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff857fffffffff00000001000000000000000000000000ffffffffffffffffffffffff037fffffffff00000001000000000000000000000000ffffffffffffffffffffffff898a09080893507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80848b097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff877fffffffff00000001000000000000000000000000ffffffffffffffffffffffff887fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038d08090892505b9650965096509692505050565b600080600061371f846137c6565b90507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff818709915060007fffffffff00000001000000000000000000000000ffffffffffffffffffffffff82870990507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff81820991507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8289099350505094509492505050565b600060405160208152602080820152602060408201528260608201527fffffffff00000001000000000000000000000000fffffffffffffffffffffffd60808201527fffffffff00000001000000000000000000000000ffffffffffffffffffffffff60a082015260208160c0836005600019fa61260f57600080fd5b50805461384f90614081565b6000825580601f1061385f575050565b601f01602090049060005260206000209081019061070591905b80821115610d4d5760008155600101613879565b600080604083850312156138a057600080fd5b50508035926020909101359150565b803573ffffffffffffffffffffffffffffffffffffffff811681146138d357600080fd5b919050565b6000602082840312156138ea57600080fd5b61076c826138af565b60008083601f84011261390557600080fd5b50813567ffffffffffffffff81111561391d57600080fd5b60208301915083602082850101111561393557600080fd5b9250929050565b60008060006040848603121561395157600080fd5b83359250602084013567ffffffffffffffff81111561396f57600080fd5b61397b868287016138f3565b9497909650939450505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160c0810167ffffffffffffffff811182821017156139da576139da613988565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613a2757613a27613988565b604052919050565b600067ffffffffffffffff821115613a4957613a49613988565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112613a8657600080fd5b8135613a99613a9482613a2f565b6139e0565b818152846020838601011115613aae57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613add57600080fd5b813567ffffffffffffffff811115613af457600080fd5b61312c84828501613a75565b60008083601f840112613b1257600080fd5b50813567ffffffffffffffff811115613b2a57600080fd5b6020830191508360208260051b850101111561393557600080fd5b60008060208385031215613b5857600080fd5b823567ffffffffffffffff811115613b6f57600080fd5b613b7b85828601613b00565b90969095509350505050565b60006101608284031215613b9a57600080fd5b50919050565b600080600060608486031215613bb557600080fd5b833567ffffffffffffffff811115613bcc57600080fd5b613bd886828701613b87565b9660208601359650604090950135949350505050565b600080600060408486031215613c0357600080fd5b613c0c846138af565b9250602084013567ffffffffffffffff81111561396f57600080fd5b600060208284031215613c3a57600080fd5b813567ffffffffffffffff811115613c5157600080fd5b61312c84828501613b87565b60005b83811015613c78578181015183820152602001613c60565b50506000910152565b60008151808452613c99816020860160208601613c5d565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e06020840152613d0860e084018a613c81565b8381036040850152613d1a818a613c81565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c08601528551808252602080880193509091019060005b81811015613d7b57835183529284019291840191600101613d5f565b50909c9b505050505050505050505050565b600060208284031215613d9f57600080fd5b5035919050565b60208152600061076c6020830184613c81565b600060208284031215613dcb57600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461076c57600080fd5b60008060008060608587031215613e1157600080fd5b613e1a856138af565b935060208501359250604085013567ffffffffffffffff811115613e3d57600080fd5b613e49878288016138f3565b95989497509550505050565b60008251613e67818460208701613c5d565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561062d5761062d613e71565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613ee457613ee4613e71565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613f4f57600080fd5b83018035915067ffffffffffffffff821115613f6a57600080fd5b60200191503681900382131561393557600080fd5b7fffffffff000000000000000000000000000000000000000000000000000000008135818116916004851015613fbf5780818660040360031b1b83161692505b505092915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112613e6757600080fd5b600067ffffffffffffffff8084111561401657614016613988565b8360051b6020614028602083016139e0565b8681529185019160208101903684111561404157600080fd5b865b848110156140755780358681111561405b5760008081fd5b61406736828b01613a75565b845250918301918301614043565b50979650505050505050565b600181811c9082168061409557607f821691505b602082108103613b9a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b601f821115610930576000816000526020600020601f850160051c810160208610156140f75750805b601f850160051c820191505b8181101561411657828155600101614103565b505050505050565b815167ffffffffffffffff81111561413857614138613988565b61414c816141468454614081565b846140ce565b602080601f83116001811461419f57600084156141695750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555614116565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156141ec578886015182559484019460019091019084016141cd565b508582101561422857878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561424a57600080fd5b813567ffffffffffffffff8082111561426257600080fd5b908301906040828603121561427657600080fd5b60405160408101818110838211171561429157614291613988565b604052823581526020830135828111156142aa57600080fd5b6142b687828601613a75565b60208301525095945050505050565b80516020808301519190811015613b9a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60209190910360031b1b16919050565b6000806040838503121561431a57600080fd5b505080516020909101519092909150565b600082601f83011261433c57600080fd5b815161434a613a9482613a2f565b81815284602083860101111561435f57600080fd5b61312c826020830160208701613c5d565b60006020828403121561438257600080fd5b815167ffffffffffffffff8082111561439a57600080fd5b9083019060c082860312156143ae57600080fd5b6143b66139b7565b8251828111156143c557600080fd5b6143d18782860161432b565b8252506020830151828111156143e657600080fd5b6143f28782860161432b565b60208301525060408301516040820152606083015160608201526080830151608082015260a083015160a082015280935050505092915050565b8183823760009101908152919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b84815260606020820152600061449f60608301858761443c565b82810360408401526144b18185613c81565b979650505050505050565b60208152600061312c60208301848661443c565b600082614506577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b8082018082111561062d5761062d613e71565b7f226368616c6c656e6765223a220000000000000000000000000000000000000081526000825161455681600d850160208701613c5d565b7f2200000000000000000000000000000000000000000000000000000000000000600d939091019283015250600e01919050565b60006020828403121561459c57600080fd5b5051919050565b600083516145b5818460208801613c5d565b9190910191825250602001919050565b808202811582820484141761062d5761062d613e7156fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5fa26469706673582212208c2ea73863e242a1e00858d0d525af430fe3045920a71b632ca3e728ddc8817d64736f6c6343000817003397e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00
Deployed Bytecode
0x6080604052600436106101b05760003560e01c80635c60da1b116100ec578063a2e1a8d81161008a578063b819736711610064578063b81973671461051d578063ce1506be1461053d578063d948fd2e1461055d578063f698da2514610591576101b7565b8063a2e1a8d8146104c3578063b0d691fe146104e3578063b61d27f61461050a576101b7565b806388ce4c7c116100c657806388ce4c7c1461044057806389625b57146104565780638ea69029146104765780639f9bcb34146104a3576101b7565b80635c60da1b146103b05780636f2de70e1461040557806384b0196e14610418576101b7565b80632c2abd1e116101595780633a871cdd116101335780633a871cdd146103485780634f1ef286146103685780634f6e7f221461037b57806352d1902d1461039b576101b7565b80632c2abd1e146102ee57806334fcd5be1461030157806336d9cf9b14610314576101b7565b80631626ba7e1161018a5780631626ba7e1461025d5780631ca5393f146102ae57806329565e3b146102ce576101b7565b8063066a1eb7146101e55780630db026221461021a5780630f0f3f241461023d576101b7565b366101b757005b60003560e01c63bc197c81811463f23a6e6182141763150b7a02821417156101e357806020526020603cf35b005b3480156101f157600080fd5b5061020561020036600461388d565b6105a6565b60405190151581526020015b60405180910390f35b34801561022657600080fd5b5061022f610633565b604051908152602001610211565b34801561024957600080fd5b506101e36102583660046138d8565b61068a565b34801561026957600080fd5b5061027d61027836600461393c565b610708565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610211565b3480156102ba57600080fd5b506102056102c9366004613acb565b610773565b3480156102da57600080fd5b506101e36102e936600461388d565b6107c0565b6101e36102fc366004613b45565b6107e9565b6101e361030f366004613b45565b610935565b34801561032057600080fd5b507f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f015461022f565b34801561035457600080fd5b5061022f610363366004613ba0565b610a34565b6101e3610376366004613bee565b610bbc565b34801561038757600080fd5b5061022f610396366004613c28565b610ca0565b3480156103a757600080fd5b5061022f610cf1565b3480156103bc57600080fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610211565b6101e3610413366004613b45565b610d51565b34801561042457600080fd5b5061042d610dbc565b6040516102119796959493929190613ccb565b34801561044c57600080fd5b5061022f61210581565b34801561046257600080fd5b506101e361047136600461393c565b610e65565b34801561048257600080fd5b50610496610491366004613d8d565b610eb9565b6040516102119190613da6565b3480156104af57600080fd5b506102056104be366004613db9565b610f7a565b3480156104cf57600080fd5b506102056104de3660046138d8565b611106565b3480156104ef57600080fd5b50735ff137d4b0fdcd49dca30c7cf57e578a026d27896103e0565b6101e3610518366004613dfb565b611189565b34801561052957600080fd5b506101e361053836600461393c565b6111ed565b34801561054957600080fd5b5061022f610558366004613d8d565b61124a565b34801561056957600080fd5b507f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f005461022f565b34801561059d57600080fd5b5061022f611255565b60408051602081018490529081018290526000907f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f0390606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905261061791613e55565b9081526040519081900360200190205460ff1690505b92915050565b7f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f01547f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00805460009261068491613ea0565b91505090565b610692611341565b6040805173ffffffffffffffffffffffffffffffffffffffff8316602082015261070591015b6040516020818303038152906040526106ee7f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f0090565b80549060006106fc83613eb3565b9190505561138d565b50565b600061071d6107168561124a565b84846114b8565b1561074957507f1626ba7e0000000000000000000000000000000000000000000000000000000061076c565b507fffffffff000000000000000000000000000000000000000000000000000000005b9392505050565b60007f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00600301826040516107a79190613e55565b9081526040519081900360200190205460ff1692915050565b6107c8611341565b60408051602081018490529081018290526107e5906060016106b8565b5050565b33735ff137d4b0fdcd49dca30c7cf57e578a026d278914610836576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156109305736600084848481811061085657610856613eeb565b90506020028101906108689190613f1a565b909250905060006108798284613f7f565b905061088481610f7a565b6108e3576040517f3b06e1460000000000000000000000000000000000000000000000000000000081527fffffffff00000000000000000000000000000000000000000000000000000000821660048201526024015b60405180910390fd5b61092530600085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061160c92505050565b505050600101610839565b505050565b33735ff137d4b0fdcd49dca30c7cf57e578a026d27891461095857610958611341565b60005b8181101561093057610a2c83838381811061097857610978613eeb565b905060200281019061098a9190613fc7565b6109989060208101906138d8565b8484848181106109aa576109aa613eeb565b90506020028101906109bc9190613fc7565b602001358585858181106109d2576109d2613eeb565b90506020028101906109e49190613fc7565b6109f2906040810190613f1a565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061160c92505050565b60010161095b565b600033735ff137d4b0fdcd49dca30c7cf57e578a026d278914610a83576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81602085013560401c7f2c2abd1e00000000000000000000000000000000000000000000000000000000610aba6060880188613f1a565b610ac391613f7f565b7fffffffff000000000000000000000000000000000000000000000000000000001603610b3857610af386610ca0565b94506121058114610b33576040517f2ef37813000000000000000000000000000000000000000000000000000000008152600481018290526024016108da565b610b76565b6121058103610b76576040517f2ef37813000000000000000000000000000000000000000000000000000000008152600481018290526024016108da565b610b8d85610b88610140890189613f1a565b6114b8565b15610b9c576000925050610ba2565b60019250505b8015610bb45760003860003884335af1505b509392505050565b7f000000000000000000000000000100abaad02f1cfc8bbe32bd5a564817339e72308103610bf257639f03a0266000526004601cfd5b610bfb84611689565b8360601b60601c93506352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80602060016004601d895afa5114610c4d576355299b496001526004601dfd5b847fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b600038a28490558115610c9a57604051828482376000388483885af4610c98573d6000823e3d81fd5b505b50505050565b6000610cab82611691565b604080516020810192909252735ff137d4b0fdcd49dca30c7cf57e578a026d2789908201526060015b604051602081830303815290604052805190602001209050919050565b60007f000000000000000000000000000100abaad02f1cfc8bbe32bd5a564817339e72308114610d2957639f03a0266000526004601cfd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc91505b5090565b7f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f005415610daa576040517f5daa87a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107e5610db78284613ffb565b6116aa565b7f0f000000000000000000000000000000000000000000000000000000000000006060806000808083610e53604080518082018252601581527f436f696e6261736520536d6172742057616c6c657400000000000000000000006020808301919091528251808401909352600183527f31000000000000000000000000000000000000000000000000000000000000009083015291565b97989097965046955030945091925090565b610e6d611341565b610e75610633565b600103610eae576040517f948bf89700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610930838383611861565b60008181527f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f0260205260409020805460609190610ef590614081565b80601f0160208091040260200160405190810160405280929190818152602001828054610f2190614081565b8015610f6e5780601f10610f4357610100808354040283529160200191610f6e565b820191906000526020600020905b815481529060010190602001808311610f5157829003601f168201915b50505050509050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f29565e3b00000000000000000000000000000000000000000000000000000000148061100d57507fffffffff0000000000000000000000000000000000000000000000000000000082167f0f0f3f2400000000000000000000000000000000000000000000000000000000145b8061105957507fffffffff0000000000000000000000000000000000000000000000000000000082167f89625b5700000000000000000000000000000000000000000000000000000000145b806110a557507fffffffff0000000000000000000000000000000000000000000000000000000082167fb819736700000000000000000000000000000000000000000000000000000000145b806110f157507fffffffff0000000000000000000000000000000000000000000000000000000082167f4f1ef28600000000000000000000000000000000000000000000000000000000145b156110fe57506001919050565b506000919050565b60007f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f006040805173ffffffffffffffffffffffffffffffffffffffff851660208201526003929092019101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526107a791613e55565b33735ff137d4b0fdcd49dca30c7cf57e578a026d2789146111ac576111ac611341565b610c9a848484848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061160c92505050565b6111f5611341565b60006111ff610633565b9050600181111561123f576040517f823f1aa8000000000000000000000000000000000000000000000000000000008152600481018290526024016108da565b610c9a848484611861565b600061062d82611a05565b60008060006112c8604080518082018252601581527f436f696e6261736520536d6172742057616c6c657400000000000000000000006020808301919091528251808401909352600183527f31000000000000000000000000000000000000000000000000000000000000009083015291565b8151602080840191909120825182840120604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f9481019490945283019190915260608201524660808201523060a0820152919350915060c001604051602081830303815290604052805190602001209250505090565b61134a33611106565b8061135457503330145b1561135b57565b6040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61139682610773565b156113cf57816040517f8d16255a0000000000000000000000000000000000000000000000000000000081526004016108da9190613da6565b6040517f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00906001907f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f0390611424908690613e55565b908152604080516020928190038301902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016931515939093179092556000848152600284019091522061147a848261411e565b50817f38109edc26e166b5579352ce56a50813177eb25208fd90d61f2f378386220220846040516114ab9190613da6565b60405180910390a2505050565b6000806114c783850185614238565b905060006114d88260000151610eb9565b9050805160200361155d5773ffffffffffffffffffffffffffffffffffffffff611501826142c5565b111561153b57806040517fbff1ac650000000000000000000000000000000000000000000000000000000081526004016108da9190613da6565b60006020820151905061155381888560200151611a56565b935050505061076c565b80516040036115d8576000808280602001905181019061157d9190614307565b915091506000846020015180602001905181019061159b9190614370565b90506115cc896040516020016115b391815260200190565b6040516020818303038152906040526000838686611b68565b9550505050505061076c565b806040517f4eeab7220000000000000000000000000000000000000000000000000000000081526004016108da9190613da6565b6000808473ffffffffffffffffffffffffffffffffffffffff1684846040516116359190613e55565b60006040518083038185875af1925050503d8060008114611672576040519150601f19603f3d011682016040523d82523d6000602084013e611677565b606091505b509150915081610c9857805160208201fd5b610705611341565b600061169c82611f72565b805190602001209050919050565b7f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00805460005b835181101561185b578381815181106116eb576116eb613eeb565b60200260200101515160201415801561171f575083818151811061171157611711613eeb565b602002602001015151604014155b156117715783818151811061173657611736613eeb565b60200260200101516040517f4eeab7220000000000000000000000000000000000000000000000000000000081526004016108da9190613da6565b83818151811061178357611783613eeb565b60200260200101515160201480156117d2575073ffffffffffffffffffffffffffffffffffffffff80168482815181106117bf576117bf613eeb565b60200260200101516117d0906142c5565b115b15611824578381815181106117e9576117e9613eeb565b60200260200101516040517fbff1ac650000000000000000000000000000000000000000000000000000000081526004016108da9190613da6565b61185384828151811061183957611839613eeb565b6020026020010151838061184c90613eb3565b945061138d565b6001016116d0565b50905550565b600061186c84610eb9565b905080516000036118ac576040517f68188e7a000000000000000000000000000000000000000000000000000000008152600481018590526024016108da565b82826040516118bc92919061442c565b604051809103902081805190602001201461190b57838383836040517f781f2e390000000000000000000000000000000000000000000000000000000081526004016108da9493929190614485565b6040517f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f00907f97e2c6aad4ce5d562ebfaa00db6b9e0fb66ea5d8162ed5b243f51a2e03086f039061195f908690869061442c565b908152604080516020928190038301902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905560008781526002840190925281206119ad91613843565b6001810180549060006119bf83613eb3565b9190505550847fcf95bbfe6f870f8cc40482dc3dccdafd268f0e9ce0a4f24ea1bea9be64e505ff85856040516119f69291906144bc565b60405180910390a25050505050565b6000611a0f611255565b611a1883612052565b6040517f1901000000000000000000000000000000000000000000000000000000000000602082015260228101929092526042820152606201610cd4565b73ffffffffffffffffffffffffffffffffffffffff909216916000831561076c576040518360005260208301516040526040835103611ad3576040830151601b8160ff1c016020528060011b60011c60605250602060016080600060015afa805186183d1517611ad15750600060605260405250600161076c565b505b6041835103611b1957606083015160001a6020526040830151606052602060016080600060015afa805186183d1517611b175750600060605260405250600161076c565b505b600060605280604052631626ba7e60e01b808252846004830152602482016040815284516020018060448501828860045afa505060208160443d01858a5afa9051909114169150509392505050565b6000611b9560027fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325516144d0565b8460a001511115611ba857506000611f69565b6060840151600090611bcb90611bbf81601561450b565b6020880151919061208d565b90507fff1a2a9176d650e4a99dedb58f1793003935130579fe17b5a3f698ac5b00e634818051906020012014611c05576000915050611f69565b6000611c1088612111565b604051602001611c20919061451e565b60405160208183030381529060405290506000611c58876040015183518960400151611c4c919061450b565b60208a0151919061208d565b90508180519060200120818051906020012014611c7b5760009350505050611f69565b865180517f01000000000000000000000000000000000000000000000000000000000000009182916020908110611cb457611cb4613eeb565b0160200151167fff000000000000000000000000000000000000000000000000000000000000001614611ced5760009350505050611f69565b878015611d595750865180517f04000000000000000000000000000000000000000000000000000000000000009182916020908110611d2e57611d2e613eeb565b0160200151167fff000000000000000000000000000000000000000000000000000000000000001614155b15611d6a5760009350505050611f69565b600060028860200151604051611d809190613e55565b602060405180830381855afa158015611d9d573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190611dc0919061458a565b905060006002896000015183604051602001611ddd9291906145a3565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052611e1591613e55565b602060405180830381855afa158015611e32573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190611e55919061458a565b6080808b015160a0808d015160408051602081018790529081019390935260608301529181018b905290810189905290915060009060c001604051602081830303815290604052905060008061010073ffffffffffffffffffffffffffffffffffffffff1683604051611ec89190613e55565b600060405180830381855afa9150503d8060008114611f03576040519150601f19603f3d011682016040523d82523d6000602084013e611f08565b606091505b50805191935091501515828015611f1c5750805b15611f485781806020019051810190611f35919061458a565b6001149950505050505050505050611f69565b611f5d858e608001518f60a001518f8f612137565b99505050505050505050505b95945050505050565b6060813560208301356000611f92611f8d6040870187613f1a565b612286565b90506000611fa6611f8d6060880188613f1a565b9050608086013560a087013560c088013560e08901356101008a01356000611fd5611f8d6101208e018e613f1a565b6040805173ffffffffffffffffffffffffffffffffffffffff9c909c1660208d01528b81019a909a5260608b019890985250608089019590955260a088019390935260c087019190915260e08601526101008501526101208401526101408084019190915281518084039091018152610160909201905292915050565b604080517f9b493d222105fee7df163ab5d57f0bf1ffd2da04dd5fafbe10b54c41c1adc6576020820152908101829052600090606001610cd4565b6060835182811161209c578092505b8381116120a7578093505b508183101561076c5750604051828203808252938301937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f820181165b86810151848201528101806120e65750600083830160200152603f9091011681016040529392505050565b606061062d826040518060600160405280604081526020016145dd604091396000612299565b600084158061216657507fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325518510155b8061216f575083155b8061219a57507fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325518410155b156121a757506000611f69565b6121b18383612419565b6121bd57506000611f69565b60006121c885612592565b905060007fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551828909905060007fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325518389099050600061222887878585612616565b90507fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325516122758a7fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551613ea0565b8208159a9950505050505050505050565b6000604051828085833790209392505050565b606083516000036122b9575060408051602081019091526000815261076c565b6000826122ea576003855160046122d091906145c5565b6122db90600261450b565b6122e591906144d0565b61230f565b6003855160026122fa919061450b565b61230491906144d0565b61230f9060046145c5565b905060008167ffffffffffffffff81111561232c5761232c613988565b6040519080825280601f01601f191660200182016040528015612356576020820181803683370190505b50905060018501602082018788518901602081018051600082525b828410156123cc576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450612371565b90525050851561240d576003885106600181146123f057600281146124035761240b565b603d6001830353603d600283035361240b565b603d60018303535b505b50909695505050505050565b60007fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8310158061246a57507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8210155b8061247c57508215801561247c575081155b156124895750600061062d565b60007fffffffff00000001000000000000000000000000ffffffffffffffffffffffff838409905060007fffffffff00000001000000000000000000000000ffffffffffffffffffffffff807fffffffff00000001000000000000000000000000fffffffffffffffffffffffc87097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff877fffffffff00000001000000000000000000000000ffffffffffffffffffffffff898a09090890507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff7f5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b820891909114949350505050565b600060405160208152602080820152602060408201528260608201527fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254f60808201527fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63255160a082015260208160c0836005600019fa61260f57600080fd5b5192915050565b600080808060ff81808815801561262b575087155b1561263f576000965050505050505061312c565b61268b7f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2967f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f58d8d613134565b90925090508115801561269c575080155b1561270d577fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551887fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551038a08985060009750881580156126f9575087155b1561270d576000965050505050505061312c565b600189841c16600189851c1660011b015b806127405760018403935060018a851c1660018a861c1660011b01905061271e565b50600189841c16600189851c1660011b019550600186036127a2577f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c29696507f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f593505b600286036127b1578a96508993505b600386036127c0578196508093505b60018303925060019550600194505b827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1115613082577fffffffff00000001000000000000000000000000ffffffffffffffffffffffff846002097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8182097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff818a097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff82840992507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff807fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8b8d087fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8c7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038e08096003097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff89850998507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8a840999507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80837fffffffff00000001000000000000000000000000fffffffffffffffffffffffd097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff838409089a507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80837fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038d08820992507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff837fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8a870908975060018d881c1660018d891c1660011b01905080612aab57877fffffffff00000001000000000000000000000000ffffffffffffffffffffffff03975050505050613077565b60018103612afa577f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c29693507f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f592505b60028103612b09578e93508d92505b60038103612b18578593508492505b89612b3157509198506001975087965094506130779050565b7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff887fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8b8609087fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8c7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff037fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8d880908935080612e705783612e70577fffffffff00000001000000000000000000000000ffffffffffffffffffffffff897fffffffff00000001000000000000000000000000fffffffffffffffffffffffd0994507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff85860993507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff848d0992507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff84860994507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff808c7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038e087fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8d8f080990507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8160030991507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8a860999507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8b85099a507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80847fffffffff00000001000000000000000000000000fffffffffffffffffffffffd097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff848509089b507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff808d7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038508830993507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff808a8709850898505050505050613077565b7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff84850991507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8483097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff838d099b507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff818c099a507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff838e097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80827fffffffff00000001000000000000000000000000fffffffffffffffffffffffd097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff847fffffffff00000001000000000000000000000000ffffffffffffffffffffffff037fffffffff00000001000000000000000000000000ffffffffffffffffffffffff878809080893507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80838d097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff857fffffffff00000001000000000000000000000000ffffffffffffffffffffffff887fffffffff00000001000000000000000000000000ffffffffffffffffffffffff03860809089a50505050809a50505050505b6001830392506127cf565b60405186606082015260208152602080820152602060408201527fffffffff00000001000000000000000000000000fffffffffffffffffffffffd60808201527fffffffff00000001000000000000000000000000ffffffffffffffffffffffff60a082015260208160c0836005600019fa6130fd57600080fd5b7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff815189099750505050505050505b949350505050565b60008080808661314b5785859350935050506131b9565b8461315d5787879350935050506131b9565b858814801561316b57508487145b1561318c5761317d88886001806131c2565b929a50909850925090506131a6565b61319b88886001808a8a61344c565b929a50909850925090505b6131b288888484613711565b9350935050505b94509492505050565b6000806000807fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8760020993507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff84850991507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff82890990507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff82850992507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff86830991507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff807fffffffff00000001000000000000000000000000ffffffffffffffffffffffff888b087fffffffff00000001000000000000000000000000ffffffffffffffffffffffff897fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038c080960030995507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80827fffffffff00000001000000000000000000000000fffffffffffffffffffffffd097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8889090893507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80857fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038308870997507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff85840990507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff808885097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff0389089250945094509450949050565b6000806000808860000361346b57508492508391506001905080613704565b7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff9889039889818988090894507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8a7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff037fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8a89090895507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff86870993507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff86850992507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff84890991507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff83880990507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff848b0997507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80897fffffffff00000001000000000000000000000000fffffffffffffffffffffffd097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff857fffffffff00000001000000000000000000000000ffffffffffffffffffffffff037fffffffff00000001000000000000000000000000ffffffffffffffffffffffff898a09080893507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff80848b097fffffffff00000001000000000000000000000000ffffffffffffffffffffffff877fffffffff00000001000000000000000000000000ffffffffffffffffffffffff887fffffffff00000001000000000000000000000000ffffffffffffffffffffffff038d08090892505b9650965096509692505050565b600080600061371f846137c6565b90507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff818709915060007fffffffff00000001000000000000000000000000ffffffffffffffffffffffff82870990507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff81820991507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8289099350505094509492505050565b600060405160208152602080820152602060408201528260608201527fffffffff00000001000000000000000000000000fffffffffffffffffffffffd60808201527fffffffff00000001000000000000000000000000ffffffffffffffffffffffff60a082015260208160c0836005600019fa61260f57600080fd5b50805461384f90614081565b6000825580601f1061385f575050565b601f01602090049060005260206000209081019061070591905b80821115610d4d5760008155600101613879565b600080604083850312156138a057600080fd5b50508035926020909101359150565b803573ffffffffffffffffffffffffffffffffffffffff811681146138d357600080fd5b919050565b6000602082840312156138ea57600080fd5b61076c826138af565b60008083601f84011261390557600080fd5b50813567ffffffffffffffff81111561391d57600080fd5b60208301915083602082850101111561393557600080fd5b9250929050565b60008060006040848603121561395157600080fd5b83359250602084013567ffffffffffffffff81111561396f57600080fd5b61397b868287016138f3565b9497909650939450505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160c0810167ffffffffffffffff811182821017156139da576139da613988565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613a2757613a27613988565b604052919050565b600067ffffffffffffffff821115613a4957613a49613988565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112613a8657600080fd5b8135613a99613a9482613a2f565b6139e0565b818152846020838601011115613aae57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613add57600080fd5b813567ffffffffffffffff811115613af457600080fd5b61312c84828501613a75565b60008083601f840112613b1257600080fd5b50813567ffffffffffffffff811115613b2a57600080fd5b6020830191508360208260051b850101111561393557600080fd5b60008060208385031215613b5857600080fd5b823567ffffffffffffffff811115613b6f57600080fd5b613b7b85828601613b00565b90969095509350505050565b60006101608284031215613b9a57600080fd5b50919050565b600080600060608486031215613bb557600080fd5b833567ffffffffffffffff811115613bcc57600080fd5b613bd886828701613b87565b9660208601359650604090950135949350505050565b600080600060408486031215613c0357600080fd5b613c0c846138af565b9250602084013567ffffffffffffffff81111561396f57600080fd5b600060208284031215613c3a57600080fd5b813567ffffffffffffffff811115613c5157600080fd5b61312c84828501613b87565b60005b83811015613c78578181015183820152602001613c60565b50506000910152565b60008151808452613c99816020860160208601613c5d565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7fff00000000000000000000000000000000000000000000000000000000000000881681526000602060e06020840152613d0860e084018a613c81565b8381036040850152613d1a818a613c81565b6060850189905273ffffffffffffffffffffffffffffffffffffffff8816608086015260a0850187905284810360c08601528551808252602080880193509091019060005b81811015613d7b57835183529284019291840191600101613d5f565b50909c9b505050505050505050505050565b600060208284031215613d9f57600080fd5b5035919050565b60208152600061076c6020830184613c81565b600060208284031215613dcb57600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461076c57600080fd5b60008060008060608587031215613e1157600080fd5b613e1a856138af565b935060208501359250604085013567ffffffffffffffff811115613e3d57600080fd5b613e49878288016138f3565b95989497509550505050565b60008251613e67818460208701613c5d565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561062d5761062d613e71565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613ee457613ee4613e71565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613f4f57600080fd5b83018035915067ffffffffffffffff821115613f6a57600080fd5b60200191503681900382131561393557600080fd5b7fffffffff000000000000000000000000000000000000000000000000000000008135818116916004851015613fbf5780818660040360031b1b83161692505b505092915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112613e6757600080fd5b600067ffffffffffffffff8084111561401657614016613988565b8360051b6020614028602083016139e0565b8681529185019160208101903684111561404157600080fd5b865b848110156140755780358681111561405b5760008081fd5b61406736828b01613a75565b845250918301918301614043565b50979650505050505050565b600181811c9082168061409557607f821691505b602082108103613b9a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b601f821115610930576000816000526020600020601f850160051c810160208610156140f75750805b601f850160051c820191505b8181101561411657828155600101614103565b505050505050565b815167ffffffffffffffff81111561413857614138613988565b61414c816141468454614081565b846140ce565b602080601f83116001811461419f57600084156141695750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555614116565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156141ec578886015182559484019460019091019084016141cd565b508582101561422857878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561424a57600080fd5b813567ffffffffffffffff8082111561426257600080fd5b908301906040828603121561427657600080fd5b60405160408101818110838211171561429157614291613988565b604052823581526020830135828111156142aa57600080fd5b6142b687828601613a75565b60208301525095945050505050565b80516020808301519190811015613b9a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60209190910360031b1b16919050565b6000806040838503121561431a57600080fd5b505080516020909101519092909150565b600082601f83011261433c57600080fd5b815161434a613a9482613a2f565b81815284602083860101111561435f57600080fd5b61312c826020830160208701613c5d565b60006020828403121561438257600080fd5b815167ffffffffffffffff8082111561439a57600080fd5b9083019060c082860312156143ae57600080fd5b6143b66139b7565b8251828111156143c557600080fd5b6143d18782860161432b565b8252506020830151828111156143e657600080fd5b6143f28782860161432b565b60208301525060408301516040820152606083015160608201526080830151608082015260a083015160a082015280935050505092915050565b8183823760009101908152919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b84815260606020820152600061449f60608301858761443c565b82810360408401526144b18185613c81565b979650505050505050565b60208152600061312c60208301848661443c565b600082614506577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b8082018082111561062d5761062d613e71565b7f226368616c6c656e6765223a220000000000000000000000000000000000000081526000825161455681600d850160208701613c5d565b7f2200000000000000000000000000000000000000000000000000000000000000600d939091019283015250600e01919050565b60006020828403121561459c57600080fd5b5051919050565b600083516145b5818460208801613c5d565b9190910191825250602001919050565b808202811582820484141761062d5761062d613e7156fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5fa26469706673582212208c2ea73863e242a1e00858d0d525af430fe3045920a71b632ca3e728ddc8817d64736f6c63430008170033
Deployed Bytecode Sourcemap
982:13565:11:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;887:1:4;874:15;869:3;865:25;1217:10;1214:1;1211:17;1198:10;1195:1;1192:17;1189:40;1176:10;1173:1;1170:17;1167:63;1164:190;;;1262:1;1256:4;1249:15;1256:4;1308;1301:18;1164:190;;6557:158:13;;;;;;;;;;-1:-1:-1;6557:158:13;;;;;:::i;:::-;;:::i;:::-;;;432:14:14;;425:22;407:41;;395:2;380:18;6557:158:13;;;;;;;;7856:190;;;;;;;;;;;;;:::i;:::-;;;605:25:14;;;593:2;578:18;7856:190:13;459:177:14;4089:163:13;;;;;;;;;;-1:-1:-1;4089:163:13;;;;;:::i;:::-;;:::i;3318:339:12:-;;;;;;;;;;-1:-1:-1;3318:339:12;;;;;:::i;:::-;;:::i;:::-;;;2041:66:14;2029:79;;;2011:98;;1999:2;1984:18;3318:339:12;1867:248:14;6958:145:13;;;;;;;;;;-1:-1:-1;6958:145:13;;;;;:::i;:::-;;:::i;4415:171::-;;;;;;;;;;-1:-1:-1;4415:171:13;;;;;:::i;:::-;;:::i;8363:425:11:-;;;;;;:::i;:::-;;:::i;9520:225::-;;;;;;:::i;:::-;;:::i;8255:136:13:-;;;;;;;;;;-1:-1:-1;8340:44:13;;8255:136;;6811:888:11;;;;;;;;;;-1:-1:-1;6811:888:11;;;;;:::i;:::-;;:::i;4012:1575:7:-;;;;;;:::i;:::-;;:::i;10390:196:11:-;;;;;;;;;;-1:-1:-1;10390:196:11;;;;;:::i;:::-;;:::i;3579:227:7:-;;;;;;;;;;;;;:::i;10723:147:11:-;;;;;;;;;;-1:-1:-1;10825:28:11;10819:35;10723:147;;;7153:42:14;7141:55;;;7123:74;;7111:2;7096:18;10723:147:11;6977:226:14;5312:194:11;;;;;;:::i;:::-;;:::i;2088:597:12:-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;2381:51:11:-;;;;;;;;;;;;2428:4;2381:51;;4984:218:13;;;;;;;;;;-1:-1:-1;4984:218:13;;;;;:::i;:::-;;:::i;7312:149::-;;;;;;;;;;-1:-1:-1;7312:149:13;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;11166:562:11:-;;;;;;;;;;-1:-1:-1;11166:562:11;;;;;:::i;:::-;;:::i;6145:154:13:-;;;;;;;;;;-1:-1:-1;6145:154:13;;;;;:::i;:::-;;:::i;9870:126:11:-;;;;;;;;;;-1:-1:-1;9947:42:11;9870:126;;9109:195;;;;;;:::i;:::-;;:::i;5645:283:13:-;;;;;;;;;;-1:-1:-1;5645:283:13;;;;;:::i;:::-;;:::i;4136:117:12:-;;;;;;;;;;-1:-1:-1;4136:117:12;;;;;:::i;:::-;;:::i;7621:128:13:-;;;;;;;;;;-1:-1:-1;11548:30:13;7702:40;7621:128;;4571:475:12;;;;;;;;;;;;;:::i;6557:158:13:-;6691:16;;;;;;11280:25:14;;;11321:18;;;11314:34;;;6634:4:13;;6657:33;;11253:18:14;;6691:16:13;;;;;;;;;;;;;;6657:51;;;:::i;:::-;;;;;;;;;;;;;;;;;;-1:-1:-1;6557:158:13;;;;;:::o;7856:190::-;8019:20;;11548:30;8000:16;;7907:7;;8000:39;;;:::i;:::-;7993:46;;;7856:190;:::o;4089:163::-;3953:13;:11;:13::i;:::-;4183:17:::1;::::0;;7153:42:14;7141:55;;4183:17:13::1;::::0;::::1;7123:74:14::0;4166:79:13::1;::::0;7096:18:14;4183:17:13::1;;;;;;;;;;;;;4202:25;11548:30:::0;;11400:194;4202:25:::1;:42:::0;;;:40:::1;:42;::::0;::::1;:::i;:::-;;;;;4166:16;:79::i;:::-;4089:163:::0;:::o;3318:339:12:-;3413:13;3442:69;3467:20;3482:4;3467:14;:20::i;:::-;3500:9;;3442:17;:69::i;:::-;3438:185;;;-1:-1:-1;3595:17:12;;;3438:185;-1:-1:-1;3633:17:12;3318:339;;;;;;:::o;6958:145:13:-;7031:4;11548:30;7054:33;;7088:7;7054:42;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;6958:145;-1:-1:-1;;6958:145:13:o;4415:171::-;3953:13;:11;:13::i;:::-;4518:16:::1;::::0;;::::1;::::0;::::1;11280:25:14::0;;;11321:18;;;11314:34;;;4501:78:13::1;::::0;11253:18:14;;4518:16:13::1;11106:248:14::0;4501:78:13::1;4415:171:::0;;:::o;8363:425:11:-;3351:10;9947:42;3351:26;3347:78;;3400:14;;;;;;;;;;;;;;3347:78;8483:9:::1;8478:304;8494:16:::0;;::::1;8478:304;;;8531:19;;8553:5;;8559:1;8553:8;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;8531:30:::0;;-1:-1:-1;8531:30:11;-1:-1:-1;8575:15:11::1;8593:12;8531:30:::0;;8593:12:::1;:::i;:::-;8575:30;;8624:34;8649:8;8624:24;:34::i;:::-;8619:109;;8685:28;::::0;::::1;::::0;;2041:66:14;2029:79;;8685:28:11::1;::::0;::::1;2011:98:14::0;1984:18;;8685:28:11::1;;;;;;;;8619:109;8742:29;8756:4;8763:1;8766:4;;8742:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;::::0;;;;-1:-1:-1;8742:5:11::1;::::0;-1:-1:-1;;;8742:29:11:i:1;:::-;-1:-1:-1::0;;;8512:3:11::1;;8478:304;;;;8363:425:::0;;:::o;9520:225::-;3604:10;9947:42;3604:26;3600:70;;3646:13;:11;:13::i;:::-;9627:9:::1;9622:117;9638:16:::0;;::::1;9622:117;;;9675:53;9681:5;;9687:1;9681:8;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;:15;::::0;::::1;::::0;::::1;::::0;::::1;:::i;:::-;9698:5;;9704:1;9698:8;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;:14;;;9714:5;;9720:1;9714:8;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;:13;::::0;::::1;::::0;::::1;::::0;::::1;:::i;:::-;9675:53;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;::::0;;;;-1:-1:-1;9675:5:11::1;::::0;-1:-1:-1;;;9675:53:11:i:1;:::-;9656:3;;9622:117;;6811:888:::0;7028:22;3351:10;9947:42;3351:26;3347:78;;3400:14;;;;;;;;;;;;;;3347:78;6990:19;7080:12:::2;::::0;::::2;;7096:2;7080:18;7140:45:::0;7120:15:::2;;::::0;::::2;7080:6:::0;7120:15:::2;:::i;:::-;7113:23;::::0;::::2;:::i;:::-;:72:::0;::::2;::::0;7109:380:::2;;7214:35;7242:6;7214:27;:35::i;:::-;7201:48;;2428:4;7267:3;:27;7263:93;;7321:20;::::0;::::2;::::0;;::::2;::::0;::::2;605:25:14::0;;;578:18;;7321:20:11::2;459:177:14::0;7263:93:11::2;7109:380;;;2428:4;7390:3;:27:::0;7386:93:::2;;7444:20;::::0;::::2;::::0;;::::2;::::0;::::2;605:25:14::0;;;578:18;;7444:20:11::2;459:177:14::0;7386:93:11::2;7567:47;7585:10:::0;7597:16:::2;;::::0;::::2;:6:::0;:16:::2;:::i;:::-;7567:17;:47::i;:::-;7563:86;;;7637:1;7630:8;;;;;7563:86;7691:1;7684:8;;;4359:1;4413:19:::1;4410:226;;;4616:4;4604:10;4598:4;4586:10;4565:19;4555:8;4548:5;4543:78;4539:83;4410:226;3435:1;6811:888:::0;;;;;:::o;4012:1575:7:-;5707:6;6068:9;6062:16;;6059:143;;6110:10;6104:4;6097:24;6183:4;6177;6170:18;6059:143;4165:36:::1;4183:17;4165;:36::i;:::-;4314:17;4310:2;4306:26;4302:2;4298:35;4277:56;;4384:10;4378:4;4371:24;4439:28;4644:1;4636:4;4630;4624;4618;4599:17;4592:5;4581:60;4575:67;4572:74;4562:199;;4679:10;4673:4;4666:24;4742:4;4736;4729:18;4562:199;4866:17;4839:25;4833:4;4821:10;4816:68;4897:28:::0;;;5055:516;::::1;;;5183:4;5177:11;5234;5221;5218:1;5205:41;5340:4;5328:10;5315:11;5312:1;5293:17;5286:5;5273:72;5263:294;;5474:16;5468:4;5465:1;5450:41;5522:16;5519:1;5512:27;5263:294;;5055:516;5685:544:::0;4012:1575;;;:::o;10390:196:11:-;10487:7;10534:29;10556:6;10534:21;:29::i;:::-;10523:55;;;;;;13880:25:14;;;;9947:42:11;13921:18:14;;;13914:83;13853:18;;10523:55:11;;;;;;;;;;;;;10513:66;;;;;;10506:73;;10390:196;;;:::o;3579:227:7:-;3646:7;6402:6;6500:9;6494:16;;6484:151;;6543:10;6537:4;6530:24;6616:4;6610;6603:18;6484:151;2614:66:::1;::::0;-1:-1:-1;6654:1:7::1;6380:282:::0;3579:227;:::o;5312:194:11:-;11548:30:13;7702:40;5396:21:11;5392:72;;5440:13;;;;;;;;;;;;;;5392:72;5474:25;;5492:6;;5474:25;:::i;:::-;:17;:25::i;2088:597:12:-;2428:16;2215:18;;2188:13;;;2215:18;2485:23;14501:37:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14387:158;2485:23:12;2088:597;;2467:41;;;-1:-1:-1;2528:13:12;;-1:-1:-1;2579:4:12;;-1:-1:-1;2088:597:12;;-1:-1:-1;2088:597:12;:::o;4984:218:13:-;3953:13;:11;:13::i;:::-;5090:12:::1;:10;:12::i;:::-;5106:1;5090:17:::0;5086:66:::1;;5130:11;;;;;;;;;;;;;;5086:66;5162:33;5182:5;5189;;5162:19;:33::i;7312:149::-:0;7409:45;;;;:38;:45;;;;;7402:52;;7378:12;;7409:45;7402:52;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7312:149;;;:::o;11166:562:11:-;11246:4;11279:59;;;11299:39;11279:59;;:136;;-1:-1:-1;11358:57:11;;;11378:37;11358:57;11279:136;:216;;;-1:-1:-1;11435:60:11;;;11455:40;11435:60;11279:216;:293;;;-1:-1:-1;11515:57:11;;;11535:37;11515:57;11279:293;:374;;;-1:-1:-1;11592:61:11;;;11612:41;11592:61;11279:374;11262:438;;;-1:-1:-1;11685:4:11;;11166:562;-1:-1:-1;11166:562:11:o;11262:438::-;-1:-1:-1;11716:5:11;;11166:562;-1:-1:-1;11166:562:11:o;6145:154:13:-;6215:4;11548:30;6272:19;;;7153:42:14;7141:55;;6272:19:13;;;7123:74:14;6238:33:13;;;;;;7096:18:14;6272:19:13;;;;;;;;;;;;;;6238:54;;;:::i;9109:195:11:-;3604:10;9947:42;3604:26;3600:70;;3646:13;:11;:13::i;:::-;9271:26:::1;9277:6;9285:5;9292:4;;9271:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;::::0;;;;-1:-1:-1;9271:5:11::1;::::0;-1:-1:-1;;;9271:26:11:i:1;5645:283:13:-:0;3953:13;:11;:13::i;:::-;5744:23:::1;5770:12;:10;:12::i;:::-;5744:38;;5814:1;5796:15;:19;5792:86;;;5838:29;::::0;::::1;::::0;;::::1;::::0;::::1;605:25:14::0;;;578:18;;5838:29:13::1;459:177:14::0;5792:86:13::1;5888:33;5908:5;5915;;5888:19;:33::i;4136:117:12:-:0;4203:7;4229:17;4241:4;4229:11;:17::i;4571:475::-;4619:7;4639:18;4659:21;4684:23;14501:37:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14387:158;4684:23:12;4888:22;;;;;;;;;;4928:25;;;;;;4747:282;;;4775:95;4747:282;;;15652:25:14;;;;15693:18;;15686:34;;;;15736:18;;;15729:34;4971:13:12;15779:18:14;;;15772:34;5010:4:12;15822:19:14;;;15815:84;4638:69:12;;-1:-1:-1;4638:69:12;-1:-1:-1;15624:19:14;;4747:282:12;;;;;;;;;;;;4724:315;;;;;;4717:322;;;;4571:475;:::o;11026:189:13:-;11085:26;11100:10;11085:14;:26::i;:::-;:59;;;-1:-1:-1;11116:10:13;11138:4;11116:27;11085:59;11081:96;;;11026:189::o;11081:96::-;11194:14;;;;;;;;;;;;;;9610:324;9702:19;9715:5;9702:12;:19::i;:::-;9698:51;;;9743:5;9730:19;;;;;;;;;;;:::i;9698:51::-;9827:16;;11548:30;;9846:4;;9827:9;;:16;;9837:5;;9827:16;:::i;:::-;;;;;;;;;;;;;;;;:23;;;;;;;;;;;;;;-1:-1:-1;9860:21:13;;;:14;;;:21;;;;:29;9884:5;9860:21;:29;:::i;:::-;;9914:5;9905:22;9921:5;9905:22;;;;;;:::i;:::-;;;;;;;;9688:246;9610:324;;:::o;12768:1307:11:-;12875:4;;12928:41;;;;12939:9;12928:41;:::i;:::-;12891:78;;12979:23;13005:35;13018:10;:21;;;13005:12;:35::i;:::-;12979:61;;13055:10;:17;13076:2;13055:23;13051:604;;13129:17;13106:19;13114:10;13106:19;:::i;:::-;13098:48;13094:318;;;13386:10;13358:39;;;;;;;;;;;:::i;13094:318::-;13426:13;13527:2;13515:10;13511:19;13505:26;13496:35;;13566:78;13606:5;13613:4;13619:10;:24;;;13566:39;:78::i;:::-;13559:85;;;;;;;13051:604;13669:10;:17;13690:2;13669:23;13665:351;;13709:9;13720;13744:10;13733:42;;;;;;;;;;;;:::i;:::-;13708:67;;;;13790:33;13837:10;:24;;;13826:61;;;;;;;;;;;;:::i;:::-;13790:97;;13909:96;13948:4;13937:16;;;;;;605:25:14;;593:2;578:18;;459:177;13937:16:11;;;;;;;;;;;;;13966:5;13987:4;13996:1;14002;13909:15;:96::i;:::-;13902:103;;;;;;;;;13665:351;14057:10;14033:35;;;;;;;;;;;:::i;12165:302::-;12250:12;12264:19;12287:6;:11;;12306:5;12313:4;12287:31;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12249:69;;;;12333:7;12328:133;;12429:6;12423:13;12418:2;12410:6;12406:15;12399:38;14257:96;3953:13:13;:11;:13::i;3509:124:2:-;3577:7;3613:12;3618:6;3613:4;:12::i;:::-;3603:23;;;;;;3596:30;;3509:124;;;:::o;8701:670:13:-;11548:30;8871:16;;8778:29;8897:424;8917:6;:13;8913:1;:17;8897:424;;;8955:6;8962:1;8955:9;;;;;;;;:::i;:::-;;;;;;;:16;8975:2;8955:22;;:48;;;;;8981:6;8988:1;8981:9;;;;;;;;:::i;:::-;;;;;;;:16;9001:2;8981:22;;8955:48;8951:128;;;9054:6;9061:1;9054:9;;;;;;;;:::i;:::-;;;;;;;9030:34;;;;;;;;;;;:::i;8951:128::-;9097:6;9104:1;9097:9;;;;;;;;:::i;:::-;;;;;;;:16;9117:2;9097:22;:73;;;;;9153:17;9123:47;;9139:6;9146:1;9139:9;;;;;;;;:::i;:::-;;;;;;;9131:18;;;:::i;:::-;9123:47;9097:73;9093:157;;;9225:6;9232:1;9225:9;;;;;;;;:::i;:::-;;;;;;;9197:38;;;;;;;;;;;:::i;9093:157::-;9264:46;9281:6;9288:1;9281:9;;;;;;;;:::i;:::-;;;;;;;9292:17;;;;;:::i;:::-;;;9264:16;:46::i;:::-;8932:3;;8897:424;;;-1:-1:-1;9330:34:13;;-1:-1:-1;8701:670:13:o;10273:575::-;10366:19;10388;10401:5;10388:12;:19::i;:::-;10366:41;;10421:6;:13;10438:1;10421:18;10417:52;;10448:21;;;;;;;;605:25:14;;;578:18;;10448:21:13;459:177:14;10417:52:13;10514:5;;10504:16;;;;;;;:::i;:::-;;;;;;;;10493:6;10483:17;;;;;;:37;10479:151;;10569:5;10591;;10611:6;10543:76;;;;;;;;;;;;;;:::i;10479:151::-;10714:16;;11548:30;;10714:9;;:16;;10724:5;;;;10714:16;:::i;:::-;;;;;;;;;;;;;;;;10707:23;;;;;;-1:-1:-1;10747:21:13;;;:14;;;:21;;;;;10740:28;;;:::i;:::-;10778:20;;;:22;;;:20;:22;;;:::i;:::-;;;;;;10828:5;10816:25;10835:5;;10816:25;;;;;;;:::i;:::-;;;;;;;;10356:492;;10273:575;;;:::o;5526:176:12:-;5592:7;5657:17;:15;:17::i;:::-;5676;5688:4;5676:11;:17::i;:::-;5628:66;;22783::14;5628::12;;;22771:79:14;22866:11;;;22859:27;;;;22902:12;;;22895:28;22939:12;;5628:66:12;22513:444:14;1980:4154:6;2295:24;;;;;2110:12;2322:6;2279:3839;;;2365:4;2359:11;2400:4;2394;2387:18;2456:4;2445:9;2441:20;2435:27;2429:4;2422:41;2512:2;2500:9;2494:16;2491:24;2488:1077;;2569:4;2558:9;2554:20;2548:27;2627:2;2622;2617:3;2613:12;2609:21;2603:4;2596:35;2687:2;2684:1;2680:10;2677:1;2673:18;2667:4;2660:32;;3089:4;3035;2983;2930;2872:1;2794:5;2754:384;3309:1;3303:8;3295:6;3291:21;3272:16;3265:24;3262:51;3252:295;;-1:-1:-1;3391:1:6;3385:4;3378:15;3451:4;3444:15;-1:-1:-1;3352:1:6;3520:5;;3252:295;;2488:1077;3606:2;3594:9;3588:16;3585:24;3582:1043;;3674:4;3663:9;3659:20;3653:27;3650:1;3645:36;3639:4;3632:50;3745:4;3734:9;3730:20;3724:27;3718:4;3711:41;4149:4;4095;4043;3990;3932:1;3854:5;3814:384;4369:1;4363:8;4355:6;4351:21;4332:16;4325:24;4322:51;4312:295;;-1:-1:-1;4451:1:6;4445:4;4438:15;4511:4;4504:15;-1:-1:-1;4412:1:6;4580:5;;4312:295;;3582:1043;4655:1;4649:4;4642:15;4713:1;4707:4;4700:15;4787:10;4782:3;4778:20;4825:1;4822;4815:12;4924:4;4917;4914:1;4910:12;4903:26;4962:4;4959:1;4955:12;4994:4;4991:1;4984:15;5137:9;5131:16;5125:4;5121:27;5218:1;5211:4;5208:1;5204:12;5201:1;5190:9;5187:1;5180:5;5169:51;5165:56;;6004:4;5952:1;5888:4;5870:16;5866:27;5806:1;5749:6;5700:5;5664:400;5418:8;;5415:15;;;5296:786;;-1:-1:-1;;1980:4154:6;;;;;:::o;6711:2934:10:-;6870:4;2132:21;2152:1;2162:66:9;2132:21:10;:::i;:::-;6894:12;:14;;;:30;6890:125;;;-1:-1:-1;6999:5:10;6992:12;;6890:125;7212:22;;;;7156:19;;7178:86;;7236:27;7212:22;7261:2;7236:27;:::i;:::-;7178;;;;;:86;:33;:86::i;:::-;7156:108;;2643:34;7294:5;7278:23;;;;;;:46;7274:89;;7347:5;7340:12;;;;;7274:89;7477:30;7547:27;7564:9;7547:16;:27::i;:::-;7516:64;;;;;;;;:::i;:::-;;;;;;;;;;;;;7477:104;;7591:29;7635:118;7669:12;:27;;;7728:17;:24;7698:12;:27;;;:54;;;;:::i;:::-;7635:27;;;;;:118;:33;:118::i;:::-;7591:162;;7814:17;7804:28;;;;;;7783:15;7767:33;;;;;;:65;7763:108;;7855:5;7848:12;;;;;;;7763:108;7987:30;;:34;;8047:19;;;;8018:2;;7987:34;;;;;;:::i;:::-;;;;;:56;:34;:56;:79;7983:122;;8089:5;8082:12;;;;;;;7983:122;8269:9;:94;;;;-1:-1:-1;8283:30:10;;:34;;8344:19;;;;8314:2;;8283:34;;;;;;:::i;:::-;;;;;:56;:34;:56;8282:81;;8269:94;8265:137;;;8386:5;8379:12;;;;;;;8265:137;8521:26;8550:42;8563:12;:27;;;8550:42;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;8521:71;;8749:19;8771:76;8795:12;:30;;;8827:18;8778:68;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;8771:76;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;8901:14;;;;;8917;;;;;8877:61;;;;;;25094:25:14;;;25135:18;;;25128:34;;;;-1:-1:-1;25178:18:14;;25171:34;25221:18;;;25214:34;;;25264:19;;;25257:35;;;8749:98:10;;-1:-1:-1;8857:17:10;;25066:19:14;;8877:61:10;;;;;;;;;;;;8857:81;;8996:12;9010:16;2404:5;9030:20;;9051:4;9030:26;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;9463:10:10;;8995:61;;-1:-1:-1;8995:61:10;-1:-1:-1;9463:14:10;;8995:61;9491:16;;;;;9502:5;9491:16;9487:60;;;9527:3;9516:26;;;;;;;;;;;;:::i;:::-;9546:1;9516:31;9509:38;;;;;;;;;;;;;9487:60;9565:73;9588:11;9601:12;:14;;;9617:12;:14;;;9633:1;9636;9565:22;:73::i;:::-;9558:80;;;;;;;;;;;6711:2934;;;;;;;;:::o;2561:942:2:-;2629:16;1877:20;;2717:12;;;;2657:14;2762:31;2777:15;;;;1877:20;2777:15;:::i;:::-;2762:14;:31::i;:::-;2739:54;-1:-1:-1;2803:20:2;2826:31;2841:15;;;;:6;:15;:::i;2826:31::-;2803:54;-1:-1:-1;2890:19:2;;;;2950:27;;;;3016:25;;;;3074:19;;;;3134:27;;;;2867:20;3202:39;3217:23;;;;2890:6;3217:23;:::i;3202:39::-;3259:237;;;25921:42:14;25909:55;;;;3259:237:2;;;25891:74:14;25981:18;;;25974:34;;;;26024:18;;;26017:34;;;;-1:-1:-1;26067:18:14;;;26060:34;;;;26110:19;;;26103:35;;;;26154:19;;;26147:35;;;;26198:19;;;26191:35;26242:19;;;26235:35;26286:19;;;26279:35;26330:19;;;;26323:35;;;;3259:237:2;;;;;;;;;;25863:19:14;;;;3259:237:2;;;2561:942;-1:-1:-1;;2561:942:2:o;6151:145:12:-;6253:35;;;1323:53;6253:35;;;11280:25:14;11321:18;;;11314:34;;;6217:7:12;;11253:18:14;;6253:35:12;11106:248:14;30663:1307:5;30776:20;30905:7;30899:14;30954:3;30939:13;30936:22;30926:58;;30969:13;30962:20;;30926:58;31025:5;31010:13;31007:24;30997:62;;31044:13;31035:22;;30997:62;;31085:3;31078:5;31075:14;31072:882;;;-1:-1:-1;31124:4:5;31118:11;31166:15;;;31198:28;;;31254:19;;;;31299:9;31303:4;31413:23;;31409:31;;31394:237;31499:15;;;31493:22;31477:14;;;31470:46;31542:9;;31591:22;31394:237;31591:22;-1:-1:-1;31747:1:5;31709:36;;;31725:4;31709:36;31702:47;31929:4;31911:23;;;31907:31;31895:44;;31889:4;31882:58;30663:1307;;;;;:::o;883:132:3:-;944:13;976:32;984:4;990:10;;;;;;;;;;;;;;;;;1002:5;976:7;:32::i;1562:703:8:-;1671:4;1691:6;;;:32;;;2162:66:9;1701:1:8;:22;;1691:32;:42;;;-1:-1:-1;1727:6:8;;1691:42;:68;;;;2162:66:9;1737:1:8;:22;;1691:68;1687:111;;;-1:-1:-1;1782:5:8;1775:12;;1687:111;1821:39;1853:2;1857;1821:31;:39::i;:::-;1816:83;;-1:-1:-1;1883:5:8;1876:12;;1816:83;1909:12;1924:30;1952:1;1924:27;:30::i;:::-;1909:45;-1:-1:-1;1965:16:8;2162:66:9;2009:4:8;1999:7;1984:49;1965:68;-1:-1:-1;2043:16:8;2162:66:9;2072:4:8;2069:1;2062:34;2043:53;;2106:10;2132:64;2169:2;2173;2177:8;2187;2132:36;:64::i;:::-;2127:69;-1:-1:-1;2162:66:9;2222:3:8;2224:1;2162:66:9;2222:3:8;:::i;:::-;2218:2;2211:18;2251:7;;1562:703;-1:-1:-1;;;;;;;;;;1562:703:8:o;3357:265:0:-;3416:11;3479:4;3473:11;3508;3563:3;3550:11;3545:3;3532:35;3587:19;;;3357:265;-1:-1:-1;;;3357:265:0:o;1084:3832:3:-;1181:13;1413:4;:11;1428:1;1413:16;1409:31;;-1:-1:-1;1431:9:3;;;;;;;;;-1:-1:-1;1431:9:3;;;;1409:31;2161:20;2184:11;:69;;2252:1;2233:4;:11;2229:1;:15;;;;:::i;:::-;:19;;2247:1;2229:19;:::i;:::-;2228:25;;;;:::i;:::-;2184:69;;;2223:1;2204:4;:11;2218:1;2204:15;;;;:::i;:::-;2203:21;;;;:::i;:::-;2198:27;;:1;:27;:::i;:::-;2161:92;;2264:20;2298:12;2287:24;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2287:24:3;;2264:47;;2486:1;2479:5;2475:13;2587:4;2579:6;2575:17;2620:4;2667;2661:11;2655:4;2651:22;2915:4;2907:6;2903:17;2957:8;2951:15;2996:4;2986:8;2979:22;3068:1259;3101:6;3092:7;3089:19;3068:1259;;;3203:1;3194:7;3190:15;3179:26;;3241:7;3235:14;3828:4;3820:5;3816:2;3812:14;3808:25;3798:8;3794:40;3788:47;3777:9;3769:67;3881:1;3870:9;3866:17;3853:30;;3971:4;3963:5;3959:2;3955:14;3951:25;3941:8;3937:40;3931:47;3920:9;3912:67;4024:1;4013:9;4009:17;3996:30;;4113:4;4105:5;4102:1;4098:13;4094:24;4084:8;4080:39;4074:46;4063:9;4055:66;4166:1;4155:9;4151:17;4138:30;;4247:4;4240:5;4236:16;4226:8;4222:31;4216:38;4205:9;4197:58;;4300:1;4289:9;4285:17;4272:30;;3068:1259;;;4388:28;;-1:-1:-1;;4430:446:3;;;;4615:1;4608:4;4602:11;4598:19;4639:1;4634:132;;;;4788:1;4783:79;;;;4591:271;;4634:132;4690:4;4686:1;4675:9;4671:17;4663:32;4743:4;4739:1;4728:9;4724:17;4716:32;4634:132;;4783:79;4839:4;4835:1;4824:9;4820:17;4812:32;4591:271;;4430:446;-1:-1:-1;4903:6:3;;1084:3832;-1:-1:-1;;;;;;1084:3832:3:o;12684:438:9:-;12754:4;1531:66;12774:1;:6;;:16;;;;1531:66;12784:1;:6;;12774:16;:42;;;-1:-1:-1;12796:6:9;;12795:20;;;;-1:-1:-1;12808:6:9;;12795:20;12770:85;;;-1:-1:-1;12839:5:9;12832:12;;12770:85;12888:11;1531:66;12912:1;12909;12902:15;12888:29;-1:-1:-1;12938:11:9;1531:66;;1666;12997:1;12990:15;1531:66;12983:1;1531:66;12976:1;12973;12966:15;12959:29;12952:57;12938:71;-1:-1:-1;1531:66:9;1802;13046:3;13039:17;13095:10;;;;;12684:438;-1:-1:-1;;;;12684:438:9:o;3154:734::-;3209:14;3279:4;3273:11;3390:4;3381:7;3374:21;3435:4;3428;3419:7;3415:18;3408:32;3480:4;3473;3464:7;3460:18;3453:32;3584:1;3577:4;3568:7;3564:18;3557:29;3626:11;3619:4;3610:7;3606:18;3599:39;3678:1;3671:4;3662:7;3658:18;3651:29;3812:4;3803:7;3797:4;3788:7;3782:4;3778:1;3774:6;3763:54;3753:82;;3831:1;3828;3821:12;3753:82;3858:14;;3154:734;-1:-1:-1;;3154:734:9:o;13895:6587::-;14077:9;;;;14174:3;14077:9;;14256:13;;:30;;;;-1:-1:-1;14273:13:9;;14256:30;14252:44;;;14295:1;14288:8;;;;;;;;;;14252:44;14322:25;1938:66;2032;14340:2;14344;14322:9;:25::i;:::-;14311:36;;-1:-1:-1;14311:36:9;-1:-1:-1;14366:5:9;;14365:16;;;;-1:-1:-1;14375:5:9;;14365:16;14362:211;;;2162:66;14456:8;2162:66;14454:10;14444:8;14437:31;14428:40;-1:-1:-1;14495:1:9;;-1:-1:-1;14518:13:9;;:30;;;;-1:-1:-1;14535:13:9;;14518:30;14514:44;;;14557:1;14550:8;;;;;;;;;;14514:44;14697:1;14686:8;14679:5;14675:20;14671:28;14666:1;14655:8;14648:5;14644:20;14640:28;14637:1;14633:36;14629:71;14613:263;14706:2;14613:263;;14755:1;14748:5;14744:13;14735:22;;14852:1;14841:8;14834:5;14830:20;14826:28;14821:1;14810:8;14803:5;14799:20;14795:28;14792:1;14788:36;14784:71;14778:77;;14613:263;;;14617:85;14967:1;14956:8;14949:5;14945:20;14941:28;14936:1;14925:8;14918:5;14914:20;14910:28;14907:1;14903:36;14899:71;14893:77;;14998:1;14994:2;14991:9;14988:88;;15028:2;15023:7;;15056:2;15051:7;;14988:88;15103:1;15099:2;15096:9;15093:88;;15133:2;15128:7;;15161:2;15156:7;;15093:88;15208:1;15204:2;15201:9;15198:88;;15238:2;15233:7;;15266:2;15261:7;;15198:88;15324:1;15317:5;15313:13;15304:22;;15349:1;15343:7;;15374:1;15367:8;;15393:4025;15412:5;15403:7;15400:18;15393:4025;;;15531:1;15528;15525;15518:15;15598:1;15594:2;15590;15583:17;15654:1;15650:2;15647:1;15640:16;15710:1;15706:2;15702;15695:17;15689:23;;15816:1;15812;15808;15804:2;15801:1;15794:16;15790:1;15785:2;15782:1;15778:10;15775:1;15768:24;15761:53;15758:1;15751:67;15886:1;15881:3;15877:2;15870:18;15863:25;;15944:1;15940:2;15936;15929:17;15923:23;;16043:1;16039;16035:2;16026:7;16019:22;16015:1;16011:2;16007;16000:17;15993:52;15988:57;;16121:1;16117;16112:2;16109:1;16105:10;16102:1;16095:24;16091:2;16084:39;16078:45;;16197:1;16193:2;16189:1;16186;16182:2;16175:16;16168:31;16163:36;;16426:1;16415:8;16408:5;16404:20;16400:28;16395:1;16384:8;16377:5;16373:20;16369:28;16366:1;16362:36;16358:71;16352:77;;16465:2;16455:148;;16511:1;16508;16504:9;16499:14;;16569:8;;;;;;16455:148;16651:1;16647:2;16644:9;16641:114;;16690:2;16684:8;;16727:2;16721:8;;16641:114;16790:1;16786:2;16783:9;16780:114;;16829:2;16823:8;;16866:2;16860:8;;16780:114;16929:1;16925:2;16922:9;16919:114;;16968:2;16962:8;;17005:2;16999:8;;16919:114;17068:2;17058:223;;-1:-1:-1;17107:2:9;;-1:-1:-1;17180:1:9;;-1:-1:-1;17180:1:9;;-1:-1:-1;17143:2:9;-1:-1:-1;17247:8:9;;-1:-1:-1;17247:8:9;17058:223;17464:1;17461;17457;17452:3;17448:2;17441:18;17434:32;17538:1;17534;17531;17527:9;17523:1;17519:2;17515;17508:17;17501:39;17495:45;;17765:2;17755:1043;;17809:2;17799:973;;17872:1;17869;17860:7;17853:21;17847:27;;17947:1;17943:2;17939;17932:17;17926:23;;18011:1;18007:2;18004:1;17997:16;17991:22;;18080:1;18076:2;18072;18065:17;18059:23;;18180:1;18176;18171:2;18168:1;18164:10;18161:1;18154:24;18150:1;18146:2;18143:1;18136:16;18129:53;18123:59;;18250:1;18246:2;18243:1;18236:16;18230:22;;18328:1;18323:3;18319:2;18312:18;18305:25;;18398:1;18394:2;18390;18383:17;18377:23;;18509:1;18505;18501:2;18492:7;18485:22;18481:1;18477:2;18473;18466:17;18459:52;18454:57;;18599:1;18595;18591;18588;18584:9;18580:2;18573:24;18569:2;18562:39;18556:45;;18679:1;18675;18672;18668:2;18661:16;18657:2;18650:31;18645:36;;18734:8;;;;;;;17799:973;18845:1;18841:2;18837;18830:17;18824:23;;18903:1;18899:2;18895;18888:17;19019:1;19015:2;19011;19004:17;18998:23;;19070:1;19065:3;19060;19053:19;19046:26;;19134:1;19130:2;19127:1;19120:16;19242:1;19238;19233:3;19224:7;19217:23;19213:1;19207:3;19204:1;19200:11;19196:1;19192:2;19188;19181:17;19174:41;19167:77;19161:83;;19343:1;19339;19334:3;19331:1;19324:17;19320:1;19316:2;19312:1;19307:2;19304:1;19300:10;19295:3;19288:26;19281:41;19274:71;19269:76;;;;;19376:2;19371:7;;16285:3115;;;;15393:4025;15441:1;15434:5;15430:13;15421:22;;15393:4025;;;19461:4;19455:11;19504:2;19497:4;19494:1;19490:12;19483:24;19769:4;19766:1;19759:15;19812:4;19805;19802:1;19798:12;19791:26;19855:4;19848;19845:1;19841:12;19834:26;20009:7;20002:4;19999:1;19995:12;19988:29;20055:1;20048:4;20045:1;20041:12;20034:23;20185:4;20182:1;20176:4;20173:1;20167:4;20163:1;20159:6;20148:42;20138:70;;20204:1;20201;20194:12;20138:70;20393:1;20389;20383:8;20380:1;20373:22;20368:27;;;20467:8;;;;;;13895:6587;;;;;;;:::o;13224:499::-;13314:7;;;;12535:6;13386:41;;13420:2;13424;13412:15;;;;;;;;13386:41;12535:6;13437:41;;13471:2;13475;13463:15;;;;;;;;13437:41;13496:2;13492;:6;13491:18;;;;;13506:2;13502;:6;13491:18;13488:181;;;13547:20;13556:2;13560;13563:1;13565;13547:8;:20::i;:::-;13525:42;;-1:-1:-1;13525:42:9;;-1:-1:-1;13525:42:9;-1:-1:-1;13525:42:9;-1:-1:-1;13488:181:9;;;13627:31;13637:2;13641;13645:1;13648;13651:2;13655;13627:9;:31::i;:::-;13605:53;;-1:-1:-1;13605:53:9;;-1:-1:-1;13605:53:9;-1:-1:-1;13605:53:9;-1:-1:-1;13488:181:9;13686:30;13698:2;13702;13706:3;13711:4;13686:11;:30::i;:::-;13679:37;;;;;;13224:499;;;;;;;;:::o;9414:928::-;9526:10;9538;9550;9562;9658:1;9655;9652;9645:15;9639:21;;9709:1;9705:2;9701;9694:17;9688:23;;9757:1;9753:2;9750:1;9743:16;9737:22;;9809:1;9805:2;9801;9794:17;9788:23;;9857:1;9853:2;9849;9842:17;9836:23;;9959:1;9955;9951;9947:2;9944:1;9937:16;9933:1;9928:2;9925:1;9921:10;9918:1;9911:24;9904:53;9901:1;9894:67;9888:73;;10058:1;10054;10050:2;10041:7;10034:22;10030:1;10026:2;10022;10015:17;10008:52;10002:58;;10132:1;10128;10123:2;10120:1;10116:10;10112:2;10105:25;10101:2;10094:40;10089:45;;10183:1;10178:3;10174:2;10167:18;10161:24;;10258:1;10253;10250;10246:2;10239:16;10236:1;10232:24;10229:1;10222:38;10216:44;;9414:928;;;;;;;;;:::o;10559:1073::-;10700:10;10712;10724;10736;10790:2;10796:1;10790:7;10786:67;;-1:-1:-1;10825:2:9;;-1:-1:-1;10829:2:9;;-1:-1:-1;10833:1:9;;-1:-1:-1;10833:1:9;10817:21;;10786:67;10904:1;10900:10;;;;;10904:1;10951:4;10947:2;10940:19;10933:34;10927:40;;11029:1;11024:2;11021:1;11017:10;11013:1;11008:3;11004:2;10997:18;10990:41;10984:47;;11069:1;11065:2;11061;11054:17;11048:23;;11120:1;11116:2;11112;11105:17;11099:23;;11174:1;11170:2;11165:3;11158:18;11152:24;;11233:1;11229:2;11223:4;11216:19;11210:25;;11294:1;11290:2;11286;11279:17;11272:24;;11405:1;11401;11396:3;11387:7;11380:23;11376:1;11371:2;11368:1;11364:10;11360:1;11356:2;11352;11345:17;11338:40;11331:76;11325:82;;11513:1;11509;11505:2;11501;11494:17;11490:1;11486:2;11482:1;11477:2;11474:1;11470:10;11465:3;11458:26;11451:41;11444:71;11438:77;;10559:1073;;;;;;;;;;;;:::o;8954:351::-;9045:10;9057;9079:14;9096:16;9108:3;9096:11;:16::i;:::-;9079:33;-1:-1:-1;1531:66:9;9145:6;9142:1;9135:20;9130:25;-1:-1:-1;9173:10:9;1531:66;9197:6;9193:2;9186:21;9173:34;-1:-1:-1;1531:66:9;9243:2;9239;9232:17;9223:26;-1:-1:-1;1531:66:9;9281:6;9278:1;9271:20;9266:25;;9069:236;;8954:351;;;;;;;:::o;4000:730::-;4055:14;4125:4;4119:11;4236:4;4227:7;4220:21;4281:4;4274;4265:7;4261:18;4254:32;4326:4;4319;4310:7;4306:18;4299:32;4430:1;4423:4;4414:7;4410:18;4403:29;4472:7;4465:4;4456:7;4452:18;4445:35;4520:1;4513:4;4504:7;4500:18;4493:29;4654:4;4645:7;4639:4;4630:7;4624:4;4620:1;4616:6;4605:54;4595:82;;4673:1;4670;4663:12;-1:-1:-1;;;;;;;:::i;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14:248:14;82:6;90;143:2;131:9;122:7;118:23;114:32;111:52;;;159:1;156;149:12;111:52;-1:-1:-1;;182:23:14;;;252:2;237:18;;;224:32;;-1:-1:-1;14:248:14:o;641:196::-;709:20;;769:42;758:54;;748:65;;738:93;;827:1;824;817:12;738:93;641:196;;;:::o;842:186::-;901:6;954:2;942:9;933:7;929:23;925:32;922:52;;;970:1;967;960:12;922:52;993:29;1012:9;993:29;:::i;1033:347::-;1084:8;1094:6;1148:3;1141:4;1133:6;1129:17;1125:27;1115:55;;1166:1;1163;1156:12;1115:55;-1:-1:-1;1189:20:14;;1232:18;1221:30;;1218:50;;;1264:1;1261;1254:12;1218:50;1301:4;1293:6;1289:17;1277:29;;1353:3;1346:4;1337:6;1329;1325:19;1321:30;1318:39;1315:59;;;1370:1;1367;1360:12;1315:59;1033:347;;;;;:::o;1385:477::-;1464:6;1472;1480;1533:2;1521:9;1512:7;1508:23;1504:32;1501:52;;;1549:1;1546;1539:12;1501:52;1585:9;1572:23;1562:33;;1646:2;1635:9;1631:18;1618:32;1673:18;1665:6;1662:30;1659:50;;;1705:1;1702;1695:12;1659:50;1744:58;1794:7;1785:6;1774:9;1770:22;1744:58;:::i;:::-;1385:477;;1821:8;;-1:-1:-1;1718:84:14;;-1:-1:-1;;;;1385:477:14:o;2120:184::-;2172:77;2169:1;2162:88;2269:4;2266:1;2259:15;2293:4;2290:1;2283:15;2309:253;2381:2;2375:9;2423:4;2411:17;;2458:18;2443:34;;2479:22;;;2440:62;2437:88;;;2505:18;;:::i;:::-;2541:2;2534:22;2309:253;:::o;2567:334::-;2638:2;2632:9;2694:2;2684:13;;2699:66;2680:86;2668:99;;2797:18;2782:34;;2818:22;;;2779:62;2776:88;;;2844:18;;:::i;:::-;2880:2;2873:22;2567:334;;-1:-1:-1;2567:334:14:o;2906:245::-;2954:4;2987:18;2979:6;2976:30;2973:56;;;3009:18;;:::i;:::-;-1:-1:-1;3066:2:14;3054:15;3071:66;3050:88;3140:4;3046:99;;2906:245::o;3156:462::-;3198:5;3251:3;3244:4;3236:6;3232:17;3228:27;3218:55;;3269:1;3266;3259:12;3218:55;3305:6;3292:20;3336:48;3352:31;3380:2;3352:31;:::i;:::-;3336:48;:::i;:::-;3409:2;3400:7;3393:19;3455:3;3448:4;3443:2;3435:6;3431:15;3427:26;3424:35;3421:55;;;3472:1;3469;3462:12;3421:55;3537:2;3530:4;3522:6;3518:17;3511:4;3502:7;3498:18;3485:55;3585:1;3560:16;;;3578:4;3556:27;3549:38;;;;3564:7;3156:462;-1:-1:-1;;;3156:462:14:o;3623:320::-;3691:6;3744:2;3732:9;3723:7;3719:23;3715:32;3712:52;;;3760:1;3757;3750:12;3712:52;3800:9;3787:23;3833:18;3825:6;3822:30;3819:50;;;3865:1;3862;3855:12;3819:50;3888:49;3929:7;3920:6;3909:9;3905:22;3888:49;:::i;3948:374::-;4018:8;4028:6;4082:3;4075:4;4067:6;4063:17;4059:27;4049:55;;4100:1;4097;4090:12;4049:55;-1:-1:-1;4123:20:14;;4166:18;4155:30;;4152:50;;;4198:1;4195;4188:12;4152:50;4235:4;4227:6;4223:17;4211:29;;4295:3;4288:4;4278:6;4275:1;4271:14;4263:6;4259:27;4255:38;4252:47;4249:67;;;4312:1;4309;4302:12;4327:455;4424:6;4432;4485:2;4473:9;4464:7;4460:23;4456:32;4453:52;;;4501:1;4498;4491:12;4453:52;4541:9;4528:23;4574:18;4566:6;4563:30;4560:50;;;4606:1;4603;4596:12;4560:50;4645:77;4714:7;4705:6;4694:9;4690:22;4645:77;:::i;:::-;4741:8;;4619:103;;-1:-1:-1;4327:455:14;-1:-1:-1;;;;4327:455:14:o;5260:162::-;5326:5;5371:3;5362:6;5357:3;5353:16;5349:26;5346:46;;;5388:1;5385;5378:12;5346:46;-1:-1:-1;5410:6:14;5260:162;-1:-1:-1;5260:162:14:o;5427:503::-;5536:6;5544;5552;5605:2;5593:9;5584:7;5580:23;5576:32;5573:52;;;5621:1;5618;5611:12;5573:52;5661:9;5648:23;5694:18;5686:6;5683:30;5680:50;;;5726:1;5723;5716:12;5680:50;5749:73;5814:7;5805:6;5794:9;5790:22;5749:73;:::i;:::-;5739:83;5869:2;5854:18;;5841:32;;-1:-1:-1;5920:2:14;5905:18;;;5892:32;;5427:503;-1:-1:-1;;;;5427:503:14:o;5935:483::-;6014:6;6022;6030;6083:2;6071:9;6062:7;6058:23;6054:32;6051:52;;;6099:1;6096;6089:12;6051:52;6122:29;6141:9;6122:29;:::i;:::-;6112:39;;6202:2;6191:9;6187:18;6174:32;6229:18;6221:6;6218:30;6215:50;;;6261:1;6258;6251:12;6423:367;6514:6;6567:2;6555:9;6546:7;6542:23;6538:32;6535:52;;;6583:1;6580;6573:12;6535:52;6623:9;6610:23;6656:18;6648:6;6645:30;6642:50;;;6688:1;6685;6678:12;6642:50;6711:73;6776:7;6767:6;6756:9;6752:22;6711:73;:::i;7208:250::-;7293:1;7303:113;7317:6;7314:1;7311:13;7303:113;;;7393:11;;;7387:18;7374:11;;;7367:39;7339:2;7332:10;7303:113;;;-1:-1:-1;;7450:1:14;7432:16;;7425:27;7208:250::o;7463:330::-;7505:3;7543:5;7537:12;7570:6;7565:3;7558:19;7586:76;7655:6;7648:4;7643:3;7639:14;7632:4;7625:5;7621:16;7586:76;:::i;:::-;7707:2;7695:15;7712:66;7691:88;7682:98;;;;7782:4;7678:109;;7463:330;-1:-1:-1;;7463:330:14:o;7798:1335::-;8195:66;8187:6;8183:79;8172:9;8165:98;8146:4;8282:2;8320:3;8315:2;8304:9;8300:18;8293:31;8347:46;8388:3;8377:9;8373:19;8365:6;8347:46;:::i;:::-;8441:9;8433:6;8429:22;8424:2;8413:9;8409:18;8402:50;8475:33;8501:6;8493;8475:33;:::i;:::-;8539:2;8524:18;;8517:34;;;8600:42;8588:55;;8582:3;8567:19;;8560:84;8675:3;8660:19;;8653:35;;;8725:22;;;8719:3;8704:19;;8697:51;8797:13;;8819:22;;;8869:2;8895:15;;;;-1:-1:-1;8857:15:14;;;;-1:-1:-1;8938:169:14;8952:6;8949:1;8946:13;8938:169;;;9013:13;;9001:26;;9082:15;;;;9047:12;;;;8974:1;8967:9;8938:169;;;-1:-1:-1;9124:3:14;;7798:1335;-1:-1:-1;;;;;;;;;;;;7798:1335:14:o;9620:180::-;9679:6;9732:2;9720:9;9711:7;9707:23;9703:32;9700:52;;;9748:1;9745;9738:12;9700:52;-1:-1:-1;9771:23:14;;9620:180;-1:-1:-1;9620:180:14:o;9805:218::-;9952:2;9941:9;9934:21;9915:4;9972:45;10013:2;10002:9;9998:18;9990:6;9972:45;:::i;10028:332::-;10086:6;10139:2;10127:9;10118:7;10114:23;10110:32;10107:52;;;10155:1;10152;10145:12;10107:52;10194:9;10181:23;10244:66;10237:5;10233:78;10226:5;10223:89;10213:117;;10326:1;10323;10316:12;10365:551;10453:6;10461;10469;10477;10530:2;10518:9;10509:7;10505:23;10501:32;10498:52;;;10546:1;10543;10536:12;10498:52;10569:29;10588:9;10569:29;:::i;:::-;10559:39;;10645:2;10634:9;10630:18;10617:32;10607:42;;10700:2;10689:9;10685:18;10672:32;10727:18;10719:6;10716:30;10713:50;;;10759:1;10756;10749:12;10713:50;10798:58;10848:7;10839:6;10828:9;10824:22;10798:58;:::i;:::-;10365:551;;;;-1:-1:-1;10875:8:14;-1:-1:-1;;;;10365:551:14:o;11359:287::-;11488:3;11526:6;11520:13;11542:66;11601:6;11596:3;11589:4;11581:6;11577:17;11542:66;:::i;:::-;11624:16;;;;;11359:287;-1:-1:-1;;11359:287:14:o;11651:184::-;11703:77;11700:1;11693:88;11800:4;11797:1;11790:15;11824:4;11821:1;11814:15;11840:128;11907:9;;;11928:11;;;11925:37;;;11942:18;;:::i;11973:195::-;12012:3;12043:66;12036:5;12033:77;12030:103;;12113:18;;:::i;:::-;-1:-1:-1;12160:1:14;12149:13;;11973:195::o;12173:184::-;12225:77;12222:1;12215:88;12322:4;12319:1;12312:15;12346:4;12343:1;12336:15;12362:580;12439:4;12445:6;12505:11;12492:25;12595:66;12584:8;12568:14;12564:29;12560:102;12540:18;12536:127;12526:155;;12677:1;12674;12667:12;12526:155;12704:33;;12756:20;;;-1:-1:-1;12799:18:14;12788:30;;12785:50;;;12831:1;12828;12821:12;12785:50;12864:4;12852:17;;-1:-1:-1;12895:14:14;12891:27;;;12881:38;;12878:58;;;12932:1;12929;12922:12;12947:369;13105:66;13067:19;;13189:11;;;;13220:1;13212:10;;13209:101;;;13297:2;13291;13284:3;13281:1;13277:11;13274:1;13270:19;13266:28;13262:2;13258:37;13254:46;13245:55;;13209:101;;;12947:369;;;;:::o;13321:380::-;13411:4;13469:11;13456:25;13559:66;13548:8;13532:14;13528:29;13524:102;13504:18;13500:127;13490:155;;13641:1;13638;13631:12;14008:938;14144:9;14178:18;14219:2;14211:6;14208:14;14205:40;;;14225:18;;:::i;:::-;14271:6;14268:1;14264:14;14297:4;14321:30;14345:4;14341:2;14337:13;14321:30;:::i;:::-;14385:19;;;14457:14;;;;14429:4;14420:14;;;14494;14483:26;;14480:46;;;14522:1;14519;14512:12;14480:46;14546:5;14560:353;14576:6;14571:3;14568:15;14560:353;;;14662:3;14649:17;14698:2;14685:11;14682:19;14679:109;;;14742:1;14771:2;14767;14760:14;14679:109;14813:57;14855:14;14841:11;14834:5;14830:23;14813:57;:::i;:::-;14801:70;;-1:-1:-1;14891:12:14;;;;14593;;14560:353;;;-1:-1:-1;14935:5:14;14008:938;-1:-1:-1;;;;;;;14008:938:14:o;14951:437::-;15030:1;15026:12;;;;15073;;;15094:61;;15148:4;15140:6;15136:17;15126:27;;15094:61;15201:2;15193:6;15190:14;15170:18;15167:38;15164:218;;15238:77;15235:1;15228:88;15339:4;15336:1;15329:15;15367:4;15364:1;15357:15;16035:542;16136:2;16131:3;16128:11;16125:446;;;16172:1;16196:5;16193:1;16186:16;16240:4;16237:1;16227:18;16310:2;16298:10;16294:19;16291:1;16287:27;16281:4;16277:38;16346:4;16334:10;16331:20;16328:47;;;-1:-1:-1;16369:4:14;16328:47;16424:2;16419:3;16415:12;16412:1;16408:20;16402:4;16398:31;16388:41;;16479:82;16497:2;16490:5;16487:13;16479:82;;;16542:17;;;16523:1;16512:13;16479:82;;;16483:3;;;16035:542;;;:::o;16813:1460::-;16937:3;16931:10;16964:18;16956:6;16953:30;16950:56;;;16986:18;;:::i;:::-;17015:96;17104:6;17064:38;17096:4;17090:11;17064:38;:::i;:::-;17058:4;17015:96;:::i;:::-;17166:4;;17223:2;17212:14;;17240:1;17235:781;;;;18060:1;18077:6;18074:89;;;-1:-1:-1;18129:19:14;;;18123:26;18074:89;16719:66;16710:1;16706:11;;;16702:84;16698:89;16688:100;16794:1;16790:11;;;16685:117;18176:81;;17205:1062;;17235:781;15982:1;15975:14;;;16019:4;16006:18;;17283:66;17271:79;;;17447:236;17461:7;17458:1;17455:14;17447:236;;;17550:19;;;17544:26;17529:42;;17642:27;;;;17610:1;17598:14;;;;17477:19;;17447:236;;;17451:3;17711:6;17702:7;17699:19;17696:261;;;17772:19;;;17766:26;17873:66;17855:1;17851:14;;;17867:3;17847:24;17843:97;17839:102;17824:118;17809:134;;17696:261;-1:-1:-1;;;;;18003:1:14;17987:14;;;17983:22;17970:36;;-1:-1:-1;16813:1460:14:o;18278:826::-;18371:6;18424:2;18412:9;18403:7;18399:23;18395:32;18392:52;;;18440:1;18437;18430:12;18392:52;18480:9;18467:23;18509:18;18550:2;18542:6;18539:14;18536:34;;;18566:1;18563;18556:12;18536:34;18589:22;;;;18645:4;18627:16;;;18623:27;18620:47;;;18663:1;18660;18653:12;18620:47;18696:4;18690:11;18740:4;18732:6;18728:17;18795:6;18783:10;18780:22;18775:2;18763:10;18760:18;18757:46;18754:72;;;18806:18;;:::i;:::-;18842:4;18835:24;18883:16;;18868:32;;18946:2;18938:11;;18925:25;18962:16;;;18959:36;;;18991:1;18988;18981:12;18959:36;19028:44;19064:7;19053:8;19049:2;19045:17;19028:44;:::i;:::-;19023:2;19011:15;;19004:69;-1:-1:-1;19015:6:14;18278:826;-1:-1:-1;;;;;18278:826:14:o;19109:357::-;19227:12;;19274:4;19263:16;;;19257:23;;19227:12;19292:16;;19289:171;;;19382:66;19366:4;19362:17;;;;19359:1;19355:25;19351:98;19340:110;;19109:357;-1:-1:-1;19109:357:14:o;19471:245::-;19550:6;19558;19611:2;19599:9;19590:7;19586:23;19582:32;19579:52;;;19627:1;19624;19617:12;19579:52;-1:-1:-1;;19650:16:14;;19706:2;19691:18;;;19685:25;19650:16;;19685:25;;-1:-1:-1;19471:245:14:o;19721:441::-;19774:5;19827:3;19820:4;19812:6;19808:17;19804:27;19794:55;;19845:1;19842;19835:12;19794:55;19874:6;19868:13;19905:48;19921:31;19949:2;19921:31;:::i;19905:48::-;19978:2;19969:7;19962:19;20024:3;20017:4;20012:2;20004:6;20000:15;19996:26;19993:35;19990:55;;;20041:1;20038;20031:12;19990:55;20054:77;20128:2;20121:4;20112:7;20108:18;20101:4;20093:6;20089:17;20054:77;:::i;20167:1005::-;20267:6;20320:2;20308:9;20299:7;20295:23;20291:32;20288:52;;;20336:1;20333;20326:12;20288:52;20369:9;20363:16;20398:18;20439:2;20431:6;20428:14;20425:34;;;20455:1;20452;20445:12;20425:34;20478:22;;;;20534:4;20516:16;;;20512:27;20509:47;;;20552:1;20549;20542:12;20509:47;20578:22;;:::i;:::-;20631:2;20625:9;20659:2;20649:8;20646:16;20643:36;;;20675:1;20672;20665:12;20643:36;20702:55;20749:7;20738:8;20734:2;20730:17;20702:55;:::i;:::-;20695:5;20688:70;;20797:2;20793;20789:11;20783:18;20826:2;20816:8;20813:16;20810:36;;;20842:1;20839;20832:12;20810:36;20878:55;20925:7;20914:8;20910:2;20906:17;20878:55;:::i;:::-;20873:2;20866:5;20862:14;20855:79;;20980:2;20976;20972:11;20966:18;20961:2;20954:5;20950:14;20943:42;21031:2;21027;21023:11;21017:18;21012:2;21005:5;21001:14;20994:42;21083:3;21079:2;21075:12;21069:19;21063:3;21056:5;21052:15;21045:44;21136:3;21132:2;21128:12;21122:19;21116:3;21109:5;21105:15;21098:44;21161:5;21151:15;;;;;20167:1005;;;;:::o;21177:271::-;21360:6;21352;21347:3;21334:33;21316:3;21386:16;;21411:13;;;21386:16;21177:271;-1:-1:-1;21177:271:14:o;21453:325::-;21541:6;21536:3;21529:19;21593:6;21586:5;21579:4;21574:3;21570:14;21557:43;;21645:1;21638:4;21629:6;21624:3;21620:16;21616:27;21609:38;21511:3;21767:4;21697:66;21692:2;21684:6;21680:15;21676:88;21671:3;21667:98;21663:109;21656:116;;21453:325;;;;:::o;21783:476::-;22014:6;22003:9;21996:25;22057:2;22052;22041:9;22037:18;22030:30;21977:4;22083:61;22140:2;22129:9;22125:18;22117:6;22109;22083:61;:::i;:::-;22192:9;22184:6;22180:22;22175:2;22164:9;22160:18;22153:50;22220:33;22246:6;22238;22220:33;:::i;:::-;22212:41;21783:476;-1:-1:-1;;;;;;;21783:476:14:o;22264:244::-;22421:2;22410:9;22403:21;22384:4;22441:61;22498:2;22487:9;22483:18;22475:6;22467;22441:61;:::i;23151:274::-;23191:1;23217;23207:189;;23252:77;23249:1;23242:88;23353:4;23350:1;23343:15;23381:4;23378:1;23371:15;23207:189;-1:-1:-1;23410:9:14;;23151:274::o;23430:125::-;23495:9;;;23516:10;;;23513:36;;;23529:18;;:::i;23560:693::-;23902:66;23897:3;23890:79;23872:3;23998:6;23992:13;24014:75;24082:6;24077:2;24072:3;24068:12;24061:4;24053:6;24049:17;24014:75;:::i;:::-;24153:66;24148:2;24108:16;;;;24140:11;;;24133:87;-1:-1:-1;24244:2:14;24236:11;;23560:693;-1:-1:-1;23560:693:14:o;24258:184::-;24328:6;24381:2;24369:9;24360:7;24356:23;24352:32;24349:52;;;24397:1;24394;24387:12;24349:52;-1:-1:-1;24420:16:14;;24258:184;-1:-1:-1;24258:184:14:o;24447:383::-;24604:3;24642:6;24636:13;24658:66;24717:6;24712:3;24705:4;24697:6;24693:17;24658:66;:::i;:::-;24746:16;;;;24771:21;;;-1:-1:-1;24819:4:14;24808:16;;24447:383;-1:-1:-1;24447:383:14:o;26369:168::-;26442:9;;;26473;;26490:15;;;26484:22;;26470:37;26460:71;;26511:18;;:::i
Swarm Source
ipfs://8c2ea73863e242a1e00858d0d525af430fe3045920a71b632ca3e728ddc8817d
Loading...
Loading
Loading...
Loading
Net Worth in USD
$3.85
Net Worth in ETH
0.001956
Token Allocations
ETH
100.00%
FRAX
0.00%
Multichain Portfolio | 33 Chains
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.