{
    "Function": "slitherConstructorVariables",
    "File": "contracts/wallet/QuickAccManager.sol",
    "Parent Contracts": [],
    "High-Level Calls": [],
    "Internal Calls": [
        "keccak256(bytes)",
        "keccak256(bytes)",
        "keccak256(bytes)"
    ],
    "Library Calls": [],
    "Low-Level Calls": [],
    "Code": "contract QuickAccManager {\n\t// Note: nonces are scoped by identity rather than by accHash - the reason for this is that there's no reason to scope them by accHash,\n\t// we merely need them for replay protection\n\tmapping (address => uint) nonces;\n\tmapping (bytes32 => uint) scheduled;\n\n\tbytes4 immutable CANCEL_PREFIX = 0xc47c3100;\n\n\t// Events\n\t// we only need those for timelocked stuff so we can show scheduled txns to the user; the oens that get executed immediately do not need logs\n\tevent LogScheduled(bytes32 indexed txnHash, bytes32 indexed accHash, address indexed signer, uint nonce, uint time, Identity.Transaction[] txns);\n\tevent LogCancelled(bytes32 indexed txnHash, bytes32 indexed accHash, address indexed signer, uint time);\n\tevent LogExecScheduled(bytes32 indexed txnHash, bytes32 indexed accHash, uint time);\n\n\t// EIP 2612\n\tbytes32 public DOMAIN_SEPARATOR;\n\tconstructor() {\n\t\tDOMAIN_SEPARATOR = keccak256(\n\t\t\tabi.encode(\n\t\t\t\tkeccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),\n\t\t\t\t// @TODO: maybe we should use a more user friendly name here?\n\t\t\t\tkeccak256(bytes('QuickAccManager')),\n\t\t\t\tkeccak256(bytes('1')),\n\t\t\t\tblock.chainid,\n\t\t\t\taddress(this)\n\t\t\t)\n\t\t);\n\t}\n\n\tstruct QuickAccount {\n\t\tuint timelock;\n\t\taddress one;\n\t\taddress two;\n\t\t// We decided to not allow certain options here such as ability to skip the second sig for send(), but leaving this a struct rather than a tuple\n\t\t// for clarity and to ensure it's future proof\n\t}\n\tstruct DualSig {\n\t\tbool isBothSigned;\n\t\tbytes one;\n\t\tbytes two;\n\t}\n\n\t// NOTE: a single accHash can control multiple identities, as long as those identities set it's hash in privileges[address(this)]\n\t// this is by design\n\n\t// isBothSigned is hashed in so that we don't allow signatures from two-sig txns to be reused for single sig txns,\n\t// ...potentially frontrunning a normal two-sig transaction and making it wait\n\t// WARNING: if the signature of this is changed, we have to change IdentityFactory\n\tfunction send(Identity identity, QuickAccount calldata acc, DualSig calldata sigs, Identity.Transaction[] calldata txns) external {\n\t\tbytes32 accHash = keccak256(abi.encode(acc));\n\t\trequire(identity.privileges(address(this)) == accHash, 'WRONG_ACC_OR_NO_PRIV');\n\t\tuint initialNonce = nonces[address(identity)];\n\t\t// Security: we must also hash in the hash of the QuickAccount, otherwise the sig of one key can be reused across multiple accs\n\t\tbytes32 hash = keccak256(abi.encode(\n\t\t\taddress(this),\n\t\t\tblock.chainid,\n\t\t\taccHash,\n\t\t\tnonces[address(identity)]++,\n\t\t\ttxns,\n\t\t\tsigs.isBothSigned\n\t\t));\n\t\tif (sigs.isBothSigned) {\n\t\t\trequire(acc.one == SignatureValidator.recoverAddr(hash, sigs.one), 'SIG_ONE');\n\t\t\trequire(acc.two == SignatureValidator.recoverAddr(hash, sigs.two), 'SIG_TWO');\n\t\t\tidentity.executeBySender(txns);\n\t\t} else {\n\t\t\taddress signer = SignatureValidator.recoverAddr(hash, sigs.one);\n\t\t\trequire(acc.one == signer || acc.two == signer, 'SIG');\n\t\t\t// no need to check whether `scheduled[hash]` is already set here cause of the incrementing nonce\n\t\t\tscheduled[hash] = block.timestamp + acc.timelock;\n\t\t\temit LogScheduled(hash, accHash, signer, initialNonce, block.timestamp, txns);\n\t\t}\n\t}\n\n\tfunction cancel(Identity identity, QuickAccount calldata acc, uint nonce, bytes calldata sig, Identity.Transaction[] calldata txns) external {\n\t\tbytes32 accHash = keccak256(abi.encode(acc));\n\t\trequire(identity.privileges(address(this)) == accHash, 'WRONG_ACC_OR_NO_PRIV');\n\n\t\tbytes32 hash = keccak256(abi.encode(CANCEL_PREFIX, address(this), block.chainid, accHash, nonce, txns, false));\n\t\taddress signer = SignatureValidator.recoverAddr(hash, sig);\n\t\trequire(signer == acc.one || signer == acc.two, 'INVALID_SIGNATURE');\n\n\t\t// @NOTE: should we allow cancelling even when it's matured? probably not, otherwise there's a minor grief\n\t\t// opportunity: someone wants to cancel post-maturity, and you front them with execScheduled\n\t\tbytes32 hashTx = keccak256(abi.encode(address(this), block.chainid, accHash, nonce, txns));\n\t\trequire(scheduled[hashTx] != 0 && block.timestamp < scheduled[hashTx], 'TOO_LATE');\n\t\tdelete scheduled[hashTx];\n\n\t\temit LogCancelled(hashTx, accHash, signer, block.timestamp);\n\t}\n\n\tfunction execScheduled(Identity identity, bytes32 accHash, uint nonce, Identity.Transaction[] calldata txns) external {\n\t\trequire(identity.privileges(address(this)) == accHash, 'WRONG_ACC_OR_NO_PRIV');\n\n\t\tbytes32 hash = keccak256(abi.encode(address(this), block.chainid, accHash, nonce, txns, false));\n\t\trequire(scheduled[hash] != 0 && block.timestamp >= scheduled[hash], 'NOT_TIME');\n\t\tdelete scheduled[hash];\n\t\tidentity.executeBySender(txns);\n\n\t\temit LogExecScheduled(hash, accHash, block.timestamp);\n\t}\n\n\t// EIP 1271 implementation\n\t// see https://eips.ethereum.org/EIPS/eip-1271\n\tfunction isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {\n\t\t(address payable id, uint timelock, bytes memory sig1, bytes memory sig2) = abi.decode(signature, (address, uint, bytes, bytes));\n\t\tbytes32 accHash = keccak256(abi.encode(QuickAccount({\n\t\t\ttimelock: timelock,\n\t\t\tone: SignatureValidator.recoverAddr(hash, sig1),\n\t\t\ttwo: SignatureValidator.recoverAddr(hash, sig2)\n\t\t})));\n\t\tif (Identity(id).privileges(address(this)) == accHash) {\n\t\t\t// bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")\n\t\t\treturn 0x1626ba7e;\n\t\t} else {\n\t\t\treturn 0xffffffff;\n\t\t}\n\t}\n\n\t// EIP 712 methods\n\t// all of the following are 2/2 only\n\tbytes32 private TRANSFER_TYPEHASH = keccak256('Transfer(address tokenAddr,address to,uint256 value,uint256 fee,uint256 nonce)');\n\tstruct Transfer { address token; address to; uint amount; uint fee; }\n\t// WARNING: if the signature of this is changed, we have to change IdentityFactory\n\tfunction sendTransfer(Identity identity, QuickAccount calldata acc, bytes calldata sigOne, bytes calldata sigTwo, Transfer calldata t) external {\n\t\trequire(identity.privileges(address(this)) == keccak256(abi.encode(acc)), 'WRONG_ACC_OR_NO_PRIV');\n\n\t\tbytes32 hash = keccak256(abi.encodePacked(\n\t\t\t'\\x19\\x01',\n\t\t\tDOMAIN_SEPARATOR,\n\t\t\tkeccak256(abi.encode(TRANSFER_TYPEHASH, t.token, t.to, t.amount, t.fee, nonces[address(identity)]++))\n\t\t));\n\t\trequire(acc.one == SignatureValidator.recoverAddr(hash, sigOne), 'SIG_ONE');\n\t\trequire(acc.two == SignatureValidator.recoverAddr(hash, sigTwo), 'SIG_TWO');\n\t\tIdentity.Transaction[] memory txns = new Identity.Transaction[](2);\n\t\ttxns[0].to = t.token;\n\t\ttxns[0].data = abi.encodeWithSelector(IERC20.transfer.selector, t.to, t.amount);\n\t\ttxns[1].to = t.token;\n\t\ttxns[1].data = abi.encodeWithSelector(IERC20.transfer.selector, msg.sender, t.fee);\n\t\tidentity.executeBySender(txns);\n\t}\n\n\t// Reference for arrays: https://github.com/sportx-bet/smart-contracts/blob/e36965a0c4748bf73ae15ed3cab5660c9cf722e1/contracts/impl/trading/EIP712FillHasher.sol\n\t// and https://eips.ethereum.org/EIPS/eip-712\n\t// and for signTypedData_v4: https://gist.github.com/danfinlay/750ce1e165a75e1c3387ec38cf452b71\n\tstruct Txn { string description; address to; uint value; bytes data; }\n\tbytes32 private TXNS_TYPEHASH = keccak256('Txn(string description,address to,uint256 value,bytes data)');\n\tbytes32 private BUNDLE_TYPEHASH = keccak256('Bundle(uint256 nonce,Txn[] transactions)');\n\t// WARNING: if the signature of this is changed, we have to change IdentityFactory\n\tfunction sendTxns(Identity identity, QuickAccount calldata acc, bytes calldata sigOne, bytes calldata sigTwo, Txn[] calldata txns) external {\n\t\trequire(identity.privileges(address(this)) == keccak256(abi.encode(acc)), 'WRONG_ACC_OR_NO_PRIV');\n\n\t\t// hashing + prepping args\n\t\tbytes32[] memory txnBytes = new bytes32[](txns.length);\n\t\tIdentity.Transaction[] memory identityTxns = new Identity.Transaction[](txns.length);\n\t\tfor (uint256 i = 0; i < txns.length; i++) {\n\t\t\ttxnBytes[i] = keccak256(abi.encode(TXNS_TYPEHASH, txns[i].description, txns[i].to, txns[i].value, txns[i].data));\n\t\t\tidentityTxns[i].to = txns[i].to;\n\t\t\tidentityTxns[i].value = txns[i].value;\n\t\t\tidentityTxns[i].data = txns[i].data;\n\t\t}\n\t\tbytes32 txnsHash = keccak256(abi.encodePacked(txnBytes));\n\t\tbytes32 hash = keccak256(abi.encodePacked(\n\t\t\t'\\x19\\x01',\n\t\t\tDOMAIN_SEPARATOR,\n\t\t\tkeccak256(abi.encode(BUNDLE_TYPEHASH, nonces[address(identity)]++, txnsHash))\n\t\t));\n\t\trequire(acc.one == SignatureValidator.recoverAddr(hash, sigOne), 'SIG_ONE');\n\t\trequire(acc.two == SignatureValidator.recoverAddr(hash, sigTwo), 'SIG_TWO');\n\t\tidentity.executeBySender(identityTxns);\n\t}\n\n}"
}