@runonflux/aa-schnorr-multisig-sdk
Version:
Account Abstraction Schnorr Multi-Signatures SDK
115 lines (114 loc) • 7.13 kB
JavaScript
"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;