'use strict'; var ethers = require('ethers'); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol */ function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); var _, done = false; for (var i = decorators.length - 1; i >= 0; i--) { var context = {}; for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; for (var p in contextIn.access) context.access[p] = contextIn.access[p]; context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); if (kind === "accessor") { if (result === void 0) continue; if (result === null || typeof result !== "object") throw new TypeError("Object expected"); if (_ = accept(result.get)) descriptor.get = _; if (_ = accept(result.set)) descriptor.set = _; if (_ = accept(result.init)) initializers.unshift(_); } else if (_ = accept(result)) { if (kind === "field") initializers.unshift(_); else descriptor[key] = _; } } if (target) Object.defineProperty(target, contextIn.name, descriptor); done = true; } function __runInitializers(thisArg, initializers, value) { var useValue = arguments.length > 2; for (var i = 0; i < initializers.length; i++) { value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); } return useValue ? value : void 0; } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; exports.TWErrorCodes = void 0; (function (TWErrorCodes) { TWErrorCodes["CALL_EXCEPTION"] = "CALL_EXCEPTION"; TWErrorCodes["CONFIG_ERROR"] = "CONFIG_ERROR"; TWErrorCodes["WALLET_NOT_OWNED"] = "WALLET_NOT_OWNED"; TWErrorCodes["WALLET_NOT_READY"] = "WALLET_NOT_READY"; TWErrorCodes["MODULE_ALREADY_INSTALLED"] = "MODULE_ALREADY_INSTALLED"; TWErrorCodes["MODULE_NOT_INSTALLED"] = "MODULE_NOT_INSTALLED"; TWErrorCodes["MODULE_NOT_SUPPORTED"] = "MODULE_NOT_SUPPORTED"; TWErrorCodes["INVALID_SIGNER_TYPE"] = "INVALID_SIGNER_TYPE"; TWErrorCodes["JWT_INIT_SIGNER_ERROR"] = "JWT_INIT_SIGNER_ERROR"; TWErrorCodes["JWT_SIGNER_INVALID_JWT"] = "JWT_SIGNER_INVALID_JWT"; TWErrorCodes["JWT_SIGNER_INVALID_KEY"] = "JWT_SIGNER_INVALID_KEY"; TWErrorCodes["JWT_SIGNER_LIMIT"] = "JWT_SIGNER_LIMIT"; TWErrorCodes["JWT_SIGNER_INITIALIZED"] = "JWT_SIGNER_INITIALIZED"; })(exports.TWErrorCodes || (exports.TWErrorCodes = {})); const Modules = { SecurityControlModule: '0x559103Ecd6cA2a0b92c973a7783dd83B9d7980ee', SocialRecoveryModule: '0x929BAF181bFE97F59ecc22c3EFd33c0D9334380F', }; const SmartContracts = { Factory: '0x5137F38ACa8572638E031710A806944480540271', Entrypoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789' }; const SocialRecoveryModuleAbi = [{ "inputs": [], "name": "AddressAlreadyExists", "type": "error" }, { "inputs": [], "name": "CallerMustBeModule", "type": "error" }, { "inputs": [], "name": "InvalidAddress", "type": "error" }, { "inputs": [], "name": "ModuleAddressEmpty", "type": "error" }, { "inputs": [], "name": "ModuleAuthorized", "type": "error" }, { "inputs": [], "name": "ModuleExecuteFromModuleRecursive", "type": "error" }, { "inputs": [], "name": "ModuleNotAuthorized", "type": "error" }, { "inputs": [], "name": "ModuleNotSupportInterface", "type": "error" }, { "inputs": [], "name": "ModuleSelectorsEmpty", "type": "error" }, { "inputs": [], "name": "SocialRecovery__AnonymousGuardianConfigError", "type": "error" }, { "inputs": [], "name": "SocialRecovery__AnonymousGuardianNotRevealed", "type": "error" }, { "inputs": [], "name": "SocialRecovery__InvalidGuardianHash", "type": "error" }, { "inputs": [], "name": "SocialRecovery__InvalidGuardianList", "type": "error" }, { "inputs": [], "name": "SocialRecovery__InvalidThreshold", "type": "error" }, { "inputs": [], "name": "SocialRecovery__NoOngoingRecovery", "type": "error" }, { "inputs": [], "name": "SocialRecovery__NoPendingGuardian", "type": "error" }, { "inputs": [], "name": "SocialRecovery__NotEnoughApprovals", "type": "error" }, { "inputs": [], "name": "SocialRecovery__OnchainGuardianConfigError", "type": "error" }, { "inputs": [], "name": "SocialRecovery__OngoingRecovery", "type": "error" }, { "inputs": [], "name": "SocialRecovery__OnlyWalletItselfCanCancelRecovery", "type": "error" }, { "inputs": [], "name": "SocialRecovery__OwnersEmpty", "type": "error" }, { "inputs": [], "name": "SocialRecovery__RecoveryPeriodStillPending", "type": "error" }, { "inputs": [], "name": "SocialRecovery__Unauthorized", "type": "error" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "wallet", "type": "address" }, { "indexed": true, "internalType": "address[]", "name": "guardians", "type": "address[]" }, { "indexed": false, "internalType": "bytes32", "name": "guardianHash", "type": "bytes32" }], "name": "AnonymousGuardianRevealed", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "wallet", "type": "address" }, { "indexed": true, "internalType": "address", "name": "guardian", "type": "address" }, { "indexed": true, "internalType": "bytes32", "name": "recoveryHash", "type": "bytes32" }], "name": "ApproveRecovery", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "wallet", "type": "address" }, { "indexed": true, "internalType": "address[]", "name": "newOwners", "type": "address[]" }, { "indexed": false, "internalType": "uint256", "name": "signatureCount", "type": "uint256" }, { "indexed": false, "internalType": "bytes", "name": "signatures", "type": "bytes" }, { "indexed": true, "internalType": "bytes32", "name": "recoveryHash", "type": "bytes32" }], "name": "BatchApproveRecovery", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "wallet", "type": "address" }], "name": "ModuleDeInit", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "wallet", "type": "address" }], "name": "ModuleInit", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "wallet", "type": "address" }, { "indexed": true, "internalType": "address[]", "name": "newOwners", "type": "address[]" }, { "indexed": false, "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "executeAfter", "type": "uint256" }], "name": "PendingRecovery", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "wallet", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "nonce", "type": "uint256" }], "name": "SocialRecoveryCanceled", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "wallet", "type": "address" }, { "indexed": true, "internalType": "address[]", "name": "newOwners", "type": "address[]" }], "name": "SocialRecoveryExecuted", "type": "event" }, { "inputs": [], "name": "NAME", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "VERSION", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }, { "internalType": "address[]", "name": "_newOwners", "type": "address[]" }, { "internalType": "uint256", "name": "_pendingUntil", "type": "uint256" }], "name": "approveRecovery", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }, { "internalType": "address[]", "name": "_newOwners", "type": "address[]" }, { "internalType": "uint256", "name": "_signatureCount", "type": "uint256" }, { "internalType": "bytes", "name": "_signatures", "type": "bytes" }, { "internalType": "uint256", "name": "_pendingUntil", "type": "uint256" }], "name": "batchApproveRecovery", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], "name": "cancelRecovery", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], "name": "cancelSetGuardians", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }, { "internalType": "bytes32", "name": "_dataHash", "type": "bytes32" }, { "internalType": "uint256", "name": "_signatureCount", "type": "uint256" }, { "internalType": "bytes", "name": "_signatures", "type": "bytes" }], "name": "checkNSignatures", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "domainSeparator", "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }, { "internalType": "address[]", "name": "_newOwners", "type": "address[]" }, { "internalType": "uint256", "name": "_nonce", "type": "uint256" }], "name": "encodeSocialRecoveryData", "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], "name": "executeRecovery", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address[]", "name": "_guardians", "type": "address[]" }, { "internalType": "bytes32", "name": "_salt", "type": "bytes32" }], "name": "getAnonymousGuardianHash", "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], "stateMutability": "pure", "type": "function" }, { "inputs": [], "name": "getChainId", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], "name": "getGuardians", "outputs": [{ "internalType": "address[]", "name": "", "type": "address[]" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], "name": "getGuardiansHash", "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }, { "internalType": "address[]", "name": "_newOwners", "type": "address[]" }], "name": "getRecoveryApprovals", "outputs": [{ "internalType": "uint256", "name": "approvalCount", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], "name": "getRecoveryEntry", "outputs": [{ "components": [{ "internalType": "address[]", "name": "newOwners", "type": "address[]" }, { "internalType": "uint256", "name": "executeAfter", "type": "uint256" }, { "internalType": "uint256", "name": "nonce", "type": "uint256" }], "internalType": "struct RecoveryEntry", "name": "", "type": "tuple" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }, { "internalType": "address[]", "name": "_newOwners", "type": "address[]" }, { "internalType": "uint256", "name": "_nonce", "type": "uint256" }], "name": "getSocialRecoveryHash", "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], "name": "guardiansCount", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_guardian", "type": "address" }, { "internalType": "address", "name": "_wallet", "type": "address" }, { "internalType": "address[]", "name": "_newOwners", "type": "address[]" }], "name": "hasGuardianApproved", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }, { "internalType": "address", "name": "_guardian", "type": "address" }], "name": "isGuardian", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], "name": "isInit", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], "name": "nonce", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], "name": "pendingGuardian", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }, { "internalType": "uint256", "name": "", "type": "uint256" }, { "internalType": "bytes32", "name": "", "type": "bytes32" }, { "internalType": "address[]", "name": "", "type": "address[]" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], "name": "processGuardianUpdates", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "requiredFunctions", "outputs": [{ "internalType": "bytes4[]", "name": "", "type": "bytes4[]" }], "stateMutability": "pure", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }, { "internalType": "address[]", "name": "_guardians", "type": "address[]" }, { "internalType": "bytes32", "name": "_salt", "type": "bytes32" }], "name": "revealAnonymousGuardians", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }], "name": "supportsInterface", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "pure", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_wallet", "type": "address" }], "name": "threshold", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address[]", "name": "_guardians", "type": "address[]" }, { "internalType": "uint256", "name": "_threshold", "type": "uint256" }, { "internalType": "bytes32", "name": "_guardianHash", "type": "bytes32" }, { "internalType": "uint256", "name": "_pendingUntil", "type": "uint256" }], "name": "updatePendingGuardians", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "walletDeInit", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "bytes", "name": "data", "type": "bytes" }], "name": "walletInit", "outputs": [], "stateMutability": "nonpayable", "type": "function" }]; const entrypointABI = [{ "inputs": [{ "internalType": "uint256", "name": "preOpGas", "type": "uint256" }, { "internalType": "uint256", "name": "paid", "type": "uint256" }, { "internalType": "uint48", "name": "validAfter", "type": "uint48" }, { "internalType": "uint48", "name": "validUntil", "type": "uint48" }, { "internalType": "bool", "name": "targetSuccess", "type": "bool" }, { "internalType": "bytes", "name": "targetResult", "type": "bytes" }], "name": "ExecutionResult", "type": "error" }, { "inputs": [{ "internalType": "uint256", "name": "opIndex", "type": "uint256" }, { "internalType": "string", "name": "reason", "type": "string" }], "name": "FailedOp", "type": "error" }, { "inputs": [{ "internalType": "address", "name": "sender", "type": "address" }], "name": "SenderAddressResult", "type": "error" }, { "inputs": [{ "internalType": "address", "name": "aggregator", "type": "address" }], "name": "SignatureValidationFailed", "type": "error" }, { "inputs": [{ "components": [{ "internalType": "uint256", "name": "preOpGas", "type": "uint256" }, { "internalType": "uint256", "name": "prefund", "type": "uint256" }, { "internalType": "bool", "name": "sigFailed", "type": "bool" }, { "internalType": "uint48", "name": "validAfter", "type": "uint48" }, { "internalType": "uint48", "name": "validUntil", "type": "uint48" }, { "internalType": "bytes", "name": "paymasterContext", "type": "bytes" }], "internalType": "struct IEntryPoint.ReturnInfo", "name": "returnInfo", "type": "tuple" }, { "components": [{ "internalType": "uint256", "name": "stake", "type": "uint256" }, { "internalType": "uint256", "name": "unstakeDelaySec", "type": "uint256" }], "internalType": "struct IStakeManager.StakeInfo", "name": "senderInfo", "type": "tuple" }, { "components": [{ "internalType": "uint256", "name": "stake", "type": "uint256" }, { "internalType": "uint256", "name": "unstakeDelaySec", "type": "uint256" }], "internalType": "struct IStakeManager.StakeInfo", "name": "factoryInfo", "type": "tuple" }, { "components": [{ "internalType": "uint256", "name": "stake", "type": "uint256" }, { "internalType": "uint256", "name": "unstakeDelaySec", "type": "uint256" }], "internalType": "struct IStakeManager.StakeInfo", "name": "paymasterInfo", "type": "tuple" }], "name": "ValidationResult", "type": "error" }, { "inputs": [{ "components": [{ "internalType": "uint256", "name": "preOpGas", "type": "uint256" }, { "internalType": "uint256", "name": "prefund", "type": "uint256" }, { "internalType": "bool", "name": "sigFailed", "type": "bool" }, { "internalType": "uint48", "name": "validAfter", "type": "uint48" }, { "internalType": "uint48", "name": "validUntil", "type": "uint48" }, { "internalType": "bytes", "name": "paymasterContext", "type": "bytes" }], "internalType": "struct IEntryPoint.ReturnInfo", "name": "returnInfo", "type": "tuple" }, { "components": [{ "internalType": "uint256", "name": "stake", "type": "uint256" }, { "internalType": "uint256", "name": "unstakeDelaySec", "type": "uint256" }], "internalType": "struct IStakeManager.StakeInfo", "name": "senderInfo", "type": "tuple" }, { "components": [{ "internalType": "uint256", "name": "stake", "type": "uint256" }, { "internalType": "uint256", "name": "unstakeDelaySec", "type": "uint256" }], "internalType": "struct IStakeManager.StakeInfo", "name": "factoryInfo", "type": "tuple" }, { "components": [{ "internalType": "uint256", "name": "stake", "type": "uint256" }, { "internalType": "uint256", "name": "unstakeDelaySec", "type": "uint256" }], "internalType": "struct IStakeManager.StakeInfo", "name": "paymasterInfo", "type": "tuple" }, { "components": [{ "internalType": "address", "name": "aggregator", "type": "address" }, { "components": [{ "internalType": "uint256", "name": "stake", "type": "uint256" }, { "internalType": "uint256", "name": "unstakeDelaySec", "type": "uint256" }], "internalType": "struct IStakeManager.StakeInfo", "name": "stakeInfo", "type": "tuple" }], "internalType": "struct IEntryPoint.AggregatorStakeInfo", "name": "aggregatorInfo", "type": "tuple" }], "name": "ValidationResultWithAggregation", "type": "error" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "bytes32", "name": "userOpHash", "type": "bytes32" }, { "indexed": true, "internalType": "address", "name": "sender", "type": "address" }, { "indexed": false, "internalType": "address", "name": "factory", "type": "address" }, { "indexed": false, "internalType": "address", "name": "paymaster", "type": "address" }], "name": "AccountDeployed", "type": "event" }, { "anonymous": false, "inputs": [], "name": "BeforeExecution", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "account", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "totalDeposit", "type": "uint256" }], "name": "Deposited", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "aggregator", "type": "address" }], "name": "SignatureAggregatorChanged", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "account", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "totalStaked", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "unstakeDelaySec", "type": "uint256" }], "name": "StakeLocked", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "account", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "withdrawTime", "type": "uint256" }], "name": "StakeUnlocked", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "account", "type": "address" }, { "indexed": false, "internalType": "address", "name": "withdrawAddress", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }], "name": "StakeWithdrawn", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "bytes32", "name": "userOpHash", "type": "bytes32" }, { "indexed": true, "internalType": "address", "name": "sender", "type": "address" }, { "indexed": true, "internalType": "address", "name": "paymaster", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "indexed": false, "internalType": "bool", "name": "success", "type": "bool" }, { "indexed": false, "internalType": "uint256", "name": "actualGasCost", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "actualGasUsed", "type": "uint256" }], "name": "UserOperationEvent", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "bytes32", "name": "userOpHash", "type": "bytes32" }, { "indexed": true, "internalType": "address", "name": "sender", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "indexed": false, "internalType": "bytes", "name": "revertReason", "type": "bytes" }], "name": "UserOperationRevertReason", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "account", "type": "address" }, { "indexed": false, "internalType": "address", "name": "withdrawAddress", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }], "name": "Withdrawn", "type": "event" }, { "inputs": [], "name": "SIG_VALIDATION_FAILED", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "bytes", "name": "initCode", "type": "bytes" }, { "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "bytes", "name": "paymasterAndData", "type": "bytes" }], "name": "_validateSenderAndPaymaster", "outputs": [], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "uint32", "name": "unstakeDelaySec", "type": "uint32" }], "name": "addStake", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], "name": "balanceOf", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], "name": "depositTo", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "", "type": "address" }], "name": "deposits", "outputs": [{ "internalType": "uint112", "name": "deposit", "type": "uint112" }, { "internalType": "bool", "name": "staked", "type": "bool" }, { "internalType": "uint112", "name": "stake", "type": "uint112" }, { "internalType": "uint32", "name": "unstakeDelaySec", "type": "uint32" }, { "internalType": "uint48", "name": "withdrawTime", "type": "uint48" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], "name": "getDepositInfo", "outputs": [{ "components": [{ "internalType": "uint112", "name": "deposit", "type": "uint112" }, { "internalType": "bool", "name": "staked", "type": "bool" }, { "internalType": "uint112", "name": "stake", "type": "uint112" }, { "internalType": "uint32", "name": "unstakeDelaySec", "type": "uint32" }, { "internalType": "uint48", "name": "withdrawTime", "type": "uint48" }], "internalType": "struct IStakeManager.DepositInfo", "name": "info", "type": "tuple" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "uint192", "name": "key", "type": "uint192" }], "name": "getNonce", "outputs": [{ "internalType": "uint256", "name": "nonce", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "bytes", "name": "initCode", "type": "bytes" }], "name": "getSenderAddress", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "components": [{ "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "internalType": "bytes", "name": "initCode", "type": "bytes" }, { "internalType": "bytes", "name": "callData", "type": "bytes" }, { "internalType": "uint256", "name": "callGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "verificationGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "preVerificationGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxFeePerGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxPriorityFeePerGas", "type": "uint256" }, { "internalType": "bytes", "name": "paymasterAndData", "type": "bytes" }, { "internalType": "bytes", "name": "signature", "type": "bytes" }], "internalType": "struct UserOperation", "name": "userOp", "type": "tuple" }], "name": "getUserOpHash", "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "components": [{ "components": [{ "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "internalType": "bytes", "name": "initCode", "type": "bytes" }, { "internalType": "bytes", "name": "callData", "type": "bytes" }, { "internalType": "uint256", "name": "callGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "verificationGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "preVerificationGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxFeePerGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxPriorityFeePerGas", "type": "uint256" }, { "internalType": "bytes", "name": "paymasterAndData", "type": "bytes" }, { "internalType": "bytes", "name": "signature", "type": "bytes" }], "internalType": "struct UserOperation[]", "name": "userOps", "type": "tuple[]" }, { "internalType": "contract IAggregator", "name": "aggregator", "type": "address" }, { "internalType": "bytes", "name": "signature", "type": "bytes" }], "internalType": "struct IEntryPoint.UserOpsPerAggregator[]", "name": "opsPerAggregator", "type": "tuple[]" }, { "internalType": "address payable", "name": "beneficiary", "type": "address" }], "name": "handleAggregatedOps", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "components": [{ "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "internalType": "bytes", "name": "initCode", "type": "bytes" }, { "internalType": "bytes", "name": "callData", "type": "bytes" }, { "internalType": "uint256", "name": "callGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "verificationGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "preVerificationGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxFeePerGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxPriorityFeePerGas", "type": "uint256" }, { "internalType": "bytes", "name": "paymasterAndData", "type": "bytes" }, { "internalType": "bytes", "name": "signature", "type": "bytes" }], "internalType": "struct UserOperation[]", "name": "ops", "type": "tuple[]" }, { "internalType": "address payable", "name": "beneficiary", "type": "address" }], "name": "handleOps", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "uint192", "name": "key", "type": "uint192" }], "name": "incrementNonce", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "bytes", "name": "callData", "type": "bytes" }, { "components": [{ "components": [{ "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "internalType": "uint256", "name": "callGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "verificationGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "preVerificationGas", "type": "uint256" }, { "internalType": "address", "name": "paymaster", "type": "address" }, { "internalType": "uint256", "name": "maxFeePerGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxPriorityFeePerGas", "type": "uint256" }], "internalType": "struct EntryPoint.MemoryUserOp", "name": "mUserOp", "type": "tuple" }, { "internalType": "bytes32", "name": "userOpHash", "type": "bytes32" }, { "internalType": "uint256", "name": "prefund", "type": "uint256" }, { "internalType": "uint256", "name": "contextOffset", "type": "uint256" }, { "internalType": "uint256", "name": "preOpGas", "type": "uint256" }], "internalType": "struct EntryPoint.UserOpInfo", "name": "opInfo", "type": "tuple" }, { "internalType": "bytes", "name": "context", "type": "bytes" }], "name": "innerHandleOp", "outputs": [{ "internalType": "uint256", "name": "actualGasCost", "type": "uint256" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "", "type": "address" }, { "internalType": "uint192", "name": "", "type": "uint192" }], "name": "nonceSequenceNumber", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "components": [{ "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "internalType": "bytes", "name": "initCode", "type": "bytes" }, { "internalType": "bytes", "name": "callData", "type": "bytes" }, { "internalType": "uint256", "name": "callGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "verificationGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "preVerificationGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxFeePerGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxPriorityFeePerGas", "type": "uint256" }, { "internalType": "bytes", "name": "paymasterAndData", "type": "bytes" }, { "internalType": "bytes", "name": "signature", "type": "bytes" }], "internalType": "struct UserOperation", "name": "op", "type": "tuple" }, { "internalType": "address", "name": "target", "type": "address" }, { "internalType": "bytes", "name": "targetCallData", "type": "bytes" }], "name": "simulateHandleOp", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "components": [{ "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "internalType": "bytes", "name": "initCode", "type": "bytes" }, { "internalType": "bytes", "name": "callData", "type": "bytes" }, { "internalType": "uint256", "name": "callGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "verificationGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "preVerificationGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxFeePerGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxPriorityFeePerGas", "type": "uint256" }, { "internalType": "bytes", "name": "paymasterAndData", "type": "bytes" }, { "internalType": "bytes", "name": "signature", "type": "bytes" }], "internalType": "struct UserOperation", "name": "userOp", "type": "tuple" }], "name": "simulateValidation", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "unlockStake", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address payable", "name": "withdrawAddress", "type": "address" }], "name": "withdrawStake", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address payable", "name": "withdrawAddress", "type": "address" }, { "internalType": "uint256", "name": "withdrawAmount", "type": "uint256" }], "name": "withdrawTo", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "stateMutability": "payable", "type": "receive" }]; const factoryABI = [{ "inputs": [{ "internalType": "address", "name": "_walletImpl", "type": "address" }, { "internalType": "address", "name": "_owner", "type": "address" }, { "internalType": "address", "name": "_entryPoint", "type": "address" }], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [], "name": "AlreadyInitialized", "type": "error" }, { "inputs": [], "name": "InvalidEntryPoint", "type": "error" }, { "inputs": [], "name": "InvalidEntryPointOrOwner", "type": "error" }, { "inputs": [], "name": "InvalidOwner", "type": "error" }, { "inputs": [], "name": "InvalidSignature", "type": "error" }, { "inputs": [], "name": "InvalidUpgradeDelay", "type": "error" }, { "inputs": [], "name": "LengthMismatch", "type": "error" }, { "inputs": [], "name": "NewOwnerIsZeroAddress", "type": "error" }, { "inputs": [], "name": "NoHandoverRequest", "type": "error" }, { "inputs": [], "name": "Unauthorized", "type": "error" }, { "inputs": [], "name": "WalletFactory__Create2CallFailed", "type": "error" }, { "inputs": [], "name": "ZeroAddressProvided", "type": "error" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "pendingOwner", "type": "address" }], "name": "OwnershipHandoverCanceled", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "pendingOwner", "type": "address" }], "name": "OwnershipHandoverRequested", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "oldOwner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" }], "name": "OwnershipTransferred", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": false, "internalType": "address", "name": "account", "type": "address" }], "name": "Paused", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": false, "internalType": "contract TrueWallet", "name": "wallet", "type": "address" }], "name": "TrueWalletCreation", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": false, "internalType": "address", "name": "account", "type": "address" }], "name": "Unpaused", "type": "event" }, { "inputs": [{ "internalType": "uint32", "name": "_unstakeDelaySec", "type": "uint32" }], "name": "addStake", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [], "name": "cancelOwnershipHandover", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "pendingOwner", "type": "address" }], "name": "completeOwnershipHandover", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "bytes", "name": "_initializer", "type": "bytes" }, { "internalType": "bytes32", "name": "_salt", "type": "bytes32" }], "name": "createWallet", "outputs": [{ "internalType": "contract TrueWallet", "name": "proxy", "type": "address" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "deposit", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [], "name": "entryPoint", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_entryPoint", "type": "address" }, { "internalType": "address", "name": "_walletOwner", "type": "address" }, { "internalType": "bytes[]", "name": "_modules", "type": "bytes[]" }], "name": "getInitializer", "outputs": [{ "internalType": "bytes", "name": "initializer", "type": "bytes" }], "stateMutability": "pure", "type": "function" }, { "inputs": [{ "internalType": "bytes32", "name": "_salt", "type": "bytes32" }], "name": "getWalletAddress", "outputs": [{ "internalType": "address", "name": "proxy", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "owner", "outputs": [{ "internalType": "address", "name": "result", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "pendingOwner", "type": "address" }], "name": "ownershipHandoverExpiresAt", "outputs": [{ "internalType": "uint256", "name": "result", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "pause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "paused", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "proxyCode", "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], "stateMutability": "pure", "type": "function" }, { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [], "name": "requestOwnershipHandover", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], "name": "transferOwnership", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [], "name": "unlockStake", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "unpause", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "walletImplementation", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address payable", "name": "_withdrawAddress", "type": "address" }], "name": "withdrawStake", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address payable", "name": "_withdrawAddress", "type": "address" }, { "internalType": "uint256", "name": "_withdrawAmount", "type": "uint256" }], "name": "withdrawTo", "outputs": [], "stateMutability": "nonpayable", "type": "function" }]; const TrueWalletAbi = [{ "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [], "name": "AddressAlreadyExists", "type": "error" }, { "inputs": [], "name": "AddressNotExists", "type": "error" }, { "inputs": [], "name": "CallerMustBeModule", "type": "error" }, { "inputs": [], "name": "CallerMustBeSelfOfModule", "type": "error" }, { "inputs": [], "name": "InvalidAddress", "type": "error" }, { "inputs": [], "name": "InvalidEntryPoint", "type": "error" }, { "inputs": [], "name": "InvalidEntryPointOrOwner", "type": "error" }, { "inputs": [], "name": "InvalidOwner", "type": "error" }, { "inputs": [], "name": "InvalidSelector", "type": "error" }, { "inputs": [], "name": "InvalidSignature", "type": "error" }, { "inputs": [], "name": "InvalidUpgradeDelay", "type": "error" }, { "inputs": [], "name": "LengthMismatch", "type": "error" }, { "inputs": [], "name": "ModuleAddressEmpty", "type": "error" }, { "inputs": [], "name": "ModuleAuthorized", "type": "error" }, { "inputs": [], "name": "ModuleExecuteFromModuleRecursive", "type": "error" }, { "inputs": [], "name": "ModuleNotAuthorized", "type": "error" }, { "inputs": [], "name": "ModuleNotSupportInterface", "type": "error" }, { "inputs": [], "name": "ModuleSelectorsEmpty", "type": "error" }, { "inputs": [], "name": "NoOwner", "type": "error" }, { "inputs": [], "name": "SelectorAlreadyExists", "type": "error" }, { "inputs": [], "name": "UpgradeDelayNotElapsed", "type": "error" }, { "inputs": [], "name": "WalletFactory__Create2CallFailed", "type": "error" }, { "inputs": [], "name": "ZeroAddressProvided", "type": "error" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "account", "type": "address" }, { "indexed": true, "internalType": "address", "name": "entryPoint", "type": "address" }, { "indexed": false, "internalType": "address", "name": "owner", "type": "address" }], "name": "AccountInitialized", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": false, "internalType": "uint8", "name": "version", "type": "uint8" }], "name": "Initialized", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "module", "type": "address" }], "name": "ModuleAdded", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "module", "type": "address" }], "name": "ModuleRemoved", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "module", "type": "address" }], "name": "ModuleRemovedWithError", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "owner", "type": "address" }], "name": "OwnerAdded", "type": "event" }, { "anonymous": false, "inputs": [], "name": "OwnerCleared", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "owner", "type": "address" }], "name": "OwnerRemoved", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": false, "internalType": "address", "name": "newLogic", "type": "address" }, { "indexed": false, "internalType": "uint64", "name": "activateTime", "type": "uint64" }], "name": "PreUpgrade", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "sender", "type": "address" }, { "indexed": true, "internalType": "uint256", "name": "amount", "type": "uint256" }], "name": "ReceivedETH", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "collection", "type": "address" }, { "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }], "name": "TransferredERC1155", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": false, "internalType": "address", "name": "token", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }], "name": "TransferredERC20", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "collection", "type": "address" }, { "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }], "name": "TransferredERC721", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }], "name": "TransferredETH", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "newEntryPoint", "type": "address" }, { "indexed": true, "internalType": "address", "name": "oldEntryPoint", "type": "address" }], "name": "UpdateEntryPoint", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": false, "internalType": "address", "name": "newImplementation", "type": "address" }], "name": "Upgraded", "type": "event" }, { "inputs": [], "name": "addDeposit", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [{ "internalType": "bytes", "name": "moduleAndData", "type": "bytes" }], "name": "addModule", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }], "name": "addOwner", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address[]", "name": "owners", "type": "address[]" }], "name": "addOwners", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "entryPoint", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "target", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }, { "internalType": "bytes", "name": "payload", "type": "bytes" }], "name": "execute", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address[]", "name": "target", "type": "address[]" }, { "internalType": "uint256[]", "name": "value", "type": "uint256[]" }, { "internalType": "bytes[]", "name": "payload", "type": "bytes[]" }], "name": "executeBatch", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "value", "type": "uint256" }, { "internalType": "bytes", "name": "data", "type": "bytes" }], "name": "executeFromModule", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "getDeposit", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getImplementation", "outputs": [], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_entryPoint", "type": "address" }, { "internalType": "address", "name": "_owner", "type": "address" }, { "internalType": "bytes[]", "name": "_modules", "type": "bytes[]" }], "name": "initialize", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "module", "type": "address" }], "name": "isAuthorizedModule", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "addr", "type": "address" }], "name": "isOwner", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "bytes32", "name": "hash", "type": "bytes32" }, { "internalType": "bytes", "name": "signature", "type": "bytes" }], "name": "isValidSignature", "outputs": [{ "internalType": "bytes4", "name": "magicValue", "type": "bytes4" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "listModules", "outputs": [{ "internalType": "address[]", "name": "modules", "type": "address[]" }, { "internalType": "bytes4[][]", "name": "selectors", "type": "bytes4[][]" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "listOwner", "outputs": [{ "internalType": "address[]", "name": "owners", "type": "address[]" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "logicUpgradeInfo", "outputs": [{ "components": [{ "internalType": "uint64", "name": "activateTime", "type": "uint64" }, { "internalType": "address", "name": "pendingImplementation", "type": "address" }, { "internalType": "uint256[50]", "name": "__gap", "type": "uint256[50]" }], "internalType": "struct ILogicUpgradeControl.UpgradeLayout", "name": "", "type": "tuple" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "nonce", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "", "type": "address" }, { "internalType": "address", "name": "", "type": "address" }, { "internalType": "uint256[]", "name": "", "type": "uint256[]" }, { "internalType": "uint256[]", "name": "", "type": "uint256[]" }, { "internalType": "bytes", "name": "", "type": "bytes" }], "name": "onERC1155BatchReceived", "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], "stateMutability": "pure", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "", "type": "address" }, { "internalType": "address", "name": "", "type": "address" }, { "internalType": "uint256", "name": "", "type": "uint256" }, { "internalType": "uint256", "name": "", "type": "uint256" }, { "internalType": "bytes", "name": "", "type": "bytes" }], "name": "onERC1155Received", "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], "stateMutability": "pure", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "", "type": "address" }, { "internalType": "address", "name": "", "type": "address" }, { "internalType": "uint256", "name": "", "type": "uint256" }, { "internalType": "bytes", "name": "", "type": "bytes" }], "name": "onERC721Received", "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], "stateMutability": "pure", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "newImplementation", "type": "address" }], "name": "preUpgradeTo", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "module", "type": "address" }], "name": "removeModule", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }], "name": "removeOwner", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], "name": "resetOwner", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address[]", "name": "newOwners", "type": "address[]" }], "name": "resetOwners", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_newEntryPoint", "type": "address" }], "name": "setEntryPoint", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "bytes4", "name": "_interfaceID", "type": "bytes4" }], "name": "supportsInterface", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "", "type": "address" }, { "internalType": "address", "name": "", "type": "address" }, { "internalType": "address", "name": "", "type": "address" }, { "internalType": "uint256", "name": "", "type": "uint256" }, { "internalType": "bytes", "name": "", "type": "bytes" }, { "internalType": "bytes", "name": "", "type": "bytes" }], "name": "tokensReceived", "outputs": [], "stateMutability": "pure", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "collection", "type": "address" }, { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" }], "name": "transferERC1155", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "token", "type": "address" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" }], "name": "transferERC20", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "collection", "type": "address" }, { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, { "internalType": "address", "name": "to", "type": "address" }], "name": "transferERC721", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address payable", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" }], "name": "transferETH", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "upgrade", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "upgradeDelay", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "components": [{ "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "uint256", "name": "nonce", "type": "uint256" }, { "internalType": "bytes", "name": "initCode", "type": "bytes" }, { "internalType": "bytes", "name": "callData", "type": "bytes" }, { "internalType": "uint256", "name": "callGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "verificationGasLimit", "type": "uint256" }, { "internalType": "uint256", "name": "preVerificationGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxFeePerGas", "type": "uint256" }, { "internalType": "uint256", "name": "maxPriorityFeePerGas", "type": "uint256" }, { "internalType": "bytes", "name": "paymasterAndData", "type": "bytes" }, { "internalType": "bytes", "name": "signature", "type": "bytes" }], "internalType": "struct UserOperation", "name": "userOp", "type": "tuple" }, { "internalType": "bytes32", "name": "userOpHash", "type": "bytes32" }, { "internalType": "uint256", "name": "missingWalletFunds", "type": "uint256" }], "name": "validateUserOp", "outputs": [{ "internalType": "uint256", "name": "validationData", "type": "uint256" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address payable", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" }], "name": "withdrawDepositTo", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "stateMutability": "payable", "type": "receive" }]; class UserOperationBuilder { constructor(config) { this.rpcProvider = config.rpcProvider; this.bundlerClient = config.bundlerClient; this.signer = config.signer; this.entrypointSC = config.entrypointSC; } async buildOperation(operation, paymaster = '0x') { const { maxFeePerGas, maxPriorityFeePerGas } = await this.getGasPrice(); const op = { sender: operation.sender, nonce: operation.nonce, initCode: operation.initCode, callData: operation.callData, maxFeePerGas: ethers.toBeHex(maxFeePerGas), maxPriorityFeePerGas: ethers.toBeHex(maxPriorityFeePerGas), preVerificationGas: ethers.toBeHex(0), verificationGasLimit: ethers.toBeHex(0), callGasLimit: ethers.toBeHex(0), paymasterAndData: paymaster, signature: await this.getDummySignature(), }; const estimatedOperation = await this.getGasEstimation(op); return { ...estimatedOperation, signature: await this.getSignature(estimatedOperation), }; } async getGasEstimation(userOperation) { const est = await this.bundlerClient.estimateUserOperationGas(userOperation); return { ...userOperation, preVerificationGas: est.preVerificationGas, verificationGasLimit: est.verificationGasLimit ?? est.verificationGas, callGasLimit: est.callGasLimit, }; } async getGasPrice() { /** * https://github.com/stackup-wallet/userop.js/blob/ef1a5fc368fd84422ee35a240b99aabae76c83e8/src/preset/middleware/gasPrice.ts#L4 * */ // FIXME: refactor with rpcProvider.getFeeData() const [fee, block] = await Promise.all([ this.rpcProvider.send("eth_maxPriorityFeePerGas", []), this.rpcProvider.getBlock("latest"), ]); const tip = BigInt(fee); const buffer = tip / BigInt(100) * BigInt(13); const maxPriorityFeePerGas = tip + buffer; const maxFeePerGas = block?.baseFeePerGas ? block.baseFeePerGas * BigInt(2) + maxPriorityFeePerGas : maxPriorityFeePerGas; return { maxFeePerGas, maxPriorityFeePerGas }; } async getDummySignature() { return '0x73c3ac716c487ca34bb858247b5ccf1dc354fbaabdd089af3b2ac8e78ba85a4959a2d76250325bd67c11771c31fccda87c33ceec17cc0de912690521bb95ffcb1b'; } async getSignature(userOperation) { const message = await this.entrypointSC['getUserOpHash'](userOperation); return await this.signer.signMessage(ethers.getBytes(message)); } } class TWConfigError extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.CONFIG_ERROR; } } class TWUnsupportedModuleError extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.MODULE_NOT_SUPPORTED; } } class TWModuleNotInstalledError extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.MODULE_NOT_INSTALLED; } } class TWModuleAlreadyInstalledError extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.MODULE_ALREADY_INSTALLED; } } class TWOwnerCallError extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.WALLET_NOT_OWNED; } } class TWWalletNotReadyError extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.WALLET_NOT_READY; } } class TWInvalidSignerTypeError extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.INVALID_SIGNER_TYPE; } } class TWJwtSignerInvalidJwtError extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.JWT_SIGNER_INVALID_JWT; } } class TWJwtSignerInvalidKeyError extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.JWT_SIGNER_INVALID_KEY; } } class TWJwtSignerInitializedError extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.JWT_SIGNER_INITIALIZED; } } class TWJwtSignerLimitError extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.JWT_SIGNER_LIMIT; } } class TWJwtInitSignerError extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.JWT_INIT_SIGNER_ERROR; } } class TWCallException extends Error { constructor() { super(...arguments); this.code = exports.TWErrorCodes.CALL_EXCEPTION; } } var BundlerMethods; (function (BundlerMethods) { BundlerMethods["sendUserOperation"] = "eth_sendUserOperation"; BundlerMethods["estimateUserOperationGas"] = "eth_estimateUserOperationGas"; BundlerMethods["getUserOperationByHash"] = "eth_getUserOperationByHash"; BundlerMethods["getUserOperationReceipt"] = "eth_getUserOperationReceipt"; BundlerMethods["supportedEntryPoints"] = "eth_supportedEntryPoints"; })(BundlerMethods || (BundlerMethods = {})); var BundlerErrorCodes; (function (BundlerErrorCodes) { BundlerErrorCodes["BUNDLER_ERROR"] = "BUNDLER_ERROR"; BundlerErrorCodes["MAX_RETRIES_EXCEEDED"] = "MAX_RETRIES_EXCEEDED"; })(BundlerErrorCodes || (BundlerErrorCodes = {})); class BundlerError extends Error { constructor() { super(...arguments); this.code = BundlerErrorCodes.BUNDLER_ERROR; } } class BundlerMaxRetriesError extends Error { constructor() { super(...arguments); this.code = BundlerErrorCodes.MAX_RETRIES_EXCEEDED; } } /** * @class BundlerClient - http client for interacting with the bundler server * */ class BundlerClient { constructor(config) { this.id = 0; this.bundlerUrl = config.url; this.entrypoint = ethers.getAddress(config.entrypoint); } /** * @method sendUserOperation - send user operation to the bundler server * @param {UserOperationData} operation - user operation data * @returns {string} Promise - userOperationHash * */ async sendUserOperation(operation) { const opHash = await this.fetch(BundlerMethods.sendUserOperation, [operation, this.entrypoint]); return { userOperationHash: opHash, wait: (maxRetry = 0) => this.getUserOperationReceipt(opHash, maxRetry), }; } estimateUserOperationGas(operation) { return this.fetch(BundlerMethods.estimateUserOperationGas, [operation, this.entrypoint]); } getUserOperationByHash(hash) { return this.fetch(BundlerMethods.getUserOperationByHash, [hash, this.entrypoint]); } getUserOperationReceipt(hash, maxRetry = 0) { const useRetry = maxRetry > 0; let retries = 0; return new Promise((resolve, reject) => { const execute = async () => { try { const receipt = await this.fetch(BundlerMethods.getUserOperationReceipt, [hash]); if (receipt === null) { if (useRetry && retries >= maxRetry) { return reject(new BundlerMaxRetriesError('User operation not found')); } retries++; setTimeout(execute, 3000); } else { resolve(receipt); } } catch (e) { reject(e); } }; execute(); }); } getSupportedEntryPoints() { return this.fetch(BundlerMethods.supportedEntryPoints, []); } async fetch(method, data, options = {}) { const body = JSON.stringify({ jsonrpc: '2.0', id: this.getId(), method: method, params: data, }); const defaultOptions = { method: 'POST', body, headers: { "Content-Type": "application/json", } }; const res = await fetch(this.bundlerUrl, Object.assign({}, defaultOptions, options)).then((res) => res.json()); if (res.error) { throw new BundlerError(res.error.message); } return res.result; } getId() { return this.id++; } } const encodeFunctionData = (scAbi, functionName, functionParams) => { const abiInterface = new ethers.Interface(scAbi); return abiInterface.encodeFunctionData(functionName, functionParams); }; const getSecurityModuleInitData = () => { const securityCallData = ethers.AbiCoder.defaultAbiCoder().encode(['uint32'], [1]); return ethers.concat([Modules.SecurityControlModule, securityCallData]); }; const ERC20Abi = [ 'function name() public view returns (string)', 'function symbol() public view returns (string)', 'function decimals() public view returns (uint8)', 'function totalSupply() public view returns (uint256)', 'function balanceOf(address _owner) public view returns (uint256 balance)', 'function transfer(address _to, uint256 _value) public returns (bool success)', 'function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)', 'function approve(address _spender, uint256 _value) public returns (bool success)', 'function allowance(address _owner, address _spender) public view returns (uint256 remaining)' ]; /** * Helper class to run ERC-20 tokens functions * */ class Erc20Manager { constructor(sdk) { this.sdk = sdk; } /** * Returns the balance of the current wallet in tokens * @param {string} contractAddress - Address of the ERC-20 token contract * @returns {Promise} - Balance of the wallet in tokens * */ async getBalance(contractAddress) { const contract = new ethers.Contract(contractAddress, ERC20Abi, this.sdk.rpcProvider); try { const decimals = await contract.decimals(); const balance = await contract['balanceOf'](this.sdk.address); return ethers.formatUnits(balance, decimals); } catch (err) { throw new TWCallException(err.message); } } /** * Send the `params.amount` of tokens from the current wallet to the `params.to` address * @param {SendErc20Params} params - Parameters for the transfer * @param {string} params.to - Address to transfer the tokens to * @param {number | string | bigint} params.amount - Amount of tokens to transfer in tokens * @param {string} params.contractAddress - Address of the ERC-20 token contract * @param {string} [paymaster=0x] - Address of the paymaster * @returns {Promise} - User Operation Response * */ async send(params, paymaster = '0x') { const tokenContract = new ethers.Contract(params.contractAddress, ERC20Abi, this.sdk.rpcProvider); const decimals = await tokenContract.decimals(); const txData = tokenContract.interface.encodeFunctionData('transfer', [ params.to, ethers.parseUnits(params.amount.toString(), decimals), ]); return this.sdk.execute(txData, params.contractAddress, ethers.toBeHex(0), paymaster); } /** * Returns the name of the token * @param {string} contractAddress - Address of the ERC-20 token contract * @returns {Promise} - Name of the token * */ async name(contractAddress) { const contract = this._getContract(contractAddress); try { return await contract.name(); } catch (err) { throw new TWCallException(err.message); } } /** * Returns the symbol of the token * @param {string} contractAddress - Address of the ERC-20 token contract * @returns {Promise} - Symbol of the token * */ async symbol(contractAddress) { const contract = this._getContract(contractAddress); try { return await contract.symbol(); } catch (err) { throw new TWCallException(err.message); } } /** * Returns the number of decimals the token uses * @param {string} contractAddress - Address of the ERC-20 token contract * @returns {Promise} - Number of decimals * */ async decimals(contractAddress) { const contract = this._getContract(contractAddress); try { return await contract.decimals(); } catch (err) { throw new TWCallException(err.message); } } /** * Returns the total token supply. * @param {string} contractAddress - Address of the ERC-20 token contract * @returns {Promise} - Total supply of the token in wei * */ async totalSupply(contractAddress) { const contract = this._getContract(contractAddress); try { return await contract.totalSupply(); } catch (err) { throw new TWCallException(err.message); } } /** * Returns the account balance of another account with address `owner`. * @param {string} contractAddress - Address of the ERC-20 token contract * @param {string} owner - Address of the account for the balance check * @returns {Promise} - Balance of the account in wei * */ async balanceOf(contractAddress, owner) { const contract = this._getContract(contractAddress); try { return await contract.balanceOf(owner); } catch (err) { throw new TWCallException(err.message); } } /** * Returns the amount which `spender` is still allowed to withdraw from `owner`. * @param {string} contractAddress - Address of the ERC-20 token contract * @param {string} owner - Address of the account that owns the tokens * @param {string} spender - Address of the account that is allowed to spend the tokens * @returns {Promise} - Remaining allowance of the spender in wei * */ async allowance(contractAddress, owner, spender) { const contract = this._getContract(contractAddress); try { return await contract.allowance(owner, spender); } catch (err) { throw new TWCallException(err.message); } } /** * Transfers `params.amount` of tokens to address 'params.to', and MUST fire the Transfer event. * The function SHOULD throw if the message caller’s account balance does not have enough tokens to spend. * @param {SendErc20Params} params - Parameters for the transfer * @param {string} params.to - Address to transfer the tokens to * @param {number | string | bigint} params.amount - Amount of tokens to transfer in wei * @param {string} params.contractAddress - Address of the ERC-20 token contract * @param {string} [paymaster=0x] - Address of the paymaster * */ async transfer(params, paymaster = '0x') { const tokenContract = this._getContract(params.contractAddress); const txData = tokenContract.interface.encodeFunctionData('transfer', [ params.to, params.amount, ]); return this.sdk.execute(txData, params.contractAddress, ethers.toBeHex(0), paymaster); } /** * Transfers `params.amount` of tokens from address `params.from` to address `params.to`, and MUST fire the Transfer event. * The transferFrom method is used for a withdrawal workflow, allowing contracts to transfer tokens on your behalf. * This can be used for example to allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies. * The function SHOULD throw unless the `params.from` account has deliberately authorized the sender of the message via some mechanism. * * @param {SendErc20Params} params - Parameters for the transfer * @param {string} params.from - Address to transfer the tokens from * @param {string} params.to - Address to transfer the tokens to * @param {number | string | bigint} params.amount - Amount of tokens to transfer in wei * @param {string} params.contractAddress - Address of the ERC-20 token contract * @param {string} [paymaster=0x] - Address of the paymaster * @returns {Promise} - User Operation Response * */ async transferFrom(params, paymaster = '0x') { const tokenContract = this._getContract(params.contractAddress); const txData = tokenContract.interface.encodeFunctionData('transferFrom', [ params.from, params.to, params.amount, ]); return this.sdk.execute(txData, params.contractAddress, ethers.toBeHex(0), paymaster); } /** * Allows `params.spender` to withdraw from your account multiple times, up to the `params.amount`. * If this function is called again it overwrites the current allowance with 'params.amount'. * * @param {object} params - Parameters for the transfer * @param {string} params.spender - Address of the account that is allowed to spend the tokens * @param {number | string | bigint} params.amount - Amount of tokens to approve in wei * @param {string} params.contractAddress - Address of the ERC-20 token contract * @param {string} [paymaster=0x] - Address of the paymaster * @returns {Promise} - User Operation Response * */ async approve(params, paymaster = '0x') { const tokenContract = this._getContract(params.contractAddress); const txData = tokenContract.interface.encodeFunctionData('approve', [ params.spender, params.amount, ]); return this.sdk.execute(txData, params.contractAddress, ethers.toBeHex(0), paymaster); } _getContract(contractAddress) { return new ethers.Contract(contractAddress, ERC20Abi, this.sdk.rpcProvider); } } const ERC721Abi = [ 'function balanceOf(address _owner) external view returns (uint256)', 'function getApproved(uint256 _tokenId) external view returns (address)', 'function isApprovedForAll(address _owner, address _operator) external view returns (bool)', 'function name() external view returns (string _name)', 'function ownerOf(uint256 _tokenId) external view returns (address)', 'function symbol() external view returns (string _symbol)', 'function tokenURI(uint256 _tokenId) external view returns (string)', 'function approve(address _approved, uint256 _tokenId) external payable', 'function setApprovalForAll(address _operator, bool _approved) external', 'function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable', 'function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable', 'function transferFrom(address _from, address _to, uint256 _tokenId) external payable', ]; /** * Helper class to run ERC-721 tokens functions * */ class Erc721Manager { constructor(sdk) { this.sdk = sdk; } /** * Count all NFTs assigned to an owner * @param {string} contractAddress - Address of the ERC-721 NFT token contract * @returns {Promise} Promise with the number of NFTs owned by the wallet * */ async balanceOf(contractAddress) { const contract = this._getContract(contractAddress); try { const balance = await contract['balanceOf'](this.sdk.address); return balance.toString(); } catch (err) { throw new TWCallException(err.message); } } /** * Get the approved address for a single NFT * @param {string} contractAddress - Address of the ERC-721 NFT token contract * @param {number} tokenId - ID of the NFT * @returns {Promise} Promise with the address of the approved account * */ async getApproved(contractAddress, tokenId) { const contract = this._getContract(contractAddress); try { return await contract['getApproved'](tokenId); } catch (err) { throw new TWCallException(err.message); } } /** * Query if an address is an authorized operator for another address * @param {string} contractAddress - Address of the ERC-721 NFT token contract * @param {string} owner - The address that owns the NFTs * @param {string} operator - The address that acts on behalf of the owner * @returns {Promise} Promise with True if `operator` is an approved operator for `owner`, false otherwise * */ async isApprovedForAll(contractAddress, owner, operator) { const contract = this._getContract(contractAddress); try { return await contract['isApprovedForAll'](owner, operator); } catch (err) { throw new TWCallException(err.message); } } /** * A descriptive name for a collection of NFTs in this contract * @param {string} contractAddress - Address of the ERC-721 NFT token contract * @returns {Promise} Promise with the name of the token * */ async name(contractAddress) { const contract = this._getContract(contractAddress); try { return await contract['name'](); } catch (err) { throw new TWCallException(err.message); } } /** * An abbreviated name for NFTs in this contract * @param {string} contractAddress - Address of the ERC-721 NFT token contract * @returns {Promise} Promise with the symbol of the token * */ async symbol(contractAddress) { const contract = this._getContract(contractAddress); try { return await contract['symbol'](); } catch (err) { throw new TWCallException(err.message); } } /** * A distinct Uniform Resource Identifier (URI) for a given asset. * @param {string} contractAddress - Address of the ERC-721 NFT token contract * @param {number} tokenId - ID of the NFT * @returns {Promise} Promise with the URI of the token * */ async tokenURI(contractAddress, tokenId) { const contract = this._getContract(contractAddress); try { return await contract['tokenURI'](tokenId); } catch (err) { throw new TWCallException(err.message); } } /** * Find the owner of an NFT * @param {string} contractAddress - Address of the ERC-721 NFT token contract * @param {number} tokenId - ID of the NFT * @returns {Promise} Promise with the address of the owner of the NFT * */ async ownerOf(contractAddress, tokenId) { const contract = this._getContract(contractAddress); try { return await contract['ownerOf'](tokenId); } catch (err) { throw new TWCallException(err.message); } } /** * Change or reaffirm the approved address for an NFT * @param {object} params - Object with the parameters for the operation * @param {string} params.to - Address to be approved for the given NFT * @param {string} params.contractAddress - Address of the ERC-721 NFT token contract * @param {number} params.tokenId - ID of the NFT * @param {string} [paymaster='0x'] - Address of the paymaster contract * @returns {Promise} Promise with the response of the operation * */ async approve(params, paymaster = '0x') { const contract = new ethers.Contract(params.contractAddress, ERC721Abi, this.sdk.rpcProvider); const txData = contract.interface.encodeFunctionData('approve', [params.to, params.tokenId]); return this.sdk.execute(txData, params.contractAddress, ethers.toBeHex(0), paymaster); } /** * Enable or disable approval for a third party ("operator") to manage * all of `msg.sender`'s assets * @param {object} params - Object with the parameters for the operation * @param {string} params.operator - Address to be approved for the given NFT * @param {boolean} params.approved - True if the operator is approved, false to revoke approval * @param {string} params.contractAddress - Address of the ERC-721 NFT token contract * @param {string} [paymaster='0x'] - Address of the paymaster contract * @returns {Promise} Promise with the response of the operation * */ setApprovalForAll(params, paymaster = '0x') { const contract = this._getContract(params.contractAddress); const txData = contract.interface.encodeFunctionData('setApprovalForAll', [params.operator, params.approved]); return this.sdk.execute(txData, params.contractAddress, ethers.toBeHex(0), paymaster); } /** * Transfer ownership of an NFT - THE CALLER IS RESPONSIBLE * TO CONFIRM THAT `params.to` IS CAPABLE OF RECEIVING NFTS OR ELSE * THEY MAY BE PERMANENTLY LOST * @param {object} params - Object with the parameters for the operation * @param {string} params.from - The current owner of the NFT * @param {string} params.to - The new owner * @param {number} params.tokenId - ID of the NFT * @param {string} params.contractAddress - Address of the ERC-721 NFT token contract * @param {string} [paymaster='0x'] - Address of the paymaster contract * @returns {Promise} Promise with the response of the operation * */ transferFrom(params, paymaster = '0x') { const contract = this._getContract(params.contractAddress); const txData = contract.interface.encodeFunctionData('transferFrom', [params.from, params.to, params.tokenId]); return this.sdk.execute(txData, params.contractAddress, ethers.toBeHex(0), paymaster); } /** * Transfers the ownership of an NFT from one address to another address * @param {object} params - Object with the parameters for the operation * @param {string} params.from - The current owner of the NFT * @param {string} params.to - The new owner * @param {number} params.tokenId - ID of the NFT * @param {string} params.contractAddress - Address of the ERC-721 NFT token contract * @param {string} [params.data] - Additional data with no specified format, sent in call to `params.to` * @param {string} [paymaster='0x'] - Address of the paymaster contract * @returns {Promise} Promise with the response of the operation * */ safeTransferFrom(params, paymaster = '0x') { const contract = this._getContract(params.contractAddress); const callParams = [params.from, params.to, params.tokenId]; let functionName = 'safeTransferFrom(address _from, address _to, uint256 _tokenId)'; if (params.data) { functionName = 'safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data)'; callParams.push(params.data); } const txData = contract.interface.encodeFunctionData(functionName, callParams); return this.sdk.execute(txData, params.contractAddress, ethers.toBeHex(0), paymaster); } _getContract(contractAddress) { return new ethers.Contract(contractAddress, ERC721Abi, this.sdk.rpcProvider); } } const ERC1155Abi = [ 'function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external', 'function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external', 'function balanceOf(address _owner, uint256 _id) external view returns (uint256)', 'function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory)', 'function setApprovalForAll(address _operator, bool _approved) external', 'function isApprovedForAll(address _owner, address _operator) external view returns (bool)', 'function uri(uint256 _id) external view returns (string memory)', ]; /** * Helper class to run ERC-1155 tokens functions * */ class Erc1155Manager { constructor(sdk) { this.sdk = sdk; } /** * Transfers `params.value` amount of an `params.id` from the `params.from` address to the `params.to` address specified (with safety call). * @dev Caller must be approved to manage the tokens being transferred out of the `params.from` account. * @param {TransferErc1155Params} params - Transfer parameters * @param {string} params.contractAddress - Address of the ERC-1155 contract * @param {string} params.from - Source address * @param {string} params.to - Target address * @param {number} params.id - ID of the token type * @param {number} params.value - Transfer amount * @param {string} [params.data] - Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `params.to` * @param {string} [paymaster] - Paymaster address * @return {Promise} * */ async safeTransferFrom(params, paymaster = '0x') { const contract = this._getContract(params.contractAddress); const callParams = [params.from, params.to, params.id, params.value, params.data || '0x']; const txData = contract.interface.encodeFunctionData('safeTransferFrom', callParams); return this.sdk.execute(txData, params.contractAddress, ethers.toBeHex(0), paymaster); } /** * Transfers `params.values` amount(s) of `params.ids` from the `params.from` address to the `params.to` address specified (with safety call). * @dev Caller must be approved to manage the tokens being transferred out of the `params.from` account. * @param {BatchTransferErc1155Params} params - Batch transfer parameters * @param {string} params.contractAddress - Address of the ERC-1155 contract * @param {string} params.from - Source address * @param {string} params.to - Target address * @param {number[]} params.ids - IDs of the token types * @param {number[]} params.values - Transfer amounts * @param {string} [params.data] - Additional data with no specified format, MUST be sent unaltered in call to `onERC1155BatchReceived` on `params.to` * @param {string} [paymaster] - Paymaster address * @return {Promise} * */ async safeBatchTransferFrom(params, paymaster = '0x') { const contract = this._getContract(params.contractAddress); const callParams = [params.from, params.to, params.ids, params.values, params.data || '0x']; const txData = contract.interface.encodeFunctionData('safeBatchTransferFrom', callParams); return this.sdk.execute(txData, params.contractAddress, ethers.toBeHex(0), paymaster); } /** * Get the balance of an account's tokens. * @param {string} contractAddress - Address of the ERC-1155 contract * @param {string} owner - The address of the token holder * @param {number} id - ID of the token * @return {Promise} The `params.owner`'s balance of the token type requested * */ async balanceOf(contractAddress, owner, id) { const contract = this._getContract(contractAddress); return contract.balanceOf(owner, id); } /** * Get the balance of multiple account/token pairs * @param {string} contractAddress - Address of the ERC-1155 contract * @param {string[]} owners - The addresses of the token holders * @param {number[]} ids - ID of the tokens * @return {Promise} The `params.owner`'s balance of the token types requested (i.e. balance for each (owner, id) pair) * */ async balanceOfBatch(contractAddress, owners, ids) { const contract = this._getContract(contractAddress); return contract.balanceOfBatch(owners, ids); } /** * Enable or disable approval for a third party ("`params.operator`") to manage all the caller's tokens. * @param {SetApprovalForAllErc1155Params} params - Approval parameters * @param {string} params.contractAddress - Address of the ERC-1155 contract * @param {string} params.operator - Address to add to the set of authorized operators * @param {boolean} params.approved - True if the operator is approved, false to revoke approval * @param {string} [paymaster] - Paymaster address * @return {Promise} * */ async setApprovalForAll(params, paymaster = '0x') { const contract = this._getContract(params.contractAddress); const callParams = [params.operator, params.approved]; const txData = contract.interface.encodeFunctionData('setApprovalForAll', callParams); return this.sdk.execute(txData, params.contractAddress, ethers.toBeHex(0), paymaster); } /** * Queries the approval status of an `operator` for a given `owner`. * @param {string} contractAddress - Address of the ERC-1155 contract * @param {string} owner - The owner of the tokens * @param {string} operator - Address of authorized operator * @return {Promise} True if the operator is approved, false if not * */ async isApprovedForAll(contractAddress, owner, operator) { const contract = this._getContract(contractAddress); return contract.isApprovedForAll(owner, operator); } /** * A distinct Uniform Resource Identifier (URI) for a given token. * The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema". * @param {string} contractAddress - Address of the ERC-1155 contract * @param {number} id - ID of the token * @return {Promise} URI string * */ async uri(contractAddress, id) { const contract = this._getContract(contractAddress); return contract.uri(id); } _getContract(contractAddress) { return new ethers.Contract(contractAddress, ERC1155Abi, this.sdk.rpcProvider); } } class JWTSignerService { constructor(apiUrl, getJwt) { this.endpoints = { getAddress: 'wallets/get-address', getSignature: 'wallets/sign', }; this._apiUrl = apiUrl; this._getJwt = getJwt; } async getAddress() { const requestOptions = this._getDefaultRequestOptions(); const response = await fetch(`${this._apiUrl}/${this.endpoints.getAddress}`, { ...requestOptions, body: JSON.stringify({ jwt_token: await this._getJwt(), }), }); if (!response.ok) { await this._handleError(response); } const { address } = await response.json(); return address; } async sign(message) { const requestOptions = this._getDefaultRequestOptions(); const response = await fetch(`${this._apiUrl}/${this.endpoints.getSignature}`, { ...requestOptions, body: JSON.stringify({ message, jwt_token: await this._getJwt(), // TODO: extend message type message_type: 'hash', // hash | message // TODO: extend wallet type wallet_type: 'evm' }), }); if (!response.ok) { await this._handleError(response); } const { signature } = await response.json(); return signature; } async _handleError(response) { const error = await response.json(); switch (response.status) { case 400: throw new TWJwtSignerInvalidJwtError(Object.values(error.detail).join(' ')); case 401: throw new TWJwtSignerInvalidKeyError(error.detail); case 403: throw new TWJwtSignerLimitError(error.detail); default: throw new TWJwtInitSignerError(JSON.stringify(error.detail)); } } _getDefaultRequestOptions() { return { method: 'POST', mode: 'cors', headers: { 'Content-Type': 'application/json', }, }; } } class JWTSigner { get address() { return this._address; } get initialized() { return this._initialized; } constructor(service) { this._initialized = false; this._address = null; this._service = service; } async init() { if (this._initialized) { throw new TWJwtSignerInitializedError('JWT Signer is already initialized'); } this._address = await this._service.getAddress(); this._initialized = true; } async signMessage(_message) { const message = _message instanceof Uint8Array ? ethers.hexlify(_message) : _message; return await this._service.sign(message); } } const getSigner = async (config) => { switch (config.signer.type) { case 'salt': return await getSaltSigner(config.signer.data[0]); case 'injected': return await getInjectedSigner(config.signer.data[0]); case 'privateKey': return new ethers.Wallet(config.signer.data[0]); case 'jwt': return await getJWTSigner(config.bundlerUrl, config.signer.data[0]); default: throw new TWInvalidSignerTypeError(`${config.signer.type} is invalid signer type`); } }; const getSaltSigner = async (salt) => { const entropy = ethers.solidityPackedKeccak256(['string'], [salt]); const mnemonic = ethers.Mnemonic.entropyToPhrase(entropy); const { privateKey } = ethers.Wallet.fromPhrase(mnemonic); return new ethers.Wallet(privateKey); }; const getInjectedSigner = async (provider) => { const browserProvider = new ethers.BrowserProvider(provider); await browserProvider.send('eth_requestAccounts', []); return await browserProvider.getSigner(); }; const getJWTSigner = async (bundlerUrl, jwt) => { const url = new URL(bundlerUrl); const apiUrl = url.origin; const [_, version, projectKey] = url.pathname.split('/'); const service = new JWTSignerService(`${apiUrl}/${version}/${projectKey}`, jwt); const signer = new JWTSigner(service); await signer.init(); return signer; }; const isContract = async (address, provider) => { const bytecode = await provider.getCode(address); return bytecode !== '0x'; }; /** * Utility function to check if given address is a valid Ethereum address * @param {string} address - Address to check if it is a valid Ethereum address * @returns {boolean} - true if given address is a valid Ethereum address, false otherwise * */ const isEthAddress = (address) => { return ethers.isAddress(address); }; /** * Utility function to get the checksum address of given address * @param {string} address - Address to get the checksum address * @returns {string} - Checksum address of given address * */ const getChecksumAddress = (address) => { return ethers.getAddress(address); }; /** * Utility function to convert wei value to the given unit value * @param {string | number | bigint} wei - Wei value to convert to ETH * @param {number} [decimals=18] - Number of decimals to use on conversion * @returns {string} - ETH value of given Wei * */ const fromWei = (wei, decimals = 18) => { return ethers.formatUnits(wei, decimals); }; /** * Utility function to convert given unit value to the wei value * @param {string} eth - ETH value to convert to Wei * @param {number} [decimals=18] - Number of decimals to use on conversion * @returns {bigint} - Wei value of given ETH * */ const toWei = (eth, decimals = 18) => { return ethers.parseUnits(eth, decimals); }; const SecurityControlModuleAbi = [{ "inputs": [{ "internalType": "contract ITrueContractManager", "name": "trueContractManagerAddress", "type": "address" }], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [], "name": "CallerMustBeModule", "type": "error" }, { "inputs": [], "name": "ModuleAddressEmpty", "type": "error" }, { "inputs": [], "name": "ModuleAuthorized", "type": "error" }, { "inputs": [], "name": "ModuleExecuteFromModuleRecursive", "type": "error" }, { "inputs": [], "name": "ModuleNotAuthorized", "type": "error" }, { "inputs": [], "name": "ModuleNotSupportInterface", "type": "error" }, { "inputs": [], "name": "ModuleSelectorsEmpty", "type": "error" }, { "inputs": [], "name": "SecurityControlModule__BasicInitAlreadyDone", "type": "error" }, { "inputs": [], "name": "SecurityControlModule__BasicInitNotDone", "type": "error" }, { "inputs": [{ "internalType": "address", "name": "target", "type": "address" }, { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "bytes", "name": "returnData", "type": "bytes" }], "name": "SecurityControlModule__ExecuteError", "type": "error" }, { "inputs": [], "name": "SecurityControlModule__FullInitAlreadyDone", "type": "error" }, { "inputs": [], "name": "SecurityControlModule__InvalidInitState", "type": "error" }, { "inputs": [], "name": "SecurityControlModule__InvalidModule", "type": "error" }, { "inputs": [], "name": "SecurityControlModule__InvalidOwner", "type": "error" }, { "inputs": [], "name": "SecurityControlModule__NotInitialized", "type": "error" }, { "inputs": [], "name": "SecurityControlModule__UnableSelfRemove", "type": "error" }, { "inputs": [{ "internalType": "bytes4", "name": "selector", "type": "bytes4" }], "name": "SecurityControlModule__UnsupportedSelector", "type": "error" }, { "anonymous": false, "inputs": [{ "indexed": false, "internalType": "address", "name": "target", "type": "address" }, { "indexed": false, "internalType": "bytes", "name": "data", "type": "bytes" }, { "indexed": false, "internalType": "address", "name": "sender", "type": "address" }], "name": "Execute", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "wallet", "type": "address" }], "name": "ModuleDeInit", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "wallet", "type": "address" }], "name": "ModuleInit", "type": "event" }, { "inputs": [{ "internalType": "address", "name": "", "type": "address" }], "name": "basicInitialized", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "target", "type": "address" }, { "internalType": "bytes", "name": "data", "type": "bytes" }], "name": "execute", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "fullInit", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "target", "type": "address" }, { "internalType": "bytes", "name": "data", "type": "bytes" }], "name": "fullInitAndAddModule", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "", "type": "address" }], "name": "fullInitialized", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "requiredFunctions", "outputs": [{ "internalType": "bytes4[]", "name": "", "type": "bytes4[]" }], "stateMutability": "pure", "type": "function" }, { "inputs": [{ "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }], "name": "supportsInterface", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "pure", "type": "function" }, { "inputs": [], "name": "trueContractManager", "outputs": [{ "internalType": "contract ITrueContractManager", "name": "", "type": "address" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "walletDeInit", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "bytes", "name": "data", "type": "bytes" }], "name": "walletInit", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "wallet", "type": "address" }], "name": "walletInitSeed", "outputs": [{ "internalType": "uint256", "name": "seed", "type": "uint256" }], "stateMutability": "view", "type": "function" }]; exports.RecoveryErrorCodes = void 0; (function (RecoveryErrorCodes) { RecoveryErrorCodes["RECOVERY_EXECUTE_NOT_READY"] = "RECOVERY_EXECUTE_NOT_READY"; })(exports.RecoveryErrorCodes || (exports.RecoveryErrorCodes = {})); class RecoveryError extends Error { constructor() { super(...arguments); this.code = exports.RecoveryErrorCodes.RECOVERY_EXECUTE_NOT_READY; } } const getRecoveryModuleInitData = (moduleData) => { let callData = ethers.AbiCoder.defaultAbiCoder().encode(['address[]', 'uint256', 'bytes32'], [moduleData.guardians, moduleData.threshold, ethers.ZeroHash]); const isAnonymous = !!moduleData.anonymousGuardiansSalt; if (isAnonymous) { const salt = ethers.encodeBytes32String(moduleData.anonymousGuardiansSalt?.toString()); const guardiansHash = ethers.solidityPackedKeccak256(['address[]', 'bytes32'], [moduleData.guardians, salt]); callData = ethers.AbiCoder.defaultAbiCoder().encode(['address[]', 'uint256', 'bytes32'], [[], moduleData.threshold, guardiansHash]); } return ethers.concat([Modules.SocialRecoveryModule, callData]); }; /** * Social Recovery Module * @class TrueWalletRecoveryModule * */ class TrueWalletRecoveryModule { constructor(config) { this.wallet = config.wallet; this.recoveryModuleSC = new ethers.Contract(Modules.SocialRecoveryModule, SocialRecoveryModuleAbi, this.wallet.rpcProvider); this.securityControlModuleSC = new ethers.Contract(Modules.SecurityControlModule, SecurityControlModuleAbi, this.wallet.rpcProvider); } async install(data) { const isInit = await this.recoveryModuleSC.isInit(this.wallet.address); if (isInit) { throw new TWModuleAlreadyInstalledError(`Social Recovery Module is already installed.`); } const isBasicInitialized = await this.securityControlModuleSC.basicInitialized(this.wallet.address); if (!isBasicInitialized) { throw new TWWalletNotReadyError(`Wallet is not initialized.`); } const addModuleFromWalletTxData = encodeFunctionData(TrueWalletAbi, 'addModule', [getRecoveryModuleInitData(data)]); let txData = encodeFunctionData(SecurityControlModuleAbi, 'fullInitAndAddModule', [this.wallet.address, addModuleFromWalletTxData]); const isFullInitialized = await this.securityControlModuleSC.fullInitialized(this.wallet.address); if (isFullInitialized) { txData = encodeFunctionData(SecurityControlModuleAbi, 'execute', [this.wallet.address, addModuleFromWalletTxData]); } return this.executeFn(txData, Modules.SecurityControlModule); } async remove() { const isInit = await this.recoveryModuleSC.isInit(this.wallet.address); if (!isInit) { throw new TWModuleNotInstalledError(`Social Recovery Module is not installed.`); } const removeModuleTxData = encodeFunctionData(TrueWalletAbi, 'removeModule', [Modules.SocialRecoveryModule]); const txData = encodeFunctionData(SecurityControlModuleAbi, 'execute', [this.wallet.address, removeModuleTxData]); return this.executeFn(txData, Modules.SecurityControlModule); } /** * Get the active guardians for a wallet. * @method getGuardians * @returns {Promise} - The list of active guardians for a wallet * */ async getGuardians() { return this.recoveryModuleSC['getGuardians'](this.wallet.address); } /** * Counts the number of active guardians for a wallet. * @method getGuardiansCount * @returns {Promise} - The number of active guardians for a wallet * */ async getGuardiansCount() { return this.recoveryModuleSC['guardiansCount'](this.wallet.address); } /** * Returns the bytes that are hashed to be signed by guardians. * @method encodeSocialRecoveryData * @param {string[]} newOwners - the list of addresses that will be the new owners of the wallet after recovery. * @returns {Promise} - The bytes that are hashed to be signed by guardians * */ async encodeSocialRecoveryData(newOwners) { const nonce = await this.nonce(); return this.recoveryModuleSC.encodeSocialRecoveryData(this.wallet.address, newOwners, nonce); } /** * Checks if an address is a guardian for a wallet. * @method isGuardian * @param {string} guardianAddress - The address to check * @returns {Promise} - True if the address is a guardian for the wallet, false if not * */ async isGuardian(guardianAddress) { return this.recoveryModuleSC['isGuardian'](this.wallet.address, guardianAddress); } /** * Retrieves specific guardian approval status a particular recovery request at current nonce. * @method hasGuardianApproved * @param {string} guardianAddress - The address of the guardian * @param {string[]} owners - The list of addresses that will be the new owners of the wallet after recovery * @returns {Promise} - True if the guardian has approved the recovery request, false if not * */ async hasGuardianApproved(guardianAddress, owners) { return this.recoveryModuleSC['hasGuardianApproved'](guardianAddress, this.wallet.address, owners); } async pendingGuardian() { return this.recoveryModuleSC['pendingGuardian'](this.wallet.address); } async getGuardiansHash() { return this.recoveryModuleSC['getGuardiansHash'](this.wallet.address); } /** * Retrieves the guardian approval count for this particular recovery request at current nonce. * @method getRecoveryApprovals * @param {string} walletAddress - The address of the wallet * @param {string[]} newOwners - The list of addresses that will be the new owners of the wallet after recovery * @returns {Promise} - The number of guardians that have approved the recovery request * */ async getRecoveryApprovals(walletAddress, newOwners) { return this.recoveryModuleSC['getRecoveryApprovals'](walletAddress, newOwners); } /** * Retrieves the wallet's current ongoing recovery request. * @method getRecoveryEntry * @param {string} walletAddress - The address of the wallet * @returns {Promise<[string[], bigint, bigint]>} - The list of new owners, the time until which the recovery will be pending, and the nonce - unique nonce to ensure each recovery process is unique * */ async getRecoveryEntry(walletAddress) { return this.recoveryModuleSC['getRecoveryEntry'](walletAddress); } /** * Get the module nonce for a wallet. * @method nonce * @returns {Promise} - The module nonce for a wallet * */ async nonce() { return await this.recoveryModuleSC['nonce'](this.wallet.address); } async getSocialRecoveryHash(newOwners) { const nonce = await this.nonce(); return this.recoveryModuleSC['getSocialRecoveryHash'](this.wallet, newOwners, nonce); } /** * Retrieves the wallet threshold count. * @method getThreshold * @returns {Promise} - The wallet threshold count * */ async getThreshold() { return await this.recoveryModuleSC['threshold'](this.wallet.address); } /** * Method should be called by guardian to start recovery process * @method approveRecovery * @param {string} restoringWallet - address of the wallet that is being restored. * @param {string[]} newOwners - the list of addresses that will be the new owners of the wallet after recovery. * @param {number} pendingUntil - the time in seconds until which the recovery will be pending. * @returns {Promise} - User Operation Response * */ async approveRecovery(restoringWallet, newOwners, pendingUntil) { const callData = encodeFunctionData(SocialRecoveryModuleAbi, 'approveRecovery', [restoringWallet, newOwners, pendingUntil]); return this.executeFn(callData); } /** * Should be called by guardian to approve and set new owner of the wallet. * Before executing this function, guardian should call `approveRecovery` function `threshold` times. * @param {string} wallet - address of the wallet that is being restored * @returns {Promise} - User Operation Response * */ async executeRecovery(wallet) { const [_newOwners, executeAfter, _nonce] = await this.getRecoveryEntry(wallet); const canExecute = Number(executeAfter) * 1000 < Date.now(); if (!canExecute) { throw new RecoveryError(`Execute recovery will be available after ${new Date(Number(executeAfter) * 1000).toLocaleString()}`); } const callData = encodeFunctionData(SocialRecoveryModuleAbi, 'executeRecovery', [wallet]); return this.executeFn(callData); } /** * Method called by wallet owner to cancel recovery process * @method cancelRecovery * @returns {Promise} - User Operation Response * */ async cancelRecovery() { const txData = encodeFunctionData(SocialRecoveryModuleAbi, 'cancelRecovery', [this.wallet.address]); return this.executeFn(txData); } async executeFn(txData, target = Modules.SocialRecoveryModule, payableAmount = ethers.toBeHex(0)) { return this.wallet.execute(txData, target, payableAmount); } } /* eslint-disable @typescript-eslint/no-explicit-any */ const onlyOwner = (originalMethod, _context) => { /* eslint-disable @typescript-eslint/no-explicit-any */ return async function replacementMethod(...args) { if (!this.ready) { this.ready = await this.isWalletReady(); } if (this.ready) { const isOwner = await this.isWalletOwner(this.signer.address); if (!isOwner) { throw new TWOwnerCallError('This operation is allowed only for the wallet owner.'); } } return originalMethod.call(this, ...args); }; }; /* eslint-disable @typescript-eslint/no-explicit-any */ const walletReady = (originalMethod, _context) => { /* eslint-disable @typescript-eslint/no-explicit-any */ return async function replacementMethod(...args) { if (!this.ready) { this.ready = await this.isWalletReady(); if (!this.ready) { throw new TWWalletNotReadyError(`Wallet is not smart contract yet.`); } } return originalMethod.call(this, ...args); }; }; /** Main SDK class */ let TrueWalletSDK = (() => { var _a; let _instanceExtraInitializers = []; let _getNonce_decorators; let _send_decorators; let _deployWallet_decorators; let _execute_decorators; let _contractCall_decorators; let _installModule_decorators; let _removeModule_decorators; let _getInstalledModules_decorators; let _isWalletOwner_decorators; return _a = class TrueWalletSDK { get address() { return this.walletSC.target; } constructor() { /** @property {boolean} ready - is wallet deployed to blockchain */ this.ready = (__runInitializers(this, _instanceExtraInitializers), false); } async init(config) { const requiredParams = ['signer', 'bundlerUrl']; const isConfigValid = requiredParams.every((param) => Object.prototype.hasOwnProperty.call(config, param)); if (!isConfigValid) { throw new TWConfigError(`Parameters 'salt', 'bundlerUrl' are required in config.`); } this.rpcProvider = new ethers.JsonRpcProvider(config.rpcProviderUrl || config.bundlerUrl); this.factorySC = new ethers.Contract(SmartContracts.Factory, factoryABI, this.rpcProvider); this.entrypointSC = new ethers.Contract(SmartContracts.Entrypoint, entrypointABI, this.rpcProvider); this.bundler = new BundlerClient({ url: config.bundlerUrl, entrypoint: this.entrypointSC.target, }); this.signer = await getSigner(config); // FIXME: hardcoded wallet idx const walletAddress = await this.getWalletAddress(0); this.walletSC = new ethers.Contract(walletAddress, TrueWalletAbi, this.rpcProvider); this.ready = await this.isWalletReady(); this.operationBuilder = new UserOperationBuilder({ entrypointSC: this.entrypointSC, rpcProvider: this.rpcProvider, bundlerClient: this.bundler, signer: this.signer, }); this.socialRecoveryModule = new TrueWalletRecoveryModule({ wallet: this, }); this.erc20 = new Erc20Manager(this); this.erc721 = new Erc721Manager(this); this.erc1155 = new Erc1155Manager(this); return this; } async getWalletAddress(idx) { const walletSalt = this.getWalletSalt(idx); return this.factorySC['getWalletAddress'](walletSalt); } /** * Get balance of the wallet in native currency * @method getBalance * @returns {Promise} - balance of the wallet in ether unit * * @example * const wallet = new TrueWalletSDK({ ... }); * await wallet.getBalance(); * */ async getBalance() { const balance = await this.rpcProvider.getBalance(this.address); return ethers.formatEther(balance); } /** * Get nonce of the wallet * @method getNonce * @returns {Promise} - nonce of the wallet * * @example * const wallet = new TrueWalletSDK({ ... }); * await wallet.getNonce(); * */ async getNonce() { return await this.walletSC['nonce'](); } /** * Send native currency to recipient * @method send * @param {Object} params * @param {string} params.to - recipient address * @param {string | number} params.amount - amount to send in ether unit * @param {string} [paymaster=0x] - paymaster address (optional) * @returns {Promise} - User Operation Response * * @example * const wallet = new TrueWalletSDK({ ... }); * await wallet.send({ * to: '0x...', * amount: '0.1', * }); * */ async send(params, paymaster = '0x') { return this.execute('0x', params.to, ethers.parseEther(params.amount.toString()).toString(), paymaster); } async deployWallet(paymaster = '0x') { return this.buildAndSendOperation('0x', paymaster); } /** * Low level method to execute the transaction on behalf of the wallet * @method execute * @param {string} payload - transaction data * @param {string} [target=this.address] - target address * @param {string} [value=0] - amount to send in ether unit * @param {string} [paymaster=0x] - paymaster contract address * @returns {Promise} - User Operation Response * */ async execute(payload, target = this.address, value = ethers.toBeHex(0), paymaster = '0x') { const executeTxData = encodeFunctionData(TrueWalletAbi, 'execute', [target, value, payload]); return this.buildAndSendOperation(executeTxData, paymaster); } /** * Used to call contract methods that change state * @param params - contract call params * @param {string} params.address - contract address * @param {InterfaceAbi | Interface} params.abi - contract abi * @param {string} params.method - contract method name to call * @param {unknown[]} params.args - contract method arguments * @param {string | number} [params.payableAmount] - amount to send in ether unit (optional) * @param {string} [paymaster='0x'] - paymaster contract address (optional) * @returns {Promise} - User Operation hash * */ async contractCall(params, paymaster = '0x') { const contract = new ethers.Contract(params.address, params.abi, this.rpcProvider); const txData = contract.interface.encodeFunctionData(params.method, params.args); return this.execute(txData, params.address, params.payableAmount?.toString() || ethers.toBeHex(0), paymaster); } /** * Used to call contract methods that don't change state * @param params - contract call params * @param {string} params.address - contract address * @param {InterfaceAbi | Interface} params.abi - contract abi * @param {string} params.method - contract method name to call * @param {unknown[]} params.args - contract method arguments * @returns {Promise} - contract method call result * */ async contractRead(params) { const contract = new ethers.Contract(params.address, params.abi, this.rpcProvider); return contract[params.method](...params.args); } async buildAndSendOperation(data, paymaster) { let nonce; try { nonce = await this.getNonce(); } catch (err) { nonce = 0; } const userOperation = await this.operationBuilder.buildOperation({ sender: this.address, callData: data, initCode: await this.getInitCode(), nonce: ethers.toBeHex(nonce), }, paymaster); return this.bundler.sendUserOperation(userOperation); } async getInitCode() { if (this.ready) { return '0x'; } const initializer = await this.getInitializerData(); // FIXME: hardcoded wallet idx const walletSalt = this.getWalletSalt(0); const callData = encodeFunctionData(factoryABI, 'createWallet', [initializer, walletSalt]); return ethers.concat([this.factorySC.target, callData]); } // MODULES /** * Method to install internal modules * @method installModule * @param {TrueWalletModules} module - module to install * @param {unknown} moduleData - data for the module installation * @returns {Promise} - User Operation Response * */ async installModule(module, moduleData) { const moduleAddress = Modules[module]; switch (moduleAddress) { case Modules.SocialRecoveryModule: return await this.socialRecoveryModule.install(moduleData); default: throw new TWUnsupportedModuleError(`Module ${module} is not supported`); } } /** * Method to remove internal modules * @method removeModule * @param {TrueWalletModules} module - module to remove * @returns {Promise} - User Operation Response * */ async removeModule(module) { const moduleAddress = Modules[module]; switch (moduleAddress) { case Modules.SocialRecoveryModule: return this.socialRecoveryModule.remove(); default: throw new TWUnsupportedModuleError(`Module ${module} is not supported`); } } /** * Method to get installed modules * @method getInstalledModules * @returns {Promise} - list of contract addresses of installed modules * */ async getInstalledModules() { return this.walletSC['listModules'](); } /** * Method to get module smart contract address * @method getModuleAddress * @param {TrueWalletModules} module - module * @returns {string} - contract address of the module * */ getModuleAddress(module) { return Modules[module]; } /** * Method to check if given module is installed * @method isModuleInstalled * @param {TrueWalletModules} module - module to check * @returns {Promise} - is module installed * */ async isModuleInstalled(module) { const modules = await this.getInstalledModules(); const moduleAddress = this.getModuleAddress(module); return modules[0].includes(moduleAddress); } /** * Method to check if given address is wallet owner * @method isWalletOwner * @param {string} address - address to check * @returns {Promise} - is wallet owner * */ async isWalletOwner(address) { return this.walletSC['isOwner'](address); } async isWalletReady() { return isContract(this.address, this.rpcProvider); } async getInitializerData() { const args = [ this.entrypointSC.target, this.signer.address, [getSecurityModuleInitData()] ]; return await this.factorySC['getInitializer'](...args); } getWalletSalt(walletIdx) { return ethers.solidityPackedKeccak256(['address', 'address', 'bytes', 'string'], [ this.entrypointSC.target, this.signer.address, getSecurityModuleInitData(), walletIdx.toString(), ]); } }, (() => { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; _getNonce_decorators = [walletReady]; _send_decorators = [onlyOwner]; _deployWallet_decorators = [onlyOwner]; _execute_decorators = [onlyOwner]; _contractCall_decorators = [onlyOwner]; _installModule_decorators = [onlyOwner]; _removeModule_decorators = [onlyOwner]; _getInstalledModules_decorators = [walletReady]; _isWalletOwner_decorators = [walletReady]; __esDecorate(_a, null, _getNonce_decorators, { kind: "method", name: "getNonce", static: false, private: false, access: { has: obj => "getNonce" in obj, get: obj => obj.getNonce }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _send_decorators, { kind: "method", name: "send", static: false, private: false, access: { has: obj => "send" in obj, get: obj => obj.send }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _deployWallet_decorators, { kind: "method", name: "deployWallet", static: false, private: false, access: { has: obj => "deployWallet" in obj, get: obj => obj.deployWallet }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _execute_decorators, { kind: "method", name: "execute", static: false, private: false, access: { has: obj => "execute" in obj, get: obj => obj.execute }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _contractCall_decorators, { kind: "method", name: "contractCall", static: false, private: false, access: { has: obj => "contractCall" in obj, get: obj => obj.contractCall }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _installModule_decorators, { kind: "method", name: "installModule", static: false, private: false, access: { has: obj => "installModule" in obj, get: obj => obj.installModule }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _removeModule_decorators, { kind: "method", name: "removeModule", static: false, private: false, access: { has: obj => "removeModule" in obj, get: obj => obj.removeModule }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _getInstalledModules_decorators, { kind: "method", name: "getInstalledModules", static: false, private: false, access: { has: obj => "getInstalledModules" in obj, get: obj => obj.getInstalledModules }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(_a, null, _isWalletOwner_decorators, { kind: "method", name: "isWalletOwner", static: false, private: false, access: { has: obj => "isWalletOwner" in obj, get: obj => obj.isWalletOwner }, metadata: _metadata }, null, _instanceExtraInitializers); if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); })(), _a; })(); async function initTrueWallet(config) { return await new TrueWalletSDK().init(config); } exports.BundlerError = BundlerError; exports.RecoveryError = RecoveryError; exports.TWCallException = TWCallException; exports.TWConfigError = TWConfigError; exports.TWInvalidSignerTypeError = TWInvalidSignerTypeError; exports.TWJwtInitSignerError = TWJwtInitSignerError; exports.TWJwtSignerInitializedError = TWJwtSignerInitializedError; exports.TWJwtSignerInvalidJwtError = TWJwtSignerInvalidJwtError; exports.TWJwtSignerInvalidKeyError = TWJwtSignerInvalidKeyError; exports.TWJwtSignerLimitError = TWJwtSignerLimitError; exports.TWModuleAlreadyInstalledError = TWModuleAlreadyInstalledError; exports.TWModuleNotInstalledError = TWModuleNotInstalledError; exports.TWOwnerCallError = TWOwnerCallError; exports.TWUnsupportedModuleError = TWUnsupportedModuleError; exports.TWWalletNotReadyError = TWWalletNotReadyError; exports.TrueWalletRecoveryModule = TrueWalletRecoveryModule; exports.encodeFunctionData = encodeFunctionData; exports.fromWei = fromWei; exports.getChecksumAddress = getChecksumAddress; exports.getRecoveryModuleInitData = getRecoveryModuleInitData; exports.initTrueWallet = initTrueWallet; exports.isContract = isContract; exports.isEthAddress = isEthAddress; exports.toWei = toWei;