ETH Price: $1,982.41 (-5.05%)

Transaction Decoder

Block:
17510287 at Jun-19-2023 12:40:23 AM +UTC
Transaction Fee:
0.00336897353584395 ETH $6.68
Gas Used:
208,290 Gas / 16.174437255 Gwei

Emitted Events:

58 IdentityFactory.LogDeployed( addr=0x0905FdfA5c250F36655D832f619c645cf123FEEF, salt=1 )
59 KosBlock.Transfer( from=AirDrop, to=0x0905FdfA5c250F36655D832f619c645cf123FEEF, value=777000000000000000000 )
60 AirDrop.Claimed( claimant=0x0905FdfA5c250F36655D832f619c645cf123FEEF )

Account State Difference:

  Address   Before After State Difference Code
0x0905FdfA...cf123FEEF
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 497590261154554171967156398743527686255631923243205565751923174073360754855118748509100196113391971605502963
4.184903021467135678 Eth4.185319601467135678 Eth0.00041658
0x5091cFed...91117087C
0x887D698C...b5da4D347
0x942f9CE5...680230348
(Ambire Wallet: Deployer)
1.135648572913269961 Eth
Nonce: 29941
1.132279599377426011 Eth
Nonce: 29942
0.00336897353584395
0xBf07a0Df...94E2a5BCA
(Ambire Wallet: Identity Factory)
0xfF3f6D14...3E07c26BF

Execution Trace

IdentityFactory.deployAndCall( code=0x7FB9B2E2A0E064AACDDED496D49AD28043896D1E27105B913A708D9804284410F77FA57498058891E98F45F8ABB85DAFBCD30F3D8B3AB586DFAE2E0228BBB1DE7018553D602D80604D3D3981F3363D3D373D3D3D363D732A2B85EB1054D6F0C6C2E37DA05ED3E5FEA684EF5AF43D82803E903D91602B57FD5BF3, salt=1, callee=0xfF3f6D14DF43c112aB98834Ee1F82083E07c26BF, data=0x534255FF0000000000000000000000000905FDFA5C250F36655D832F619C645CF123FEEF000000000000000000000000000000000000000000000000000000000003F480000000000000000000000000E13949DD0C3E142EFB16D97439275BCB90119CEE0000000000000000000000006C87F08B1F8718A8A31BE1D6B03F4C99DF2770C600000000000000000000000000000000000000000000000000000000000000C000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000E000000000000000000000000000000000000000000000000000000000000000424FD9F4AD4B327F5B645B4C27D5D8A8555DFDDD0614A0F1CF53581DC1227D25BB34C21E1BA6788A5CCD9B57E6D21F56BD0960651FC8F37A9B550D45981E7A74EE1C010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042B415342B774111CEF9D4BF2273AFACD6F03B83D0B18B367484ACE52F273524682409F57BC1FBE338234DC3BF7D77AE7EC2A2D68A7FEBD58727D82A5B06D466FC1C0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000887D698CBEE4452AADD7E45391969A5B5DA4D3470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000044E71D92D00000000000000000000000000000000000000000000000000000000 )
  • 0x0905fdfa5c250f36655d832f619c645cf123feef.7fb9b2e2( )
  • QuickAccManager.send( identity=0x0905FdfA5c250F36655D832f619c645cf123FEEF, acc=[{name:timelock, type:uint256, order:1, indexed:false, value:259200, valueString:259200}, {name:one, type:address, order:2, indexed:false, value:0xE13949Dd0c3e142Efb16d97439275bCB90119CEe, valueString:0xE13949Dd0c3e142Efb16d97439275bCB90119CEe}, {name:two, type:address, order:3, indexed:false, value:0x6c87f08B1F8718a8a31BE1d6b03f4C99dF2770C6, valueString:0x6c87f08B1F8718a8a31BE1d6b03f4C99dF2770C6}], sigs=[{name:isBothSigned, type:bool, order:1, indexed:false, value:true, valueString:True}, {name:one, type:bytes, order:2, indexed:false, value:0x4FD9F4AD4B327F5B645B4C27D5D8A8555DFDDD0614A0F1CF53581DC1227D25BB34C21E1BA6788A5CCD9B57E6D21F56BD0960651FC8F37A9B550D45981E7A74EE1C01, valueString:0x4FD9F4AD4B327F5B645B4C27D5D8A8555DFDDD0614A0F1CF53581DC1227D25BB34C21E1BA6788A5CCD9B57E6D21F56BD0960651FC8F37A9B550D45981E7A74EE1C01}, {name:two, type:bytes, order:3, indexed:false, value:0xB415342B774111CEF9D4BF2273AFACD6F03B83D0B18B367484ACE52F273524682409F57BC1FBE338234DC3BF7D77AE7EC2A2D68A7FEBD58727D82A5B06D466FC1C01, valueString:0xB415342B774111CEF9D4BF2273AFACD6F03B83D0B18B367484ACE52F273524682409F57BC1FBE338234DC3BF7D77AE7EC2A2D68A7FEBD58727D82A5B06D466FC1C01}], txns= )
    • 0x0905fdfa5c250f36655d832f619c645cf123feef.c066a5b1( )
      • Identity.privileges( 0xfF3f6D14DF43c112aB98834Ee1F82083E07c26BF ) => ( B9B2E2A0E064AACDDED496D49AD28043896D1E27105B913A708D9804284410F7 )
      • Null: 0x000...001.b9faff10( )
      • Null: 0x000...001.b9faff10( )
      • 0x0905fdfa5c250f36655d832f619c645cf123feef.abc5345e( )
        • Identity.executeBySender( txns= )
          • AirDrop.CALL( )
            • KosBlock.balanceOf( account=0x887D698Cbee4452aaDd7e45391969a5b5da4D347 ) => ( 448336000000000000000000 )
            • KosBlock.transfer( to=0x0905FdfA5c250F36655D832f619c645cf123FEEF, amount=777000000000000000000 ) => ( True )
              File 1 of 5: IdentityFactory
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity 0.8.7;
              
              // @TODO: Formatting
              library LibBytes {
                // @TODO: see if we can just set .length = 
                function trimToSize(bytes memory b, uint newLen)
                  internal
                  pure
                {
                  require(b.length > newLen, "BytesLib: only shrinking");
                  assembly {
                    mstore(b, newLen)
                  }
                }
              
              
                /***********************************|
                |        Read Bytes Functions       |
                |__________________________________*/
              
                /**
                 * @dev Reads a bytes32 value from a position in a byte array.
                 * @param b Byte array containing a bytes32 value.
                 * @param index Index in byte array of bytes32 value.
                 * @return result bytes32 value from byte array.
                 */
                function readBytes32(
                  bytes memory b,
                  uint256 index
                )
                  internal
                  pure
                  returns (bytes32 result)
                {
                  // Arrays are prefixed by a 256 bit length parameter
                  index += 32;
              
                  require(b.length >= index, "BytesLib: length");
              
                  // Read the bytes32 from array memory
                  assembly {
                    result := mload(add(b, index))
                  }
                  return result;
                }
              }
              
              
              
              interface IERC1271Wallet {
              	function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4 magicValue);
              }
              
              library SignatureValidator {
              	using LibBytes for bytes;
              
              	enum SignatureMode {
              		EIP712,
              		EthSign,
              		SmartWallet,
              		Spoof
              	}
              
              	// bytes4(keccak256("isValidSignature(bytes32,bytes)"))
              	bytes4 constant internal ERC1271_MAGICVALUE_BYTES32 = 0x1626ba7e;
              
              	function recoverAddr(bytes32 hash, bytes memory sig) internal view returns (address) {
              		return recoverAddrImpl(hash, sig, false);
              	}
              
              	function recoverAddrImpl(bytes32 hash, bytes memory sig, bool allowSpoofing) internal view returns (address) {
              		require(sig.length >= 1, "SV_SIGLEN");
              		uint8 modeRaw;
              		unchecked { modeRaw = uint8(sig[sig.length - 1]); }
              		SignatureMode mode = SignatureMode(modeRaw);
              
              		// {r}{s}{v}{mode}
              		if (mode == SignatureMode.EIP712 || mode == SignatureMode.EthSign) {
              			require(sig.length == 66, "SV_LEN");
              			bytes32 r = sig.readBytes32(0);
              			bytes32 s = sig.readBytes32(32);
              			uint8 v = uint8(sig[64]);
              			// Hesitant about this check: seems like this is something that has no business being checked on-chain
              			require(v == 27 || v == 28, "SV_INVALID_V");
              			if (mode == SignatureMode.EthSign) hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
              			address signer = ecrecover(hash, v, r, s);
              			require(signer != address(0), "SV_ZERO_SIG");
              			return signer;
              		// {sig}{verifier}{mode}
              		} else if (mode == SignatureMode.SmartWallet) {
              			// 32 bytes for the addr, 1 byte for the type = 33
              			require(sig.length > 33, "SV_LEN_WALLET");
              			uint newLen;
              			unchecked {
              				newLen = sig.length - 33;
              			}
              			IERC1271Wallet wallet = IERC1271Wallet(address(uint160(uint256(sig.readBytes32(newLen)))));
              			sig.trimToSize(newLen);
              			require(ERC1271_MAGICVALUE_BYTES32 == wallet.isValidSignature(hash, sig), "SV_WALLET_INVALID");
              			return address(wallet);
              		// {address}{mode}; the spoof mode is used when simulating calls
              		} else if (mode == SignatureMode.Spoof && allowSpoofing) {
              			require(tx.origin == address(1), "SV_SPOOF_ORIGIN");
              			require(sig.length == 33, "SV_SPOOF_LEN");
              			sig.trimToSize(32);
              			return abi.decode(sig, (address));
              		} else revert("SV_SIGMODE");
              	}
              }
              
              
              contract Identity {
              	mapping (address => bytes32) public privileges;
              	// The next allowed nonce
              	uint public nonce;
              
              	// Events
              	event LogPrivilegeChanged(address indexed addr, bytes32 priv);
              	event LogErr(address indexed to, uint value, bytes data, bytes returnData); // only used in tryCatch
              
              	// Transaction structure
              	// we handle replay protection separately by requiring (address(this), chainID, nonce) as part of the sig
              	struct Transaction {
              		address to;
              		uint value;
              		bytes data;
              	}
              
              	constructor(address[] memory addrs) {
              		uint len = addrs.length;
              		for (uint i=0; i<len; i++) {
              			// @TODO should we allow setting to any arb value here?
              			privileges[addrs[i]] = bytes32(uint(1));
              			emit LogPrivilegeChanged(addrs[i], bytes32(uint(1)));
              		}
              	}
              
              	// This contract can accept ETH without calldata
              	receive() external payable {}
              
              	// This contract can accept ETH with calldata
              	// However, to support EIP 721 and EIP 1155, we need to respond to those methods with their own method signature
              	fallback() external payable {
              		bytes4 method = msg.sig;
              		if (
              			method == 0x150b7a02 // bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
              				|| method == 0xf23a6e61 // bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))
              				|| method == 0xbc197c81 // bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))
              		) {
              			// Copy back the method
              			// solhint-disable-next-line no-inline-assembly
              			assembly {
              				calldatacopy(0, 0, 0x04)
              				return (0, 0x20)
              			}
              		}
              	}
              
              	function setAddrPrivilege(address addr, bytes32 priv)
              		external
              	{
              		require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
              		// Anti-bricking measure: if the privileges slot is used for special data (not 0x01),
              		// don't allow to set it to true
              		if (uint(privileges[addr]) > 1) require(priv != bytes32(uint(1)), 'UNSETTING_SPECIAL_DATA');
              		privileges[addr] = priv;
              		emit LogPrivilegeChanged(addr, priv);
              	}
              
              	function tipMiner(uint amount)
              		external
              	{
              		require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
              		// See https://docs.flashbots.net/flashbots-auction/searchers/advanced/coinbase-payment/#managing-payments-to-coinbaseaddress-when-it-is-a-contract
              		// generally this contract is reentrancy proof cause of the nonce
              		executeCall(block.coinbase, amount, new bytes(0));
              	}
              
              	function tryCatch(address to, uint value, bytes calldata data)
              		external
              	{
              		require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
              		(bool success, bytes memory returnData) = to.call{value: value, gas: gasleft()}(data);
              		if (!success) emit LogErr(to, value, data, returnData);
              	}
              
              
              	// WARNING: if the signature of this is changed, we have to change IdentityFactory
              	function execute(Transaction[] calldata txns, bytes calldata signature)
              		external
              	{
              		require(txns.length > 0, 'MUST_PASS_TX');
              		uint currentNonce = nonce;
              		// NOTE: abi.encode is safer than abi.encodePacked in terms of collision safety
              		bytes32 hash = keccak256(abi.encode(address(this), block.chainid, currentNonce, txns));
              		// We have to increment before execution cause it protects from reentrancies
              		nonce = currentNonce + 1;
              
              		address signer = SignatureValidator.recoverAddrImpl(hash, signature, true);
              		require(privileges[signer] != bytes32(0), 'INSUFFICIENT_PRIVILEGE');
              		uint len = txns.length;
              		for (uint i=0; i<len; i++) {
              			Transaction memory txn = txns[i];
              			executeCall(txn.to, txn.value, txn.data);
              		}
              		// The actual anti-bricking mechanism - do not allow a signer to drop their own priviledges
              		require(privileges[signer] != bytes32(0), 'PRIVILEGE_NOT_DOWNGRADED');
              	}
              
              	// no need for nonce management here cause we're not dealing with sigs
              	function executeBySender(Transaction[] calldata txns) external {
              		require(txns.length > 0, 'MUST_PASS_TX');
              		require(privileges[msg.sender] != bytes32(0), 'INSUFFICIENT_PRIVILEGE');
              		uint len = txns.length;
              		for (uint i=0; i<len; i++) {
              			Transaction memory txn = txns[i];
              			executeCall(txn.to, txn.value, txn.data);
              		}
              		// again, anti-bricking
              		require(privileges[msg.sender] != bytes32(0), 'PRIVILEGE_NOT_DOWNGRADED');
              	}
              
              	function executeBySelf(Transaction[] calldata txns) external {
              		require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
              		require(txns.length > 0, 'MUST_PASS_TX');
              		uint len = txns.length;
              		for (uint i=0; i<len; i++) {
              			Transaction memory txn = txns[i];
              			executeCall(txn.to, txn.value, txn.data);
              		}
              	}
              
              	// we shouldn't use address.call(), cause: https://github.com/ethereum/solidity/issues/2884
              	// copied from https://github.com/uport-project/uport-identity/blob/develop/contracts/Proxy.sol
              	// there's also
              	// https://github.com/gnosis/MultiSigWallet/commit/e1b25e8632ca28e9e9e09c81bd20bf33fdb405ce
              	// https://github.com/austintgriffith/bouncer-proxy/blob/master/BouncerProxy/BouncerProxy.sol
              	// https://github.com/gnosis/safe-contracts/blob/7e2eeb3328bb2ae85c36bc11ea6afc14baeb663c/contracts/base/Executor.sol
              	function executeCall(address to, uint256 value, bytes memory data)
              		internal
              	{
              		assembly {
              			let result := call(gas(), to, value, add(data, 0x20), mload(data), 0, 0)
              
              			switch result case 0 {
              				let size := returndatasize()
              				let ptr := mload(0x40)
              				returndatacopy(ptr, 0, size)
              				revert(ptr, size)
              			}
              			default {}
              		}
              		// A single call consumes around 477 more gas with the pure solidity version, for whatever reason
              		// WARNING: do not use this, it corrupts the returnData string (returns it in a slightly different format)
              		//(bool success, bytes memory returnData) = to.call{value: value, gas: gasleft()}(data);
              		//if (!success) revert(string(data));
              	}
              
              	// EIP 1271 implementation
              	// see https://eips.ethereum.org/EIPS/eip-1271
              	function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {
              		if (privileges[SignatureValidator.recoverAddr(hash, signature)] != bytes32(0)) {
              			// bytes4(keccak256("isValidSignature(bytes32,bytes)")
              			return 0x1626ba7e;
              		} else {
              			return 0xffffffff;
              		}
              	}
              
              	// EIP 1155 implementation
              	// we pretty much only need to signal that we support the interface for 165, but for 1155 we also need the fallback function
              	function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
              		return
              			interfaceID == 0x01ffc9a7 ||    // ERC-165 support (i.e. `bytes4(keccak256('supportsInterface(bytes4)'))`).
              			interfaceID == 0x4e2312e0;      // ERC-1155 `ERC1155TokenReceiver` support (i.e. `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`).
              	}
              }
              
              interface IERC20 {
                  function totalSupply() external view returns (uint256);
                  function balanceOf(address account) external view returns (uint256);
                  function transfer(address recipient, uint256 amount) external returns (bool);
                  function allowance(address owner, address spender) external view returns (uint256);
                  function approve(address spender, uint256 amount) external returns (bool);
                  function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              
              
              contract IdentityFactory {
              	event LogDeployed(address addr, uint256 salt);
              
              	address public immutable allowedToDrain;
              	constructor(address allowed) {
              		allowedToDrain = allowed;
              	}
              
              	function deploy(bytes calldata code, uint256 salt) external {
              		deploySafe(code, salt);
              	}
              
              	// When the relayer needs to act upon an /identity/:addr/submit call, it'll either call execute on the Identity directly
              	// if it's already deployed, or call `deployAndExecute` if the account is still counterfactual
              	// we can't have deployAndExecuteBySender, because the sender will be the factory
              	function deployAndExecute(
              		bytes calldata code, uint256 salt,
              		Identity.Transaction[] calldata txns, bytes calldata signature
              	) external {
              		address payable addr = payable(deploySafe(code, salt));
              		Identity(addr).execute(txns, signature);
              	}
              	// but for the quick accounts we need this
              	function deployAndCall(bytes calldata code, uint256 salt, address callee, bytes calldata data) external {
              		deploySafe(code, salt);
              		require(data.length > 4, 'DATA_LEN');
              		bytes4 method;
              		// solium-disable-next-line security/no-inline-assembly
              		assembly {
              			// can also do shl(224, shr(224, calldataload(0)))
              			method := and(calldataload(data.offset), 0xffffffff00000000000000000000000000000000000000000000000000000000)
              		}
              		require(
              			method == 0x6171d1c9 // execute((address,uint256,bytes)[],bytes)
              			|| method == 0x534255ff // send(address,(uint256,address,address),(bool,bytes,bytes),(address,uint256,bytes)[])
              			|| method == 0x4b776c6d // sendTransfer(address,(uint256,address,address),(bytes,bytes),(address,address,uint256,uint256))
              			|| method == 0x63486689 // sendTxns(address,(uint256,address,address),(bytes,bytes),(string,address,uint256,bytes)[])
              		, 'INVALID_METHOD');
              
              		assembly {
              			let dataPtr := mload(0x40)
              			calldatacopy(dataPtr, data.offset, data.length)
              			let result := call(gas(), callee, 0, dataPtr, data.length, 0, 0)
              
              			switch result case 0 {
              				let size := returndatasize()
              				let ptr := mload(0x40)
              				returndatacopy(ptr, 0, size)
              				revert(ptr, size)
              			}
              			default {}
              		}
              	}
              
              
              	// Withdraw the earnings from various fees (deploy fees and execute fees earned cause of `deployAndExecute`)
              	// although we do not use this since we no longer receive fees on the factory, it's good to have this for safety
              	// In practice, we (almost) never receive fees on the factory, but there's one exception: QuickAccManager EIP 712 methods (sendTransfer) + deployAndCall
              	function withdraw(IERC20 token, address to, uint256 tokenAmount) external {
              		require(msg.sender == allowedToDrain, 'ONLY_AUTHORIZED');
              		token.transfer(to, tokenAmount);
              	}
              
              	// This is done to mitigate possible frontruns where, for example, deploying the same code/salt via deploy()
              	// would make a pending deployAndExecute fail
              	// The way we mitigate that is by checking if the contract is already deployed and if so, we continue execution
              	function deploySafe(bytes memory code, uint256 salt) internal returns (address) {
              		address expectedAddr = address(uint160(uint256(
              			keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, keccak256(code)))
              		)));
              		uint size;
              		assembly { size := extcodesize(expectedAddr) }
              		// If there is code at that address, we can assume it's the one we were about to deploy,
              		// because of how CREATE2 and keccak256 works
              		if (size == 0) {
              			address addr;
              			assembly { addr := create2(0, add(code, 0x20), mload(code), salt) }
              			require(addr != address(0), 'FAILED_DEPLOYING');
              			require(addr == expectedAddr, 'FAILED_MATCH');
              			emit LogDeployed(addr, salt);
              		}
              		return expectedAddr;
              	}
              }

              File 2 of 5: AirDrop
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.6;
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import "@openzeppelin/contracts/access/Ownable.sol";
              contract AirDrop is Ownable {
                  IERC20 public token;
                  uint256 public constant CLAIM_AMOUNT = 777 * 10**18;
                  mapping(address => bool) public claimants;
                  event Claimed(address claimant);
                  constructor() {
                      token = IERC20(0x5091cFed6b48a3CA20f2f845a0BEeD791117087C);
                  }
                  function claim() external {
                      require(!claimants[msg.sender], "Claimant has already claimed tokens");
                      require(token.balanceOf(address(this)) >= CLAIM_AMOUNT, "Not enough tokens left");
                      claimants[msg.sender] = true; 
                      token.transfer(msg.sender, CLAIM_AMOUNT);
                      emit Claimed(msg.sender);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
              pragma solidity ^0.8.0;
              import "../utils/Context.sol";
              /**
               * @dev Contract module which provides a basic access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * By default, the owner account will be the one that deploys the contract. This
               * can later be changed with {transferOwnership}.
               *
               * This module is used through inheritance. It will make available the modifier
               * `onlyOwner`, which can be applied to your functions to restrict their use to
               * the owner.
               */
              abstract contract Ownable is Context {
                  address private _owner;
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the deployer as the initial owner.
                   */
                  constructor() {
                      _transferOwnership(_msgSender());
                  }
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      _checkOwner();
                      _;
                  }
                  /**
                   * @dev Returns the address of the current owner.
                   */
                  function owner() public view virtual returns (address) {
                      return _owner;
                  }
                  /**
                   * @dev Throws if the sender is not the owner.
                   */
                  function _checkOwner() internal view virtual {
                      require(owner() == _msgSender(), "Ownable: caller is not the owner");
                  }
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby disabling any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public virtual onlyOwner {
                      _transferOwnership(address(0));
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                      _transferOwnership(newOwner);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Internal function without access restriction.
                   */
                  function _transferOwnership(address newOwner) internal virtual {
                      address oldOwner = _owner;
                      _owner = newOwner;
                      emit OwnershipTransferred(oldOwner, newOwner);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 amount) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
                  /**
                   * @dev Moves `amount` tokens from `from` to `to` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 amount) external returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              

              File 3 of 5: KosBlock
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.9;
              import "@openzeppelin/contracts@4.9.1/token/ERC20/ERC20.sol";
              contract KosBlock is ERC20 {
                  constructor() ERC20("Kos Block", "KOS") {
                      _mint(msg.sender, 777777777 * 10 ** decimals());
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
              pragma solidity ^0.8.0;
              import "./IERC20.sol";
              import "./extensions/IERC20Metadata.sol";
              import "../../utils/Context.sol";
              /**
               * @dev Implementation of the {IERC20} interface.
               *
               * This implementation is agnostic to the way tokens are created. This means
               * that a supply mechanism has to be added in a derived contract using {_mint}.
               * For a generic mechanism see {ERC20PresetMinterPauser}.
               *
               * TIP: For a detailed writeup see our guide
               * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
               * to implement supply mechanisms].
               *
               * The default value of {decimals} is 18. To change this, you should override
               * this function so it returns a different value.
               *
               * We have followed general OpenZeppelin Contracts guidelines: functions revert
               * instead returning `false` on failure. This behavior is nonetheless
               * conventional and does not conflict with the expectations of ERC20
               * applications.
               *
               * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
               * This allows applications to reconstruct the allowance for all accounts just
               * by listening to said events. Other implementations of the EIP may not emit
               * these events, as it isn't required by the specification.
               *
               * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
               * functions have been added to mitigate the well-known issues around setting
               * allowances. See {IERC20-approve}.
               */
              contract ERC20 is Context, IERC20, IERC20Metadata {
                  mapping(address => uint256) private _balances;
                  mapping(address => mapping(address => uint256)) private _allowances;
                  uint256 private _totalSupply;
                  string private _name;
                  string private _symbol;
                  /**
                   * @dev Sets the values for {name} and {symbol}.
                   *
                   * All two of these values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor(string memory name_, string memory symbol_) {
                      _name = name_;
                      _symbol = symbol_;
                  }
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev Returns the symbol of the token, usually a shorter version of the
                   * name.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev Returns the number of decimals used to get its user representation.
                   * For example, if `decimals` equals `2`, a balance of `505` tokens should
                   * be displayed to a user as `5.05` (`505 / 10 ** 2`).
                   *
                   * Tokens usually opt for a value of 18, imitating the relationship between
                   * Ether and Wei. This is the default value returned by this function, unless
                   * it's overridden.
                   *
                   * NOTE: This information is only used for _display_ purposes: it in
                   * no way affects any of the arithmetic of the contract, including
                   * {IERC20-balanceOf} and {IERC20-transfer}.
                   */
                  function decimals() public view virtual override returns (uint8) {
                      return 18;
                  }
                  /**
                   * @dev See {IERC20-totalSupply}.
                   */
                  function totalSupply() public view virtual override returns (uint256) {
                      return _totalSupply;
                  }
                  /**
                   * @dev See {IERC20-balanceOf}.
                   */
                  function balanceOf(address account) public view virtual override returns (uint256) {
                      return _balances[account];
                  }
                  /**
                   * @dev See {IERC20-transfer}.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - the caller must have a balance of at least `amount`.
                   */
                  function transfer(address to, uint256 amount) public virtual override returns (bool) {
                      address owner = _msgSender();
                      _transfer(owner, to, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-allowance}.
                   */
                  function allowance(address owner, address spender) public view virtual override returns (uint256) {
                      return _allowances[owner][spender];
                  }
                  /**
                   * @dev See {IERC20-approve}.
                   *
                   * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
                   * `transferFrom`. This is semantically equivalent to an infinite approval.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function approve(address spender, uint256 amount) public virtual override returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-transferFrom}.
                   *
                   * Emits an {Approval} event indicating the updated allowance. This is not
                   * required by the EIP. See the note at the beginning of {ERC20}.
                   *
                   * NOTE: Does not update the allowance if the current allowance
                   * is the maximum `uint256`.
                   *
                   * Requirements:
                   *
                   * - `from` and `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   * - the caller must have allowance for ``from``'s tokens of at least
                   * `amount`.
                   */
                  function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
                      address spender = _msgSender();
                      _spendAllowance(from, spender, amount);
                      _transfer(from, to, amount);
                      return true;
                  }
                  /**
                   * @dev Atomically increases the allowance granted to `spender` by the caller.
                   *
                   * This is an alternative to {approve} that can be used as a mitigation for
                   * problems described in {IERC20-approve}.
                   *
                   * Emits an {Approval} event indicating the updated allowance.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, allowance(owner, spender) + addedValue);
                      return true;
                  }
                  /**
                   * @dev Atomically decreases the allowance granted to `spender` by the caller.
                   *
                   * This is an alternative to {approve} that can be used as a mitigation for
                   * problems described in {IERC20-approve}.
                   *
                   * Emits an {Approval} event indicating the updated allowance.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `spender` must have allowance for the caller of at least
                   * `subtractedValue`.
                   */
                  function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                      address owner = _msgSender();
                      uint256 currentAllowance = allowance(owner, spender);
                      require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                      unchecked {
                          _approve(owner, spender, currentAllowance - subtractedValue);
                      }
                      return true;
                  }
                  /**
                   * @dev Moves `amount` of tokens from `from` to `to`.
                   *
                   * This internal function is equivalent to {transfer}, and can be used to
                   * e.g. implement automatic token fees, slashing mechanisms, etc.
                   *
                   * Emits a {Transfer} event.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   */
                  function _transfer(address from, address to, uint256 amount) internal virtual {
                      require(from != address(0), "ERC20: transfer from the zero address");
                      require(to != address(0), "ERC20: transfer to the zero address");
                      _beforeTokenTransfer(from, to, amount);
                      uint256 fromBalance = _balances[from];
                      require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                      unchecked {
                          _balances[from] = fromBalance - amount;
                          // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                          // decrementing then incrementing.
                          _balances[to] += amount;
                      }
                      emit Transfer(from, to, amount);
                      _afterTokenTransfer(from, to, amount);
                  }
                  /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                   * the total supply.
                   *
                   * Emits a {Transfer} event with `from` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   */
                  function _mint(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: mint to the zero address");
                      _beforeTokenTransfer(address(0), account, amount);
                      _totalSupply += amount;
                      unchecked {
                          // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                          _balances[account] += amount;
                      }
                      emit Transfer(address(0), account, amount);
                      _afterTokenTransfer(address(0), account, amount);
                  }
                  /**
                   * @dev Destroys `amount` tokens from `account`, reducing the
                   * total supply.
                   *
                   * Emits a {Transfer} event with `to` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   * - `account` must have at least `amount` tokens.
                   */
                  function _burn(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: burn from the zero address");
                      _beforeTokenTransfer(account, address(0), amount);
                      uint256 accountBalance = _balances[account];
                      require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                      unchecked {
                          _balances[account] = accountBalance - amount;
                          // Overflow not possible: amount <= accountBalance <= totalSupply.
                          _totalSupply -= amount;
                      }
                      emit Transfer(account, address(0), amount);
                      _afterTokenTransfer(account, address(0), amount);
                  }
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                   *
                   * This internal function is equivalent to `approve`, and can be used to
                   * e.g. set automatic allowances for certain subsystems, etc.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `owner` cannot be the zero address.
                   * - `spender` cannot be the zero address.
                   */
                  function _approve(address owner, address spender, uint256 amount) internal virtual {
                      require(owner != address(0), "ERC20: approve from the zero address");
                      require(spender != address(0), "ERC20: approve to the zero address");
                      _allowances[owner][spender] = amount;
                      emit Approval(owner, spender, amount);
                  }
                  /**
                   * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
                   *
                   * Does not update the allowance amount in case of infinite allowance.
                   * Revert if not enough allowance is available.
                   *
                   * Might emit an {Approval} event.
                   */
                  function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
                      uint256 currentAllowance = allowance(owner, spender);
                      if (currentAllowance != type(uint256).max) {
                          require(currentAllowance >= amount, "ERC20: insufficient allowance");
                          unchecked {
                              _approve(owner, spender, currentAllowance - amount);
                          }
                      }
                  }
                  /**
                   * @dev Hook that is called before any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * will be transferred to `to`.
                   * - when `from` is zero, `amount` tokens will be minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                  /**
                   * @dev Hook that is called after any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * has been transferred to `to`.
                   * - when `from` is zero, `amount` tokens have been minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
              pragma solidity ^0.8.0;
              import "../IERC20.sol";
              /**
               * @dev Interface for the optional metadata functions from the ERC20 standard.
               *
               * _Available since v4.1._
               */
              interface IERC20Metadata is IERC20 {
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the symbol of the token.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the decimals places of the token.
                   */
                  function decimals() external view returns (uint8);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 amount) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
                  /**
                   * @dev Moves `amount` tokens from `from` to `to` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 amount) external returns (bool);
              }
              

              File 4 of 5: QuickAccManager
              pragma solidity 0.8.7;
              
              // @TODO: Formatting
              library LibBytes {
                // @TODO: see if we can just set .length = 
                function trimToSize(bytes memory b, uint newLen)
                  internal
                  pure
                {
                  require(b.length > newLen, "BytesLib: only shrinking");
                  assembly {
                    mstore(b, newLen)
                  }
                }
              
              
                /***********************************|
                |        Read Bytes Functions       |
                |__________________________________*/
              
                /**
                 * @dev Reads a bytes32 value from a position in a byte array.
                 * @param b Byte array containing a bytes32 value.
                 * @param index Index in byte array of bytes32 value.
                 * @return result bytes32 value from byte array.
                 */
                function readBytes32(
                  bytes memory b,
                  uint256 index
                )
                  internal
                  pure
                  returns (bytes32 result)
                {
                  // Arrays are prefixed by a 256 bit length parameter
                  index += 32;
              
                  require(b.length >= index, "BytesLib: length");
              
                  // Read the bytes32 from array memory
                  assembly {
                    result := mload(add(b, index))
                  }
                  return result;
                }
              }
              
              
              
              interface IERC1271Wallet {
              	function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4 magicValue);
              }
              
              library SignatureValidator {
              	using LibBytes for bytes;
              
              	enum SignatureMode {
              		EIP712,
              		EthSign,
              		SmartWallet,
              		Spoof
              	}
              
              	// bytes4(keccak256("isValidSignature(bytes32,bytes)"))
              	bytes4 constant internal ERC1271_MAGICVALUE_BYTES32 = 0x1626ba7e;
              
              	function recoverAddr(bytes32 hash, bytes memory sig) internal view returns (address) {
              		return recoverAddrImpl(hash, sig, false);
              	}
              
              	function recoverAddrImpl(bytes32 hash, bytes memory sig, bool allowSpoofing) internal view returns (address) {
              		require(sig.length >= 1, "SV_SIGLEN");
              		uint8 modeRaw;
              		unchecked { modeRaw = uint8(sig[sig.length - 1]); }
              		SignatureMode mode = SignatureMode(modeRaw);
              
              		// {r}{s}{v}{mode}
              		if (mode == SignatureMode.EIP712 || mode == SignatureMode.EthSign) {
              			require(sig.length == 66, "SV_LEN");
              			bytes32 r = sig.readBytes32(0);
              			bytes32 s = sig.readBytes32(32);
              			uint8 v = uint8(sig[64]);
              			// Hesitant about this check: seems like this is something that has no business being checked on-chain
              			require(v == 27 || v == 28, "SV_INVALID_V");
              			if (mode == SignatureMode.EthSign) hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
              			address signer = ecrecover(hash, v, r, s);
              			require(signer != address(0), "SV_ZERO_SIG");
              			return signer;
              		// {sig}{verifier}{mode}
              		} else if (mode == SignatureMode.SmartWallet) {
              			// 32 bytes for the addr, 1 byte for the type = 33
              			require(sig.length > 33, "SV_LEN_WALLET");
              			uint newLen;
              			unchecked {
              				newLen = sig.length - 33;
              			}
              			IERC1271Wallet wallet = IERC1271Wallet(address(uint160(uint256(sig.readBytes32(newLen)))));
              			sig.trimToSize(newLen);
              			require(ERC1271_MAGICVALUE_BYTES32 == wallet.isValidSignature(hash, sig), "SV_WALLET_INVALID");
              			return address(wallet);
              		// {address}{mode}; the spoof mode is used when simulating calls
              		} else if (mode == SignatureMode.Spoof && allowSpoofing) {
              			require(tx.origin == address(1), "SV_SPOOF_ORIGIN");
              			require(sig.length == 33, "SV_SPOOF_LEN");
              			sig.trimToSize(32);
              			return abi.decode(sig, (address));
              		} else revert("SV_SIGMODE");
              	}
              }
              
              
              contract Identity {
              	mapping (address => bytes32) public privileges;
              	// The next allowed nonce
              	uint public nonce;
              
              	// Events
              	event LogPrivilegeChanged(address indexed addr, bytes32 priv);
              	event LogErr(address indexed to, uint value, bytes data, bytes returnData); // only used in tryCatch
              
              	// Transaction structure
              	// we handle replay protection separately by requiring (address(this), chainID, nonce) as part of the sig
              	struct Transaction {
              		address to;
              		uint value;
              		bytes data;
              	}
              
              	constructor(address[] memory addrs) {
              		uint len = addrs.length;
              		for (uint i=0; i<len; i++) {
              			// @TODO should we allow setting to any arb value here?
              			privileges[addrs[i]] = bytes32(uint(1));
              			emit LogPrivilegeChanged(addrs[i], bytes32(uint(1)));
              		}
              	}
              
              	// This contract can accept ETH without calldata
              	receive() external payable {}
              
              	// This contract can accept ETH with calldata
              	// However, to support EIP 721 and EIP 1155, we need to respond to those methods with their own method signature
              	fallback() external payable {
              		bytes4 method = msg.sig;
              		if (
              			method == 0x150b7a02 // bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
              				|| method == 0xf23a6e61 // bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))
              				|| method == 0xbc197c81 // bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))
              		) {
              			// Copy back the method
              			// solhint-disable-next-line no-inline-assembly
              			assembly {
              				calldatacopy(0, 0, 0x04)
              				return (0, 0x20)
              			}
              		}
              	}
              
              	function setAddrPrivilege(address addr, bytes32 priv)
              		external
              	{
              		require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
              		// Anti-bricking measure: if the privileges slot is used for special data (not 0x01),
              		// don't allow to set it to true
              		if (uint(privileges[addr]) > 1) require(priv != bytes32(uint(1)), 'UNSETTING_SPECIAL_DATA');
              		privileges[addr] = priv;
              		emit LogPrivilegeChanged(addr, priv);
              	}
              
              	function tipMiner(uint amount)
              		external
              	{
              		require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
              		// See https://docs.flashbots.net/flashbots-auction/searchers/advanced/coinbase-payment/#managing-payments-to-coinbaseaddress-when-it-is-a-contract
              		// generally this contract is reentrancy proof cause of the nonce
              		executeCall(block.coinbase, amount, new bytes(0));
              	}
              
              	function tryCatch(address to, uint value, bytes calldata data)
              		external
              	{
              		require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
              		(bool success, bytes memory returnData) = to.call{value: value, gas: gasleft()}(data);
              		if (!success) emit LogErr(to, value, data, returnData);
              	}
              
              
              	// WARNING: if the signature of this is changed, we have to change IdentityFactory
              	function execute(Transaction[] calldata txns, bytes calldata signature)
              		external
              	{
              		require(txns.length > 0, 'MUST_PASS_TX');
              		uint currentNonce = nonce;
              		// NOTE: abi.encode is safer than abi.encodePacked in terms of collision safety
              		bytes32 hash = keccak256(abi.encode(address(this), block.chainid, currentNonce, txns));
              		// We have to increment before execution cause it protects from reentrancies
              		nonce = currentNonce + 1;
              
              		address signer = SignatureValidator.recoverAddrImpl(hash, signature, true);
              		require(privileges[signer] != bytes32(0), 'INSUFFICIENT_PRIVILEGE');
              		uint len = txns.length;
              		for (uint i=0; i<len; i++) {
              			Transaction memory txn = txns[i];
              			executeCall(txn.to, txn.value, txn.data);
              		}
              		// The actual anti-bricking mechanism - do not allow a signer to drop their own priviledges
              		require(privileges[signer] != bytes32(0), 'PRIVILEGE_NOT_DOWNGRADED');
              	}
              
              	// no need for nonce management here cause we're not dealing with sigs
              	function executeBySender(Transaction[] calldata txns) external {
              		require(txns.length > 0, 'MUST_PASS_TX');
              		require(privileges[msg.sender] != bytes32(0), 'INSUFFICIENT_PRIVILEGE');
              		uint len = txns.length;
              		for (uint i=0; i<len; i++) {
              			Transaction memory txn = txns[i];
              			executeCall(txn.to, txn.value, txn.data);
              		}
              		// again, anti-bricking
              		require(privileges[msg.sender] != bytes32(0), 'PRIVILEGE_NOT_DOWNGRADED');
              	}
              
              	function executeBySelf(Transaction[] calldata txns) external {
              		require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
              		require(txns.length > 0, 'MUST_PASS_TX');
              		uint len = txns.length;
              		for (uint i=0; i<len; i++) {
              			Transaction memory txn = txns[i];
              			executeCall(txn.to, txn.value, txn.data);
              		}
              	}
              
              	// we shouldn't use address.call(), cause: https://github.com/ethereum/solidity/issues/2884
              	// copied from https://github.com/uport-project/uport-identity/blob/develop/contracts/Proxy.sol
              	// there's also
              	// https://github.com/gnosis/MultiSigWallet/commit/e1b25e8632ca28e9e9e09c81bd20bf33fdb405ce
              	// https://github.com/austintgriffith/bouncer-proxy/blob/master/BouncerProxy/BouncerProxy.sol
              	// https://github.com/gnosis/safe-contracts/blob/7e2eeb3328bb2ae85c36bc11ea6afc14baeb663c/contracts/base/Executor.sol
              	function executeCall(address to, uint256 value, bytes memory data)
              		internal
              	{
              		assembly {
              			let result := call(gas(), to, value, add(data, 0x20), mload(data), 0, 0)
              
              			switch result case 0 {
              				let size := returndatasize()
              				let ptr := mload(0x40)
              				returndatacopy(ptr, 0, size)
              				revert(ptr, size)
              			}
              			default {}
              		}
              		// A single call consumes around 477 more gas with the pure solidity version, for whatever reason
              		// WARNING: do not use this, it corrupts the returnData string (returns it in a slightly different format)
              		//(bool success, bytes memory returnData) = to.call{value: value, gas: gasleft()}(data);
              		//if (!success) revert(string(data));
              	}
              
              	// EIP 1271 implementation
              	// see https://eips.ethereum.org/EIPS/eip-1271
              	function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {
              		if (privileges[SignatureValidator.recoverAddr(hash, signature)] != bytes32(0)) {
              			// bytes4(keccak256("isValidSignature(bytes32,bytes)")
              			return 0x1626ba7e;
              		} else {
              			return 0xffffffff;
              		}
              	}
              
              	// EIP 1155 implementation
              	// we pretty much only need to signal that we support the interface for 165, but for 1155 we also need the fallback function
              	function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
              		return
              			interfaceID == 0x01ffc9a7 ||    // ERC-165 support (i.e. `bytes4(keccak256('supportsInterface(bytes4)'))`).
              			interfaceID == 0x4e2312e0;      // ERC-1155 `ERC1155TokenReceiver` support (i.e. `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`).
              	}
              }
              
              interface IERC20 {
                  function totalSupply() external view returns (uint256);
                  function balanceOf(address account) external view returns (uint256);
                  function transfer(address recipient, uint256 amount) external returns (bool);
                  function allowance(address owner, address spender) external view returns (uint256);
                  function approve(address spender, uint256 amount) external returns (bool);
                  function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              
              
              contract QuickAccManager {
              	// 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,
              	// we merely need them for replay protection
              	mapping (address => uint) public nonces;
              	mapping (bytes32 => uint) public scheduled;
              
              	bytes4 constant CANCEL_PREFIX = 0xc47c3100;
              
              	// Events
              	// 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
              	event LogScheduled(bytes32 indexed txnHash, bytes32 indexed accHash, address indexed signer, uint nonce, uint time, Identity.Transaction[] txns);
              	event LogCancelled(bytes32 indexed txnHash, bytes32 indexed accHash, address indexed signer, uint time);
              	event LogExecScheduled(bytes32 indexed txnHash, bytes32 indexed accHash, uint time);
              
              	// EIP 2612
              	/// @notice Chain Id at this contract's deployment.
              	uint256 internal immutable DOMAIN_SEPARATOR_CHAIN_ID;
              	/// @notice EIP-712 typehash for this contract's domain at deployment.
              	bytes32 internal immutable _DOMAIN_SEPARATOR;
              
              	constructor() {
              		DOMAIN_SEPARATOR_CHAIN_ID = block.chainid;
              		_DOMAIN_SEPARATOR = calculateDomainSeparator();
              	}
              
              	struct QuickAccount {
              		uint timelock;
              		address one;
              		address two;
              		// 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
              		// for clarity and to ensure it's future proof
              	}
              	struct DualSig {
              		bool isBothSigned;
              		bytes one;
              		bytes two;
              	}
              
              	// NOTE: a single accHash can control multiple identities, as long as those identities set it's hash in privileges[address(this)]
              	// this is by design
              
              	// isBothSigned is hashed in so that we don't allow signatures from two-sig txns to be reused for single sig txns,
              	// ...potentially frontrunning a normal two-sig transaction and making it wait
              	// WARNING: if the signature of this is changed, we have to change IdentityFactory
              	function send(Identity identity, QuickAccount calldata acc, DualSig calldata sigs, Identity.Transaction[] calldata txns) external {
              		bytes32 accHash = keccak256(abi.encode(acc));
              		require(identity.privileges(address(this)) == accHash, 'WRONG_ACC_OR_NO_PRIV');
              		uint initialNonce = nonces[address(identity)]++;
              		// Security: we must also hash in the hash of the QuickAccount, otherwise the sig of one key can be reused across multiple accs
              		bytes32 hash = keccak256(abi.encode(
              			address(this),
              			block.chainid,
              			address(identity),
              			accHash,
              			initialNonce,
              			txns,
              			sigs.isBothSigned
              		));
              		if (sigs.isBothSigned) {
              			require(acc.one == SignatureValidator.recoverAddr(hash, sigs.one), 'SIG_ONE');
              			require(acc.two == SignatureValidator.recoverAddr(hash, sigs.two), 'SIG_TWO');
              			identity.executeBySender(txns);
              		} else {
              			address signer = SignatureValidator.recoverAddr(hash, sigs.one);
              			require(acc.one == signer || acc.two == signer, 'SIG');
              			// no need to check whether `scheduled[hash]` is already set here cause of the incrementing nonce
              			scheduled[hash] = block.timestamp + acc.timelock;
              			emit LogScheduled(hash, accHash, signer, initialNonce, block.timestamp, txns);
              		}
              	}
              
              	function cancel(Identity identity, QuickAccount calldata acc, uint nonce, bytes calldata sig, Identity.Transaction[] calldata txns) external {
              		bytes32 accHash = keccak256(abi.encode(acc));
              		require(identity.privileges(address(this)) == accHash, 'WRONG_ACC_OR_NO_PRIV');
              
              		bytes32 hash = keccak256(abi.encode(CANCEL_PREFIX, address(this), block.chainid, address(identity), accHash, nonce, txns, false));
              		address signer = SignatureValidator.recoverAddr(hash, sig);
              		require(signer == acc.one || signer == acc.two, 'INVALID_SIGNATURE');
              
              		// @NOTE: should we allow cancelling even when it's matured? probably not, otherwise there's a minor grief
              		// opportunity: someone wants to cancel post-maturity, and you front them with execScheduled
              		bytes32 hashTx = keccak256(abi.encode(address(this), block.chainid, accHash, nonce, txns, false));
              		uint scheduledTime = scheduled[hashTx];
              		require(scheduledTime != 0 && block.timestamp < scheduledTime, 'TOO_LATE');
              		delete scheduled[hashTx];
              
              		emit LogCancelled(hashTx, accHash, signer, block.timestamp);
              	}
              
              	function execScheduled(Identity identity, bytes32 accHash, uint nonce, Identity.Transaction[] calldata txns) external {
              		require(identity.privileges(address(this)) == accHash, 'WRONG_ACC_OR_NO_PRIV');
              
              		bytes32 hash = keccak256(abi.encode(address(this), block.chainid, address(identity), accHash, nonce, txns, false));
              		uint scheduledTime = scheduled[hash];
              		require(scheduledTime != 0 && block.timestamp >= scheduledTime, 'NOT_TIME');
              
              		delete scheduled[hash];
              		identity.executeBySender(txns);
              
              		emit LogExecScheduled(hash, accHash, block.timestamp);
              	}
              
              	// EIP 1271 implementation
              	// see https://eips.ethereum.org/EIPS/eip-1271
              	// NOTE: this method is not intended to be called from off-chain eth_calls; technically it's not a clean EIP 1271
              	// ...implementation, because EIP1271 assumes every smart wallet implements that method, while this contract is not a smart wallet
              	function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {
              		(uint timelock, bytes memory sig1, bytes memory sig2) = abi.decode(signature, (uint, bytes, bytes));
              		bytes32 accHash = keccak256(abi.encode(QuickAccount({
              			timelock: timelock,
              			one: SignatureValidator.recoverAddr(hash, sig1),
              			two: SignatureValidator.recoverAddr(hash, sig2)
              		})));
              		if (Identity(payable(address(msg.sender))).privileges(address(this)) == accHash) {
              			// bytes4(keccak256("isValidSignature(bytes32,bytes)")
              			return 0x1626ba7e;
              		} else {
              			return 0xffffffff;
              		}
              	}
              
              
              	// EIP 712 methods
              	// all of the following are 2/2 only
              	struct DualSigAlwaysBoth {
              		bytes one;
              		bytes two;
              	}
              
              	function calculateDomainSeparator() internal view returns (bytes32) {
              		return keccak256(
              			abi.encode(
              				keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
              				// @TODO: maybe we should use a more user friendly name here?
              				keccak256(bytes('QuickAccManager')),
              				keccak256(bytes('1')),
              				block.chainid,
              				address(this)
              			)
              		);
              	}
              
              	/// @notice EIP-712 typehash for this contract's domain.
              	function DOMAIN_SEPARATOR() public view returns (bytes32) {
              		return block.chainid == DOMAIN_SEPARATOR_CHAIN_ID ? _DOMAIN_SEPARATOR : calculateDomainSeparator();
              	}
              
              	bytes32 private constant TRANSFER_TYPEHASH = keccak256('Transfer(address tokenAddr,address to,uint256 value,uint256 fee,address identity,uint256 nonce)');
              	struct Transfer { address token; address to; uint amount; uint fee; }
              	// WARNING: if the signature of this is changed, we have to change IdentityFactory
              	function sendTransfer(Identity identity, QuickAccount calldata acc, DualSigAlwaysBoth calldata sigs, Transfer calldata t) external {
              		require(identity.privileges(address(this)) == keccak256(abi.encode(acc)), 'WRONG_ACC_OR_NO_PRIV');
              
              		bytes32 hash = keccak256(abi.encodePacked(
              			'\x19\x01',
              			DOMAIN_SEPARATOR(),
              			keccak256(abi.encode(TRANSFER_TYPEHASH, t.token, t.to, t.amount, t.fee, address(identity), nonces[address(identity)]++))
              		));
              		require(acc.one == SignatureValidator.recoverAddr(hash, sigs.one), 'SIG_ONE');
              		require(acc.two == SignatureValidator.recoverAddr(hash, sigs.two), 'SIG_TWO');
              		Identity.Transaction[] memory txns = new Identity.Transaction[](2);
              		txns[0].to = t.token;
              		txns[0].data = abi.encodeWithSelector(IERC20.transfer.selector, t.to, t.amount);
              		txns[1].to = t.token;
              		txns[1].data = abi.encodeWithSelector(IERC20.transfer.selector, msg.sender, t.fee);
              		identity.executeBySender(txns);
              	}
              
              	// Reference for arrays: https://github.com/sportx-bet/smart-contracts/blob/e36965a0c4748bf73ae15ed3cab5660c9cf722e1/contracts/impl/trading/EIP712FillHasher.sol
              	// and https://eips.ethereum.org/EIPS/eip-712
              	// and for signTypedData_v4: https://gist.github.com/danfinlay/750ce1e165a75e1c3387ec38cf452b71
              	struct Txn { string description; address to; uint value; bytes data; }
              	bytes32 private constant TXNS_TYPEHASH = keccak256('Txn(string description,address to,uint256 value,bytes data)');
              	bytes32 private constant BUNDLE_TYPEHASH = keccak256('Bundle(address identity,uint256 nonce,Txn[] transactions)');
              	// WARNING: if the signature of this is changed, we have to change IdentityFactory
              	function sendTxns(Identity identity, QuickAccount calldata acc, DualSigAlwaysBoth calldata sigs, Txn[] calldata txns) external {
              		require(identity.privileges(address(this)) == keccak256(abi.encode(acc)), 'WRONG_ACC_OR_NO_PRIV');
              
              		// hashing + prepping args
              		bytes32[] memory txnBytes = new bytes32[](txns.length);
              		Identity.Transaction[] memory identityTxns = new Identity.Transaction[](txns.length);
              		uint txnLen = txns.length;
              		for (uint256 i = 0; i < txnLen; i++) {
              			txnBytes[i] = keccak256(abi.encode(TXNS_TYPEHASH, txns[i].description, txns[i].to, txns[i].value, txns[i].data));
              			identityTxns[i].to = txns[i].to;
              			identityTxns[i].value = txns[i].value;
              			identityTxns[i].data = txns[i].data;
              		}
              		bytes32 txnsHash = keccak256(abi.encodePacked(txnBytes));
              		bytes32 hash = keccak256(abi.encodePacked(
              			'\x19\x01',
              			DOMAIN_SEPARATOR(),
              			keccak256(abi.encode(BUNDLE_TYPEHASH, address(identity), nonces[address(identity)]++, txnsHash))
              		));
              		require(acc.one == SignatureValidator.recoverAddr(hash, sigs.one), 'SIG_ONE');
              		require(acc.two == SignatureValidator.recoverAddr(hash, sigs.two), 'SIG_TWO');
              		identity.executeBySender(identityTxns);
              	}
              
              }

              File 5 of 5: Identity
              pragma solidity 0.8.7;
              
              // @TODO: Formatting
              library LibBytes {
                // @TODO: see if we can just set .length = 
                function trimToSize(bytes memory b, uint newLen)
                  internal
                  pure
                {
                  require(b.length > newLen, "BytesLib: only shrinking");
                  assembly {
                    mstore(b, newLen)
                  }
                }
              
              
                /***********************************|
                |        Read Bytes Functions       |
                |__________________________________*/
              
                /**
                 * @dev Reads a bytes32 value from a position in a byte array.
                 * @param b Byte array containing a bytes32 value.
                 * @param index Index in byte array of bytes32 value.
                 * @return result bytes32 value from byte array.
                 */
                function readBytes32(
                  bytes memory b,
                  uint256 index
                )
                  internal
                  pure
                  returns (bytes32 result)
                {
                  // Arrays are prefixed by a 256 bit length parameter
                  index += 32;
              
                  require(b.length >= index, "BytesLib: length");
              
                  // Read the bytes32 from array memory
                  assembly {
                    result := mload(add(b, index))
                  }
                  return result;
                }
              }
              
              
              
              interface IERC1271Wallet {
              	function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4 magicValue);
              }
              
              library SignatureValidator {
              	using LibBytes for bytes;
              
              	enum SignatureMode {
              		EIP712,
              		EthSign,
              		SmartWallet,
              		Spoof
              	}
              
              	// bytes4(keccak256("isValidSignature(bytes32,bytes)"))
              	bytes4 constant internal ERC1271_MAGICVALUE_BYTES32 = 0x1626ba7e;
              
              	function recoverAddr(bytes32 hash, bytes memory sig) internal view returns (address) {
              		return recoverAddrImpl(hash, sig, false);
              	}
              
              	function recoverAddrImpl(bytes32 hash, bytes memory sig, bool allowSpoofing) internal view returns (address) {
              		require(sig.length >= 1, "SV_SIGLEN");
              		uint8 modeRaw;
              		unchecked { modeRaw = uint8(sig[sig.length - 1]); }
              		SignatureMode mode = SignatureMode(modeRaw);
              
              		// {r}{s}{v}{mode}
              		if (mode == SignatureMode.EIP712 || mode == SignatureMode.EthSign) {
              			require(sig.length == 66, "SV_LEN");
              			bytes32 r = sig.readBytes32(0);
              			bytes32 s = sig.readBytes32(32);
              			uint8 v = uint8(sig[64]);
              			// Hesitant about this check: seems like this is something that has no business being checked on-chain
              			require(v == 27 || v == 28, "SV_INVALID_V");
              			if (mode == SignatureMode.EthSign) hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
              			address signer = ecrecover(hash, v, r, s);
              			require(signer != address(0), "SV_ZERO_SIG");
              			return signer;
              		// {sig}{verifier}{mode}
              		} else if (mode == SignatureMode.SmartWallet) {
              			// 32 bytes for the addr, 1 byte for the type = 33
              			require(sig.length > 33, "SV_LEN_WALLET");
              			uint newLen;
              			unchecked {
              				newLen = sig.length - 33;
              			}
              			IERC1271Wallet wallet = IERC1271Wallet(address(uint160(uint256(sig.readBytes32(newLen)))));
              			sig.trimToSize(newLen);
              			require(ERC1271_MAGICVALUE_BYTES32 == wallet.isValidSignature(hash, sig), "SV_WALLET_INVALID");
              			return address(wallet);
              		// {address}{mode}; the spoof mode is used when simulating calls
              		} else if (mode == SignatureMode.Spoof && allowSpoofing) {
              			require(tx.origin == address(1), "SV_SPOOF_ORIGIN");
              			require(sig.length == 33, "SV_SPOOF_LEN");
              			sig.trimToSize(32);
              			return abi.decode(sig, (address));
              		} else revert("SV_SIGMODE");
              	}
              }
              
              
              contract Identity {
              	mapping (address => bytes32) public privileges;
              	// The next allowed nonce
              	uint public nonce;
              
              	// Events
              	event LogPrivilegeChanged(address indexed addr, bytes32 priv);
              	event LogErr(address indexed to, uint value, bytes data, bytes returnData); // only used in tryCatch
              
              	// Transaction structure
              	// we handle replay protection separately by requiring (address(this), chainID, nonce) as part of the sig
              	struct Transaction {
              		address to;
              		uint value;
              		bytes data;
              	}
              
              	constructor(address[] memory addrs) {
              		uint len = addrs.length;
              		for (uint i=0; i<len; i++) {
              			// @TODO should we allow setting to any arb value here?
              			privileges[addrs[i]] = bytes32(uint(1));
              			emit LogPrivilegeChanged(addrs[i], bytes32(uint(1)));
              		}
              	}
              
              	// This contract can accept ETH without calldata
              	receive() external payable {}
              
              	// This contract can accept ETH with calldata
              	// However, to support EIP 721 and EIP 1155, we need to respond to those methods with their own method signature
              	fallback() external payable {
              		bytes4 method = msg.sig;
              		if (
              			method == 0x150b7a02 // bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
              				|| method == 0xf23a6e61 // bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))
              				|| method == 0xbc197c81 // bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))
              		) {
              			// Copy back the method
              			// solhint-disable-next-line no-inline-assembly
              			assembly {
              				calldatacopy(0, 0, 0x04)
              				return (0, 0x20)
              			}
              		}
              	}
              
              	function setAddrPrivilege(address addr, bytes32 priv)
              		external
              	{
              		require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
              		// Anti-bricking measure: if the privileges slot is used for special data (not 0x01),
              		// don't allow to set it to true
              		if (uint(privileges[addr]) > 1) require(priv != bytes32(uint(1)), 'UNSETTING_SPECIAL_DATA');
              		privileges[addr] = priv;
              		emit LogPrivilegeChanged(addr, priv);
              	}
              
              	function tipMiner(uint amount)
              		external
              	{
              		require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
              		// See https://docs.flashbots.net/flashbots-auction/searchers/advanced/coinbase-payment/#managing-payments-to-coinbaseaddress-when-it-is-a-contract
              		// generally this contract is reentrancy proof cause of the nonce
              		executeCall(block.coinbase, amount, new bytes(0));
              	}
              
              	function tryCatch(address to, uint value, bytes calldata data)
              		external
              	{
              		require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
              		(bool success, bytes memory returnData) = to.call{value: value, gas: gasleft()}(data);
              		if (!success) emit LogErr(to, value, data, returnData);
              	}
              
              
              	// WARNING: if the signature of this is changed, we have to change IdentityFactory
              	function execute(Transaction[] calldata txns, bytes calldata signature)
              		external
              	{
              		require(txns.length > 0, 'MUST_PASS_TX');
              		uint currentNonce = nonce;
              		// NOTE: abi.encode is safer than abi.encodePacked in terms of collision safety
              		bytes32 hash = keccak256(abi.encode(address(this), block.chainid, currentNonce, txns));
              		// We have to increment before execution cause it protects from reentrancies
              		nonce = currentNonce + 1;
              
              		address signer = SignatureValidator.recoverAddrImpl(hash, signature, true);
              		require(privileges[signer] != bytes32(0), 'INSUFFICIENT_PRIVILEGE');
              		uint len = txns.length;
              		for (uint i=0; i<len; i++) {
              			Transaction memory txn = txns[i];
              			executeCall(txn.to, txn.value, txn.data);
              		}
              		// The actual anti-bricking mechanism - do not allow a signer to drop their own priviledges
              		require(privileges[signer] != bytes32(0), 'PRIVILEGE_NOT_DOWNGRADED');
              	}
              
              	// no need for nonce management here cause we're not dealing with sigs
              	function executeBySender(Transaction[] calldata txns) external {
              		require(txns.length > 0, 'MUST_PASS_TX');
              		require(privileges[msg.sender] != bytes32(0), 'INSUFFICIENT_PRIVILEGE');
              		uint len = txns.length;
              		for (uint i=0; i<len; i++) {
              			Transaction memory txn = txns[i];
              			executeCall(txn.to, txn.value, txn.data);
              		}
              		// again, anti-bricking
              		require(privileges[msg.sender] != bytes32(0), 'PRIVILEGE_NOT_DOWNGRADED');
              	}
              
              	function executeBySelf(Transaction[] calldata txns) external {
              		require(msg.sender == address(this), 'ONLY_IDENTITY_CAN_CALL');
              		require(txns.length > 0, 'MUST_PASS_TX');
              		uint len = txns.length;
              		for (uint i=0; i<len; i++) {
              			Transaction memory txn = txns[i];
              			executeCall(txn.to, txn.value, txn.data);
              		}
              	}
              
              	// we shouldn't use address.call(), cause: https://github.com/ethereum/solidity/issues/2884
              	// copied from https://github.com/uport-project/uport-identity/blob/develop/contracts/Proxy.sol
              	// there's also
              	// https://github.com/gnosis/MultiSigWallet/commit/e1b25e8632ca28e9e9e09c81bd20bf33fdb405ce
              	// https://github.com/austintgriffith/bouncer-proxy/blob/master/BouncerProxy/BouncerProxy.sol
              	// https://github.com/gnosis/safe-contracts/blob/7e2eeb3328bb2ae85c36bc11ea6afc14baeb663c/contracts/base/Executor.sol
              	function executeCall(address to, uint256 value, bytes memory data)
              		internal
              	{
              		assembly {
              			let result := call(gas(), to, value, add(data, 0x20), mload(data), 0, 0)
              
              			switch result case 0 {
              				let size := returndatasize()
              				let ptr := mload(0x40)
              				returndatacopy(ptr, 0, size)
              				revert(ptr, size)
              			}
              			default {}
              		}
              		// A single call consumes around 477 more gas with the pure solidity version, for whatever reason
              		// WARNING: do not use this, it corrupts the returnData string (returns it in a slightly different format)
              		//(bool success, bytes memory returnData) = to.call{value: value, gas: gasleft()}(data);
              		//if (!success) revert(string(data));
              	}
              
              	// EIP 1271 implementation
              	// see https://eips.ethereum.org/EIPS/eip-1271
              	function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {
              		if (privileges[SignatureValidator.recoverAddr(hash, signature)] != bytes32(0)) {
              			// bytes4(keccak256("isValidSignature(bytes32,bytes)")
              			return 0x1626ba7e;
              		} else {
              			return 0xffffffff;
              		}
              	}
              
              	// EIP 1155 implementation
              	// we pretty much only need to signal that we support the interface for 165, but for 1155 we also need the fallback function
              	function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
              		return
              			interfaceID == 0x01ffc9a7 ||    // ERC-165 support (i.e. `bytes4(keccak256('supportsInterface(bytes4)'))`).
              			interfaceID == 0x4e2312e0;      // ERC-1155 `ERC1155TokenReceiver` support (i.e. `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`).
              	}
              }