UNPKG

@runonflux/aa-schnorr-multisig-sdk

Version:

Account Abstraction Schnorr Multi-Signatures SDK

115 lines (114 loc) 7.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.saltToHex = exports.ENTRY_POINT_ALCHEMY_ADDRESS = exports.PROXY_FACTORY_ADDRESS = void 0; exports.predictAccountAddrOffchain = predictAccountAddrOffchain; exports.predictFactoryAddrOffchain = predictFactoryAddrOffchain; exports.predictAccountImplementationAddrOffchain = predictAccountImplementationAddrOffchain; exports.predictAccountAddrOnchain = predictAccountAddrOnchain; exports.getAccountImplementationAddress = getAccountImplementationAddress; const ethers_1 = require("ethers"); const typechain_1 = require("../generated/typechain"); const abi_1 = require("../generated/abi"); // Proxy address contract used to deploy (using create2) new MultiSigSmartAccount Factory // see: https://github.com/Arachnid/deterministic-deployment-proxy exports.PROXY_FACTORY_ADDRESS = "0x4e59b44847b379578588920ca78fbf26c0b4956c"; // Alchemy Supported Entry Point // see: https://docs.alchemy.com/reference/eth-supportedentrypoints exports.ENTRY_POINT_ALCHEMY_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; /** * Calculates offchain MultiSigSmartAccount address with create2. * @param factoryAddress MultiSigSmartAccountFactory address * @param accountImplementationAddress MultiSigSmartAccount implementation address * @param combinedAddresses combined schnorr signers' public addresses used as contract owners * @param salt salt text: string or number * @returns predicted MultiSigSmartAccount address */ function predictAccountAddrOffchain(factoryAddress, accountImplementationAddress, combinedAddresses, salt) { // ERC1967Proxy is the contract which gets deployed when creating new MultiSigSmartAccount const erc1967ProxyBytecode = typechain_1.ERC1967Proxy__factory.bytecode; const smartAccountInterface = typechain_1.MultiSigSmartAccount__factory.createInterface(); const encodedInitializer = smartAccountInterface.encodeFunctionData("initialize", [combinedAddresses]); // ERC1967Proxy takes two parameters while deploying: implementation and encoded init data const coder = ethers_1.AbiCoder.defaultAbiCoder(); const encodedConstructorInitCode = coder.encode(["address", "bytes"], [accountImplementationAddress, encodedInitializer]); // Calculating initCodeHash keccak256(contractByteCode+ConstructorCode) const initByteCode = ethers_1.ethers.solidityPacked(["bytes", "bytes"], [erc1967ProxyBytecode, encodedConstructorInitCode]); const initCodeHash = ethers_1.ethers.keccak256(initByteCode); return ethers_1.ethers.getCreate2Address(factoryAddress, (0, exports.saltToHex)(salt), initCodeHash); } /** * Calculates offchain MultiSigSmartAccount address with create2 * @param salt salt text: string or number * @param entryPointAddress Account Abstraction's Entry Point address (default: Alchemy Entry Point) * @returns predicted MultiSigSmartAccount Factory address */ function predictFactoryAddrOffchain(salt, entryPointAddress) { const factoryBytecode = typechain_1.MultiSigSmartAccountFactory__factory.bytecode; const saltHex = (0, exports.saltToHex)(salt); const entryPointAddr = entryPointAddress ?? exports.ENTRY_POINT_ALCHEMY_ADDRESS; // Factory takes only one parameter while deploying: Entry Point address const coder = ethers_1.AbiCoder.defaultAbiCoder(); const encodedConstructorInitCode = coder.encode(["address", "bytes32"], [entryPointAddr, saltHex]); // Calculating initCodeHash keccak256(contractByteCode+ConstructorCode) const initCode = ethers_1.ethers.solidityPacked(["bytes", "bytes"], [factoryBytecode, encodedConstructorInitCode]); const initCodeHash = ethers_1.ethers.keccak256(initCode); return ethers_1.ethers.getCreate2Address(exports.PROXY_FACTORY_ADDRESS, saltHex, initCodeHash); } /** * Calculates offchain MultiSigSmartAccount implementation address with create2 * @param factoryAddress MultiSigSmartAccount Factory address. * If not known, can be predicted with given `factorySalt` and `entryPointAddress` params * @param factorySalt salt text: string or number - the same used for Factory deployment * @param entryPointAddress Account Abstraction's Entry Point address (default: Alchemy Entry Point) * @returns */ function predictAccountImplementationAddrOffchain(factorySalt, factoryAddress, entryPointAddress) { const entryPointAddr = entryPointAddress ?? exports.ENTRY_POINT_ALCHEMY_ADDRESS; const factorySaltHex = (0, exports.saltToHex)(factorySalt); const multiSigFactoryAddress = factoryAddress ?? predictFactoryAddrOffchain(factorySalt, entryPointAddr); const smartAccountByteCode = typechain_1.MultiSigSmartAccount__factory.bytecode; // Factory takes only one parameter while deploying: Entry Point address const coder = ethers_1.AbiCoder.defaultAbiCoder(); const encodedConstructorInitCode = coder.encode(["address"], [entryPointAddr]); // Calculating initCodeHash keccak256(contractByteCode+ConstructorCode) const initCode = ethers_1.ethers.solidityPacked(["bytes", "bytes"], [smartAccountByteCode, encodedConstructorInitCode]); const initCodeHash = ethers_1.ethers.keccak256(initCode); return ethers_1.ethers.getCreate2Address(multiSigFactoryAddress, factorySaltHex, initCodeHash); } /** * Calculates MultiSigSmartAccount address with create2 and onchain data. * @param factoryAddress MultiSigSmartAccountFactory address * @param combinedAddresses combined schnorr signers' public addresses used as contract owners * @param salt salt text: string or number * @param ethersSignerOrProvider Signer or Provider type to call the Factory contract * @returns predicted MultiSigSmartAccount address */ async function predictAccountAddrOnchain(factoryAddress, combinedAddresses, salt, ethersSignerOrProvider) { const smartAccountFactory = new ethers_1.ethers.Contract(factoryAddress, abi_1.MultiSigSmartAccountFactory_abi, ethersSignerOrProvider); const saltHash = (0, exports.saltToHex)(salt); const predictedAccount = await smartAccountFactory.getAccountAddress(combinedAddresses, saltHash); return predictedAccount; } /** * Helper for getting account implementation address from Account Factory * @param factoryAddress deployed MultiSigSmartAccountFactory address * @param ethersSignerOrProvider signer or provider to call contract * @returns account implementation address */ async function getAccountImplementationAddress(factoryAddress, ethersSignerOrProvider) { const smartAccountFactory = new ethers_1.ethers.Contract(factoryAddress, abi_1.MultiSigSmartAccountFactory_abi, ethersSignerOrProvider); const accountImplementation = await smartAccountFactory.accountImplementation(); return accountImplementation; } /** * Checks if salt is Hex and if not - converts from string or number to hashed string with keccak256. * @param salt salt text: string or number * @returns hashed salt */ const saltToHex = (salt) => { const saltString = salt.toString(); if (ethers_1.ethers.isHexString(saltString)) return saltString; return ethers_1.ethers.id(saltString); }; exports.saltToHex = saltToHex;