UNPKG

near-safe

Version:

An SDK for controlling Ethereum Smart Accounts via ERC4337 from a Near Account.

161 lines (160 loc) 6.94 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SafeContractSuite = void 0; const viem_1 = require("viem"); const deployments_1 = require("../_gen/deployments"); const constants_1 = require("../constants"); const util_1 = require("../util"); /** * All contracts used in account creation & execution */ class SafeContractSuite { constructor(rpcUrl = constants_1.DEFAULT_SETUP_RPC) { this.setupClient = (0, viem_1.createPublicClient)({ transport: (0, viem_1.http)(rpcUrl) }); const deployments = deployments_1.SAFE_DEPLOYMENTS; this.singleton = deployments.singleton; this.proxyFactory = deployments.proxyFactory; this.m4337 = deployments.m4337; this.moduleSetup = deployments.moduleSetup; this.entryPoint = deployments.entryPoint; } async addressForSetup(setup, saltNonce) { // bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce)); // cf: https://github.com/safe-global/safe-smart-account/blob/499b17ad0191b575fcadc5cb5b8e3faeae5391ae/contracts/proxies/SafeProxyFactory.sol#L58 const salt = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["bytes32", "uint256"], [(0, viem_1.keccak256)(setup), BigInt(saltNonce)])); // abi.encodePacked(type(SafeProxy).creationCode, uint256(uint160(_singleton))); // cf: https://github.com/safe-global/safe-smart-account/blob/499b17ad0191b575fcadc5cb5b8e3faeae5391ae/contracts/proxies/SafeProxyFactory.sol#L29 const initCode = (0, viem_1.encodePacked)(["bytes", "uint256"], [ (await this.setupClient.readContract({ address: this.proxyFactory.address, abi: this.proxyFactory.abi, functionName: "proxyCreationCode", })), BigInt(this.singleton.address), ]); return (0, viem_1.getCreate2Address)({ from: this.proxyFactory.address, salt, bytecodeHash: (0, viem_1.keccak256)(initCode), }); } getSetup(owners) { return (0, viem_1.encodeFunctionData)({ abi: this.singleton.abi, functionName: "setup", args: [ owners, // _owners 1, // _threshold this.moduleSetup.address, // to (0, viem_1.encodeFunctionData)({ abi: this.moduleSetup.abi, functionName: "enableModules", args: [[this.m4337.address]], }), // data this.m4337.address, // fallbackHandler viem_1.zeroAddress, // paymentToken 0, // payment viem_1.zeroAddress, // paymentReceiver ], }); } addOwnerData(newOwner) { return (0, viem_1.encodeFunctionData)({ abi: this.singleton.abi, functionName: "addOwnerWithThreshold", args: [newOwner, 1], }); } async removeOwnerData(chainId, safeAddress, owner) { const prevOwner = await this.prevOwner(chainId, safeAddress, owner); return (0, viem_1.encodeFunctionData)({ abi: this.singleton.abi, functionName: "removeOwner", // Keep threshold at 1! args: [prevOwner, owner, 1], }); } async getOpHash(chainId, unsignedUserOp) { const { factory, factoryData, verificationGasLimit, callGasLimit, maxPriorityFeePerGas, maxFeePerGas, } = unsignedUserOp; const client = await (0, util_1.getClient)(chainId); const opHash = await client.readContract({ address: this.m4337.address, abi: this.m4337.abi, functionName: "getOperationHash", args: [ { ...unsignedUserOp, initCode: factory ? (0, viem_1.encodePacked)(["address", "bytes"], [factory, factoryData]) : "0x", accountGasLimits: (0, util_1.packGas)(verificationGasLimit, callGasLimit), gasFees: (0, util_1.packGas)(maxPriorityFeePerGas, maxFeePerGas), paymasterAndData: (0, util_1.packPaymasterData)(unsignedUserOp), signature: util_1.PLACEHOLDER_SIG, }, ], }); return opHash; } factoryDataForSetup(safeNotDeployed, setup, safeSaltNonce) { return safeNotDeployed ? { factory: this.proxyFactory.address, factoryData: (0, viem_1.encodeFunctionData)({ abi: this.proxyFactory.abi, functionName: "createProxyWithNonce", args: [this.singleton.address, setup, safeSaltNonce], }), } : {}; } async buildUserOp(nonce, txData, safeAddress, feeData, setup, safeNotDeployed, safeSaltNonce) { return { sender: safeAddress, nonce: (0, viem_1.toHex)(nonce), ...this.factoryDataForSetup(safeNotDeployed, setup, safeSaltNonce), // <https://github.com/safe-global/safe-modules/blob/9a18245f546bf2a8ed9bdc2b04aae44f949ec7a0/modules/4337/contracts/Safe4337Module.sol#L172> callData: (0, viem_1.concat)([ (0, viem_1.encodeFunctionData)({ abi: this.m4337.abi, functionName: "executeUserOp", args: [ (0, viem_1.getAddress)(txData.to), BigInt(txData.value), txData.data, txData.operation || 0, ], }), // Append On-Chain Identifier: constants_1.USER_OP_IDENTIFIER, ]), ...feeData, }; } async getNonce(address, chainId) { const nonce = (await (0, util_1.getClient)(chainId).readContract({ abi: this.entryPoint.abi, address: this.entryPoint.address, functionName: "getNonce", args: [address, 0], })); return nonce; } async prevOwner(chainId, safeAddress, owner) { const client = (0, util_1.getClient)(chainId); const currentOwners = await client.readContract({ address: safeAddress, // abi: this.singleton.abi, abi: (0, viem_1.parseAbi)([ "function getOwners() public view returns (address[] memory)", ]), functionName: "getOwners", }); const ownerIndex = currentOwners.findIndex((t) => t === owner); if (ownerIndex === -1) { throw new Error(`Not a current owner: ${owner}`); } return ownerIndex > 0 ? currentOwners[ownerIndex - 1] : constants_1.SENTINEL_OWNERS; } } exports.SafeContractSuite = SafeContractSuite;