@verified-network/verified-sdk
Version:
An SDK to develop applications on the Verified Network
999 lines (998 loc) • 39.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createMultiChainNexusAccount = exports.waitForTransactionReceiptsDecorator = exports.unDelegateDecorator = exports.buildDefaultInstructions = exports.createNexusAccount = exports.computeNexusAccount = exports.PARENT_TYPEHASH = exports.EXECUTE_BATCH = exports.MODE_PAYLOAD = exports.UNUSED = exports.MODE_DEFAULT = exports.EXECTYPE_DELEGATE = exports.EXECTYPE_TRY = exports.EXECTYPE_DEFAULT = exports.CALLTYPE_BATCH = exports.CALLTYPE_SINGLE = void 0;
const viem_1 = require("viem");
const boostrap_json_1 = __importDefault(require("./abi/boostrap.json"));
const factory_json_1 = __importDefault(require("./abi/factory.json"));
const entrypoint_1 = require("./abi/entrypoint");
const account_abstraction_1 = require("viem/account-abstraction");
const accounts_1 = require("viem/accounts");
const utils_1 = require("viem/utils");
const composability_1 = require("./abi/composability");
const NEXUS_ACCOUNT_FACTORY_ADDRESS = "0x000000001D1D5004a02bAfAb9de2D6CE5b7B13de";
const NEXUS_BOOTSTRAP_ADDRESS = "0x00000000D3254452a909E4eeD47455Af7E27C289";
const DUMMY_SIGNATURE = "0x81d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b";
const ENTRY_POINT_ADDRESS = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
const ENTRYPOINT_SIMULATIONS_any = "0x74Cb5e4eE81b86e70f9045036a1C5477de69eE87";
exports.CALLTYPE_SINGLE = "0x00"; // 1 byte
exports.CALLTYPE_BATCH = "0x01"; // 1 byte
exports.EXECTYPE_DEFAULT = "0x00"; // 1 byte
exports.EXECTYPE_TRY = "0x01"; // 1 byte
exports.EXECTYPE_DELEGATE = "0xFF"; // 1 byte
exports.MODE_DEFAULT = "0x00000000"; // 4 bytes
exports.UNUSED = "0x00000000"; // 4 bytes
exports.MODE_PAYLOAD = "0x00000000000000000000000000000000000000000000"; // 22 bytes
const EXECUTE_SINGLE = (0, viem_1.concat)([
exports.CALLTYPE_SINGLE,
exports.EXECTYPE_DEFAULT,
exports.MODE_DEFAULT,
exports.UNUSED,
exports.MODE_PAYLOAD,
]);
exports.EXECUTE_BATCH = (0, viem_1.concat)([
exports.CALLTYPE_BATCH,
exports.EXECTYPE_DEFAULT,
exports.MODE_DEFAULT,
exports.UNUSED,
exports.MODE_PAYLOAD,
]);
exports.PARENT_TYPEHASH = "TypedDataSign(Contents contents,string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)Contents(bytes32 stuff)";
const index = BigInt(0);
const toInitData = (mod) => {
const module = mod.module || mod.address;
const data = mod.initData || mod.data;
if (!module || !data) {
throw new Error("Module or data is missing");
}
return { module, data };
};
const getInitData = (params) => (0, viem_1.encodeAbiParameters)([
{ name: "bootstrap", type: "address" },
{ name: "initData", type: "bytes" },
], [
params.bootStrapAddress,
(0, viem_1.encodeFunctionData)({
abi: boostrap_json_1.default.abi,
functionName: "initNexusWithDefaultValidatorAndOtherModulesNoRegistry",
args: [
params.defaultValidator.data,
params.validators,
params.executors,
params.hook,
params.fallbacks,
params.prevalidationHooks,
],
}),
]);
const toValidator = (parameters) => {
const { deInitData = "0x", type = "validator", signer, data = "0x", module, ...rest } = parameters;
return {
deInitData,
data,
module,
address: module,
signer,
type,
getStubSignature: async () => DUMMY_SIGNATURE,
signUserOpHash: async (userOpHash) => await signer.signMessage({ message: { raw: userOpHash } }),
signMessage: async (message) => await signer.signMessage({ message }),
...rest,
};
};
const toDefaultModule = (parameters) => toValidator({
initData: parameters.signer.address,
data: parameters.signer.address,
deInitData: "0x",
...parameters,
address: viem_1.zeroAddress,
module: viem_1.zeroAddress,
type: "validator",
getStubSignature: async () => DUMMY_SIGNATURE,
});
const toEmptyHook = () => ({
module: viem_1.zeroAddress,
data: viem_1.zeroHash,
});
const computeNexusAccount = async (chain, transport, signer) => {
const publicClient = (0, viem_1.createPublicClient)({ chain, transport });
const defaultValidator = toDefaultModule({ signer });
const validators = [];
const executors = [];
const hook = toEmptyHook();
const fallbacks = [];
const registryAddress = viem_1.zeroAddress;
const factoryAddress = NEXUS_ACCOUNT_FACTORY_ADDRESS;
const bootStrapAddress = NEXUS_BOOTSTRAP_ADDRESS;
const prevalidationHooks = [];
const initData = getInitData({
defaultValidator: toInitData(defaultValidator),
validators: validators.map(toInitData),
executors: executors.map(toInitData),
hook: toInitData(hook),
fallbacks: fallbacks.map(toInitData),
registryAddress,
bootStrapAddress,
prevalidationHooks,
});
const salt = (0, viem_1.pad)((0, viem_1.toHex)(index), { size: 32 });
const nexusAccount = await publicClient.readContract({
address: factoryAddress,
abi: factory_json_1.default.abi,
functionName: "computeAccountAddress",
args: [initData, salt],
});
if (nexusAccount?.toLowerCase() !== viem_1.zeroAddress?.toLowerCase()) {
return nexusAccount;
}
return;
};
exports.computeNexusAccount = computeNexusAccount;
const computeNexusAddress = async ({ chain, transport, index, initData, factoryAddress = NEXUS_ACCOUNT_FACTORY_ADDRESS, }) => {
const publicClient = (0, viem_1.createPublicClient)({ chain, transport });
const salt = (0, viem_1.pad)((0, viem_1.toHex)(index), { size: 32 });
const address = await publicClient.readContract({
address: factoryAddress,
abi: factory_json_1.default.abi,
functionName: "computeAccountAddress",
args: [initData, salt],
});
if (!address || address === viem_1.zeroAddress) {
throw new Error("Failed to compute Nexus account address");
}
return address;
};
const buildInitCode = ({ factoryAddress = NEXUS_ACCOUNT_FACTORY_ADDRESS, factoryData, }) => {
return (0, viem_1.concatHex)([factoryAddress, factoryData]);
};
const sanitizeUrl = (value) => {
return value.replace(/https?:\/\/[^\s]+/g, "");
};
class NonceManager {
constructor() {
this.isNonceKeyBeingCalculated = new Map();
}
static getInstance() {
if (!NonceManager.instance) {
NonceManager.instance = new NonceManager();
}
return NonceManager.instance;
}
buildNonceStoreKey(accountAddress, chainId) {
return `${accountAddress.toLowerCase()}::${chainId}`;
}
async getDefaultNonceKey(accountAddress, chainId) {
const storeKey = this.buildNonceStoreKey(accountAddress, chainId);
while (this.isNonceKeyBeingCalculated.get(storeKey)) {
await new Promise((resolve) => setTimeout(resolve, 1));
}
this.isNonceKeyBeingCalculated.set(storeKey, true);
const key = BigInt(Date.now());
await new Promise((resolve) => setTimeout(resolve, 1));
this.isNonceKeyBeingCalculated.set(storeKey, false);
return key;
}
async getNonceWithKey(client, accountAddress, parameters) {
const TIMESTAMP_ADJUSTMENT = BigInt(16777215);
const { key: key_, validationMode, moduleAddress } = parameters;
try {
const adjustedKey = BigInt(key_) % TIMESTAMP_ADJUSTMENT;
const key = (0, viem_1.concat)([
(0, viem_1.toHex)(adjustedKey, { size: 3 }),
validationMode,
moduleAddress,
]);
const entryPointContract = (0, viem_1.getContract)({
address: ENTRY_POINT_ADDRESS,
abi: entrypoint_1.EntrypointAbi,
client,
});
const nonce = await entryPointContract.read.getNonce([
accountAddress,
BigInt(key),
]);
return { nonceKey: BigInt(key), nonce };
}
catch (error) {
const errorMessage = error.message ?? "RPC issue";
throw new Error(`Failed to fetch nonce due to the error: ${sanitizeUrl(errorMessage)}`);
}
}
}
const getDefaultNonceKey = async (accountAddress, chainId) => {
const manager = NonceManager.getInstance();
return manager.getDefaultNonceKey(accountAddress, chainId);
};
const getNonceWithKeyUtil = async (client, accountAddress, parameters) => {
const manager = NonceManager.getInstance();
return manager.getNonceWithKey(client, accountAddress, parameters);
};
const buildNexusRuntime = ({ chain, transport, signer, index, factoryAddress = NEXUS_ACCOUNT_FACTORY_ADDRESS, factoryData, initData, defaultValidator, meeConfig, walletClient, }) => {
const publicClient = (0, viem_1.createPublicClient)({ chain, transport });
let cachedAddress;
let module = defaultValidator;
// Lazy counterfactual address
const getAddress = async () => {
if (cachedAddress)
return cachedAddress;
cachedAddress = await computeNexusAddress({
chain,
transport,
index,
initData,
factoryAddress,
});
return cachedAddress;
};
// Init code for deployment / bundlers
const getInitCode = () => (0, viem_1.concatHex)([factoryAddress, factoryData]);
// Module management
const setModule = (newModule) => {
module = newModule;
};
const getModule = () => module;
// Nonce with key
const getNonceWithKey = async (accountAddress, params) => {
const key = params?.key ?? (await getDefaultNonceKey(accountAddress, chain.id));
const moduleAddress = params?.moduleAddress ?? module.module;
const validationMode = params?.validationMode ?? "0x00";
return getNonceWithKeyUtil(publicClient, accountAddress, {
key,
validationMode,
moduleAddress,
});
};
// User operation hash
const getUserOpHash = (userOp) => {
return (0, account_abstraction_1.getUserOperationHash)({
chainId: chain.id,
entryPointAddress: account_abstraction_1.entryPoint07Address,
entryPointVersion: "0.7",
userOperation: userOp,
});
};
const toDelegation = async (params) => {
const { authorization: authorization_, multiChain, delegatedContract, chainId, } = params || {};
const contractAddress = delegatedContract || meeConfig.implementationAddress;
const authorization = authorization_ ||
(await walletClient.signAuthorization({
contractAddress,
chainId: multiChain ? 0 : chainId,
}));
const eip7702Auth = {
chainId: `0x${authorization.chainId.toString(16)}`,
address: authorization.address,
nonce: `0x${authorization.nonce.toString(16)}`,
r: authorization.r,
s: authorization.s,
yParity: `0x${authorization.yParity.toString(16)}`,
};
return eip7702Auth;
};
const isDelegated = async () => {
const code = await publicClient.getCode({ address: signer.address });
return (!!code &&
code
?.toLowerCase()
.includes(meeConfig.implementationAddress.substring(2).toLowerCase()));
};
const unDelegate = async (params) => {
const { authorization } = params || {};
const deAuthorization = authorization ||
(await walletClient.signAuthorization({
address: viem_1.zeroAddress,
executor: "self",
}));
return await walletClient.sendTransaction({
to: signer.address,
data: "0xdeadbeef",
type: "eip7702",
authorizationList: [deAuthorization],
});
};
return {
getAddress,
getInitCode,
publicClient,
signer,
module,
setModule,
getModule,
getNonceWithKey,
getUserOpHash,
isDelegated,
toDelegation,
unDelegate,
};
};
const encodeExecuteBatch = async (calls, mode = exports.EXECUTE_BATCH) => {
const executionAbiParams = {
type: "tuple[]",
components: [
{ name: "target", type: "address" },
{ name: "value", type: "uint256" },
{ name: "callData", type: "bytes" },
],
};
const executions = calls.map((tx) => ({
target: tx.to,
callData: tx.data ?? "0x",
value: BigInt(tx.value ?? BigInt(0)),
}));
const executionCalldataPrep = (0, viem_1.encodeAbiParameters)([executionAbiParams], [executions]);
return (0, viem_1.encodeFunctionData)({
abi: (0, viem_1.parseAbi)([
"function execute(bytes32 mode, bytes calldata executionCalldata) external",
]),
functionName: "execute",
args: [mode, executionCalldataPrep],
});
};
const encodeExecute = async (call, mode = EXECUTE_SINGLE) => {
const executionCalldata = (0, viem_1.encodePacked)(["address", "uint256", "bytes"], [
call.to,
BigInt(call.value ?? BigInt(0)),
(call.data ?? "0x"),
]);
return (0, viem_1.encodeFunctionData)({
abi: (0, viem_1.parseAbi)([
"function execute(bytes32 mode, bytes calldata executionCalldata) external",
]),
functionName: "execute",
args: [mode, executionCalldata],
});
};
const encodeExecuteComposable = async (calls) => {
const composableCalls = calls.map((call) => {
return {
to: call.to,
value: call.value ?? BigInt(0),
functionSig: call.functionSig,
inputParams: call.inputParams,
outputParams: call.outputParams,
};
});
return (0, viem_1.encodeFunctionData)({
abi: composability_1.COMPOSABILITY_MODULE_ABI,
functionName: "executeComposable",
args: [composableCalls],
});
};
const signMessage = async (message, module) => {
const tempSignature = await module.signMessage(message);
return (0, viem_1.encodePacked)(["address", "bytes"], [module.module, tempSignature]);
};
const signUserOperation = async (userOpHash, module) => {
return module.signUserOpHash(userOpHash);
};
const toSigner = async ({ signer, address, }) => {
if (!signer) {
throw new Error("Signer is required");
}
if ("provider" in signer) {
const wallet = signer;
const address = await wallet.getAddress();
return (0, accounts_1.toAccount)({
address: (0, viem_1.getAddress)(address),
async signMessage({ message }) {
if (typeof message === "string") {
return await wallet.signMessage(message);
}
if (typeof message?.raw === "string") {
return await wallet.signMessage((0, viem_1.hexToBytes)(message.raw));
}
return await wallet.signMessage(message.raw);
},
async signTransaction(_) {
throw new Error("Not supported");
},
async signTypedData(typedData) {
return wallet.signTypedData(typedData.domain, typedData.types, typedData.message);
},
});
}
if ("type" in signer && signer.type === "local") {
return signer;
}
let walletClient = undefined;
if ("request" in signer) {
if (!address) {
try {
[address] = await signer.request({
method: "eth_requestAccounts",
});
}
catch {
[address] = await signer.request({
method: "eth_accounts",
});
}
}
if (!address) {
// For TS to be happy
throw new Error("address is required");
}
walletClient = (0, viem_1.createWalletClient)({
account: address,
transport: (0, viem_1.custom)(signer),
});
}
if (!walletClient) {
walletClient = signer;
}
const addressFromWalletClient = walletClient?.account?.address ?? (await walletClient?.getAddresses())?.[0];
if (!addressFromWalletClient) {
throw new Error("address not found in wallet client");
}
return (0, accounts_1.toAccount)({
address: addressFromWalletClient,
async signMessage({ message }) {
return walletClient.signMessage({ message });
},
async signTypedData(typedData) {
return (0, utils_1.getAction)(walletClient, accounts_1.signTypedData, "signTypedData")(typedData);
},
async signTransaction(_) {
throw new Error("Not supported");
},
});
};
const toWalletClient = ({ unresolvedSigner, resolvedSigner, chain, transport, }) => {
const browserSigner = unresolvedSigner?.transport?.key === "custom";
return (0, viem_1.createWalletClient)(browserSigner
? {
account: resolvedSigner.address,
chain,
// @ts-ignore
transport: (0, viem_1.custom)(window?.ethereum),
}
: {
account: resolvedSigner,
chain,
transport,
}).extend(viem_1.publicActions);
};
const prepareValidators = async (signer, meeConfig, customValidators) => {
return [];
};
const prepareExecutors = (meeConfig, customExecutors) => {
let executors = [];
executors = customExecutors || [];
return executors;
};
const prepareFallbacks = (meeConfig, customFallbacks) => {
let fallbacks = [];
fallbacks = customFallbacks || [];
return fallbacks;
};
const getInitDataNoRegistry = (params) => {
try {
return (0, viem_1.encodeAbiParameters)([
{ name: "bootstrap", type: "address" },
{ name: "bootstrapData", type: "bytes" },
], [
params.bootStrapAddress,
(0, viem_1.encodeFunctionData)({
abi: boostrap_json_1.default?.abi,
functionName: "initNexusWithDefaultValidatorAndOtherModulesNoRegistry",
args: [
params.defaultValidator.data,
params.validators,
params.executors,
params.hook,
params.fallbacks,
params.prevalidationHooks,
],
}),
]);
}
catch (err) {
console.error("getInitDataNoRegistry failed with error: ", err);
}
};
const getFactoryData = (parameters) => {
const { index, initData } = parameters;
const salt = (0, viem_1.pad)((0, viem_1.toHex)(index), { size: 32 });
return (0, viem_1.encodeFunctionData)({
abi: (0, viem_1.parseAbi)([
"function createAccount(bytes initData, bytes32 salt) external returns (address)",
]),
functionName: "createAccount",
args: [initData, salt],
});
};
const prepareFactoryData = (meeConfig, initDataParams) => {
let factoryData = "0x";
let initData = "0x";
initData =
initDataParams.customInitData ||
getInitDataNoRegistry({
defaultValidator: initDataParams.defaultValidator,
prevalidationHooks: initDataParams.prevalidationHooks,
validators: initDataParams.validators,
executors: initDataParams.executors,
hook: initDataParams.hook,
fallbacks: initDataParams.fallbacks,
bootStrapAddress: meeConfig.bootStrapAddress,
});
factoryData = getFactoryData({
initData,
index: initDataParams.accountIndex,
});
return { initData, factoryData };
};
const getAccountDomainStructFields = async (publicClient, accountAddress) => {
const accountDomainStructFields = (await publicClient.readContract({
address: accountAddress,
abi: (0, viem_1.parseAbi)([
"function eip712Domain() public view returns (bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions)",
]),
functionName: "eip712Domain",
}));
const [, name, version, chainId, verifyingContract, salt] = accountDomainStructFields;
const params = (0, viem_1.parseAbiParameters)([
"bytes32",
"bytes32",
"uint256",
"address",
"bytes32",
]);
return (0, viem_1.encodeAbiParameters)(params, [
(0, viem_1.keccak256)((0, viem_1.toBytes)(name)),
(0, viem_1.keccak256)((0, viem_1.toBytes)(version)),
chainId,
verifyingContract,
salt,
]);
};
const eip712WrapHash = (typedHash, appDomainSeparator) => (0, viem_1.keccak256)((0, viem_1.concat)(["0x1901", appDomainSeparator, typedHash]));
const typeToString = (typeDef) => {
return Object.entries(typeDef).map(([key, fields]) => {
const fieldArray = Array.isArray(fields) ? fields : [];
const fieldStrings = fieldArray
.map((field) => `${field.type} ${field.name}`)
.join(",");
return `${key}(${fieldStrings})`;
});
};
const createNexusAccount = async ({ chain, transport, _signer, index = BigInt(0), customValidators, customExecutors, customHook, customFallbacks, customPrevalidationHooks, customInitData, meeConfig, }) => {
const publicClient = (0, viem_1.createPublicClient)({ chain, transport });
const addressesToDeploymentSet = new Set([
meeConfig.bootStrapAddress,
meeConfig.defaultValidatorAddress,
meeConfig.validatorAddress,
meeConfig.factoryAddress,
meeConfig.implementationAddress,
]);
if (meeConfig.moduleRegistry) {
addressesToDeploymentSet.add(meeConfig.moduleRegistry.registryAddress);
}
if (meeConfig.composableModuleAddress) {
addressesToDeploymentSet.add(meeConfig.composableModuleAddress);
}
// Filtering zero address because sometimes the default validator is zeroAddress which needs to be excluded
const addressesToDeploymentCheck = [...addressesToDeploymentSet].filter((address) => address !== viem_1.zeroAddress);
await Promise.all(addressesToDeploymentCheck.map(async (address) => {
// Checks if the MEE contracts are deployed or not
// This ensures the MEE version suite is supported or not for the chain
const bytecode = await publicClient.getCode({
address,
});
if (!bytecode || bytecode === "0x") {
console.debug(`MEE version (${meeConfig.version}) is not supported for the ${chain.name} chain. Contract address (${address}) is not deployed`);
throw new Error(`MEE version (${meeConfig.version}) is not supported for the ${chain.name} chain.`);
}
}));
const signer = await toSigner({ signer: _signer });
const walletClient = toWalletClient({
unresolvedSigner: _signer,
resolvedSigner: signer,
chain,
transport,
});
// Prepare validator modules
const validators = await prepareValidators(signer, meeConfig, customValidators);
const defaultValidator = toDefaultModule({ signer });
// For 1.2.x accounts, no explicit validators will be added. So default validator will be used
let module = validators[0] || defaultValidator;
// Prepare executor modules
const executors = prepareExecutors(meeConfig, customExecutors);
// Prepare hook module
const hook = customHook || toEmptyHook();
// Prepare fallback modules
const fallbacks = prepareFallbacks(meeConfig, customFallbacks);
// Generate the initialization data for the account using the initNexus function
const prevalidationHooks = customPrevalidationHooks || [];
// prepare factory data
const { initData, factoryData } = prepareFactoryData(meeConfig, {
accountIndex: index,
defaultValidator: toInitData(defaultValidator),
prevalidationHooks,
validators: validators.map(toInitData),
executors: executors.map(toInitData),
hook: toInitData(hook),
fallbacks: fallbacks.map(toInitData),
customInitData,
});
let accountId;
if (meeConfig?.implementationAddress) {
accountId = (await publicClient.readContract({
address: meeConfig.implementationAddress,
abi: (0, viem_1.parseAbi)(["function accountId() public view returns (string)"]),
functionName: "accountId",
args: [],
}));
}
const runtime = buildNexusRuntime({
chain,
transport,
signer,
index,
factoryAddress: NEXUS_ACCOUNT_FACTORY_ADDRESS,
factoryData,
initData,
defaultValidator,
meeConfig,
walletClient,
});
const getNonce = async (parameters) => {
const accountAddress = await runtime.getAddress();
const { nonce } = await runtime.getNonceWithKey(accountAddress, parameters);
return nonce;
};
const signTypedData = async (parameters) => {
const { message, primaryType, types: _types, domain } = parameters;
if (!domain)
throw new Error("Missing domain");
if (!message)
throw new Error("Missing message");
const types = {
EIP712Domain: (0, viem_1.getTypesForEIP712Domain)({ domain }),
..._types,
};
// @ts-ignore: Comes from nexus parent typehash
const messageStuff = message.stuff;
// @ts-ignore
validateTypedData({
domain,
message,
primaryType,
types,
});
const appDomainSeparator = (0, viem_1.domainSeparator)({ domain });
const accountDomainStructFields = await getAccountDomainStructFields(publicClient, await runtime.getAddress());
const parentStructHash = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["bytes", "bytes"], [
(0, viem_1.encodeAbiParameters)((0, viem_1.parseAbiParameters)(["bytes32, bytes32"]), [
(0, viem_1.keccak256)((0, viem_1.toBytes)(exports.PARENT_TYPEHASH)),
messageStuff,
]),
accountDomainStructFields,
]));
const wrappedTypedHash = eip712WrapHash(parentStructHash, appDomainSeparator);
let signature = await module.signMessage({
raw: (0, viem_1.toBytes)(wrappedTypedHash),
});
const contentsType = (0, viem_1.toBytes)(typeToString(types)[1]);
const signatureData = (0, viem_1.concatHex)([
signature,
appDomainSeparator,
messageStuff,
(0, viem_1.toHex)(contentsType),
(0, viem_1.toHex)(contentsType.length, { size: 2 }),
]);
signature = (0, viem_1.encodePacked)(["address", "bytes"], [module.module, signatureData]);
return signature;
};
return (0, account_abstraction_1.toSmartAccount)({
client: publicClient,
entryPoint: {
abi: entrypoint_1.EntrypointAbi,
address: ENTRY_POINT_ADDRESS,
version: "0.7",
},
getAddress: runtime.getAddress,
encodeCalls: (calls) => {
return calls.length === 1
? encodeExecute(calls[0], runtime.module)
: encodeExecuteBatch(calls, runtime.module);
},
getFactoryArgs: async () => ({
factory: meeConfig.factoryAddress,
factoryData,
}),
getStubSignature: async () => module.getStubSignature(),
/**
* @description Signs a message
* @param params - The parameters for signing
* @param params.message - The message to sign
* @returns The signature
*/
async signMessage({ message }) {
const tempSignature = await module.signMessage(message);
return (0, viem_1.encodePacked)(["address", "bytes"], [module.module, tempSignature]);
},
signTypedData,
signUserOperation: async (parameters) => {
const { chainId = publicClient.chain.id, ...userOpWithoutSender } = parameters;
const address = await runtime.getAddress();
const userOperation = {
...userOpWithoutSender,
sender: address,
};
const hash = (0, account_abstraction_1.getUserOperationHash)({
chainId,
entryPointAddress: account_abstraction_1.entryPoint07Address,
entryPointVersion: "0.7",
userOperation,
});
return await module.signUserOpHash(hash);
},
getNonce,
extend: {
entryPointAddress: account_abstraction_1.entryPoint07Address,
accountId,
encodeExecute,
encodeExecuteBatch,
encodeExecuteComposable,
factoryData,
registryAddress: meeConfig.moduleRegistry?.registryAddress || viem_1.zeroAddress,
walletClient,
version: meeConfig,
isDelegated: runtime.isDelegated,
toDelegation: runtime.toDelegation,
unDelegate: runtime.unDelegate,
getAddress: runtime.getAddress,
getInitCode: runtime.getInitCode,
getNonceWithKey: runtime.getNonceWithKey,
getUserOpHash: runtime.getUserOpHash,
setModule: runtime.setModule,
getModule: runtime.getModule,
factoryAddress: NEXUS_ACCOUNT_FACTORY_ADDRESS,
signer: runtime.signer,
publicClient: runtime.publicClient,
chain,
module: runtime.module,
},
});
};
exports.createNexusAccount = createNexusAccount;
const buildChainConfigurations = (chains, transports) => {
if (chains.length !== transports.length) {
throw new Error("chains and transports length mismatch");
}
return chains.map((chain, i) => ({
chain,
transport: transports[i],
}));
};
const buildDefaultInstructions = async (baseParams, instructions) => {
const { currentInstructions = [] } = baseParams;
return [
...currentInstructions,
...(Array.isArray(instructions) ? instructions : [instructions]),
];
};
exports.buildDefaultInstructions = buildDefaultInstructions;
const buildDecorator = async (baseParams, parameters) => {
const { type, data } = parameters;
switch (type) {
// case "intent": {
// return buildIntent(baseParams, data)
// }
case "default": {
return (0, exports.buildDefaultInstructions)(baseParams, data);
}
// case "transferFrom": {
// return buildTransferFrom(baseParams, data)
// }
// case "transfer": {
// return buildTransfer(baseParams, data)
// }
// case "approve": {
// return buildApprove(baseParams, data)
// }
// case "withdrawal": {
// return buildWithdrawal(baseParams, data)
// }
// case "batch": {
// return buildBatch(baseParams, data)
// }
// case "multichain": {
// return buildMultichainInstructions(baseParams, data)
// }
default: {
throw new Error(`Unknown build action type: ${type}`);
}
}
};
const getUnifiedERC20BalanceDecorator = async (parameters) => {
const { mcToken, account: account_ } = parameters;
const relevantTokensByChain = Array.from(mcToken.deployments).filter(([chainId]) => account_.deployments.some((account) => account.client.chain?.id === chainId));
const balances = await Promise.all(relevantTokensByChain.map(async ([chainId, address]) => {
const { publicClient, address: accountAddress } = account_.deploymentOn(chainId, true);
const tokenContract = (0, viem_1.getContract)({
abi: viem_1.erc20Abi,
address,
client: publicClient,
});
const [balance, decimals] = await Promise.all([
tokenContract.read.balanceOf([accountAddress]),
tokenContract.read.decimals(),
]);
return {
balance,
decimals,
chainId,
};
}));
return {
...balances
.map((balance) => {
return {
balance: balance.balance,
decimals: balance.decimals,
};
})
.reduce((curr, acc) => {
if (curr.decimals !== acc.decimals) {
throw Error(`
Error while trying to fetch a unified ERC20 balance. The addresses provided
in the mapping don't have the same number of decimals across all chains.
The function can't fetch a unified balance for token mappings with differing
decimals.
`);
}
return {
balance: curr.balance + acc.balance,
decimals: curr.decimals,
};
}),
breakdown: balances,
mcToken,
};
};
const waitForTransactionReceipts = async (parameters) => {
const receipts = await Promise.all(parameters.account.deployments.map(({ publicClient }, i) => publicClient.waitForTransactionReceipt({
hash: parameters.hashes[i],
confirmations: 5,
})));
const failure = receipts.find((receipt) => receipt.status !== "success");
return { receipts, status: failure ? failure.status : "success" };
};
const isDelegatedDecorator = async (parameters) => (await Promise.all(parameters.account.deployments.map(({ isDelegated }) => isDelegated()))).every(Boolean);
const unDelegateDecorator = async (parameters) => {
const hashes = await Promise.all(parameters.account.deployments.map(({ unDelegate }) => unDelegate()));
return await waitForTransactionReceipts({
account: parameters.account,
hashes,
});
};
exports.unDelegateDecorator = unDelegateDecorator;
const waitForTransactionReceiptsDecorator = async (parameters) => {
const receipts = await Promise.all(parameters.account.deployments.map(({ publicClient }, i) => publicClient.waitForTransactionReceipt({
hash: parameters.hashes[i],
confirmations: 5,
})));
const failure = receipts.find((receipt) => receipt.status !== "success");
return { receipts, status: failure ? failure.status : "success" };
};
exports.waitForTransactionReceiptsDecorator = waitForTransactionReceiptsDecorator;
const createMultiChainNexusAccount = async (params) => {
const { signer: unresolvedSigner, chains, transports, ...accountParameters } = params;
if (!chains?.length) {
throw new Error("No chains provided");
}
const chainConfigurations = buildChainConfigurations(chains, transports);
if (chainConfigurations.length === 0) {
throw new Error("No chain configuration provided");
}
const meeConfig = {
version: "2.0.0",
accountId: "biconomy.nexus.1.2.0",
factoryAddress: "0x000000001D1D5004a02bAfAb9de2D6CE5b7B13de", // Nexus Account Factory Address
bootStrapAddress: "0x00000000D3254452a909E4eeD47455Af7E27C289",
implementationAddress: "0x000000004F43C49e93C970E84001853a70923B03",
validatorAddress: "0x00000000d12897DDAdC2044614A9677B191A2d95", // K1 MEE Validator Address
defaultValidatorAddress: viem_1.zeroAddress,
ethForwarderAddress: "0x000000Afe527A978Ecb761008Af475cfF04132a1",
};
// 🔁 Create one Nexus account per chain
const deployments = await Promise.all(chainConfigurations.map(async ({ chain, transport }) => (0, exports.createNexusAccount)({
chain,
transport,
_signer: unresolvedSigner,
meeConfig,
...accountParameters,
})));
/* -------------------------------------------------- */
/* Deployment helpers */
/* -------------------------------------------------- */
function deploymentOn(chainId, strictMode) {
const deployment = deployments.find((dep) => dep.publicClient.chain?.id === chainId);
if (!deployment && strictMode) {
throw new Error(`Deployment not found for chainId: ${chainId}`);
}
return deployment;
}
function addressOn(chainId, strictMode) {
return deploymentOn(chainId, strictMode)?.address;
}
/* -------------------------------------------------- */
/* Base multichain account */
/* -------------------------------------------------- */
const baseAccount = {
signer: deployments[0].signer, // resolved signer
deployments,
deploymentOn,
addressOn,
};
// /* -------------------------------------------------- */
// /* Decorators (SDK parity) */
// /* -------------------------------------------------- */
const getUnifiedERC20Balance = (mcToken) => getUnifiedERC20BalanceDecorator({
mcToken,
account: baseAccount,
});
const build = (params, currentInstructions) => buildDecorator({
currentInstructions,
accountAddress: baseAccount.signer.address,
}, params);
// const buildComposable = (
// params: any,
// currentInstructions?: any[]
// ) =>
// buildComposableDecorator(
// {
// currentInstructions,
// accountAddress: baseAccount.signer.address
// },
// params
// )
// const buildBridgeInstructions = (
// params: any
// ) =>
// buildBridgeInstructionsDecorator({
// ...params
// })
// const queryBridge = (params: any) =>
// queryBridgeDecorator({ ...params })
const isDelegated = (parameters) => isDelegatedDecorator({
...parameters,
account: baseAccount,
});
const unDelegate = (parameters) => (0, exports.unDelegateDecorator)({
...parameters,
account: baseAccount,
});
const waitForTransactionReceipts = (parameters) => (0, exports.waitForTransactionReceiptsDecorator)({
...parameters,
account: baseAccount,
});
// const read = <T>(params: any) =>
// multichainRead(baseAccount, params) as Promise<
// any
// >
/* -------------------------------------------------- */
/* Delegation (chainId = 0 semantics) */
/* -------------------------------------------------- */
const toDelegation = async () => await deployments[0].extend.toDelegation({ multiChain: true });
/* -------------------------------------------------- */
/* Final multichain account */
/* -------------------------------------------------- */
return {
...baseAccount,
getUnifiedERC20Balance,
build,
// buildComposable,
// buildBridgeInstructions,
// queryBridge,
isDelegated,
unDelegate,
waitForTransactionReceipts,
// read,
toDelegation,
};
};
exports.createMultiChainNexusAccount = createMultiChainNexusAccount;