permissionless
Version:
A utility library for working with ERC-4337
603 lines • 25.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getEcdsaRootIdentifierForKernelV3 = exports.KERNEL_VERSION_TO_ADDRESSES_MAP = exports.MIGRATION_HELPER_ADDRESS = void 0;
exports.toKernelSmartAccount = toKernelSmartAccount;
const viem_1 = require("viem");
const account_abstraction_1 = require("viem/account-abstraction");
const actions_1 = require("viem/actions");
const utils_1 = require("viem/utils");
const getAccountNonce_js_1 = require("../../actions/public/getAccountNonce.js");
const getSenderAddress_js_1 = require("../../actions/public/getSenderAddress.js");
const encode7579Calls_js_1 = require("../../utils/encode7579Calls.js");
const encodeInstallModule_js_1 = require("../../utils/encodeInstallModule.js");
const encodeUninstallModule_js_1 = require("../../utils/encodeUninstallModule.js");
const ox_js_1 = require("../../utils/ox.js");
const toOwner_js_1 = require("../../utils/toOwner.js");
const KernelAccountAbi_js_1 = require("./abi/KernelAccountAbi.js");
const KernelV3AccountAbi_js_1 = require("./abi/KernelV3AccountAbi.js");
const KernelV3FactoryAbi_js_1 = require("./abi/KernelV3FactoryAbi.js");
const KernelV3MetaFactoryAbi_js_1 = require("./abi/KernelV3MetaFactoryAbi.js");
const constants_js_1 = require("./constants.js");
const decodeCallData_js_1 = require("./utils/decodeCallData.js");
const encodeCallData_js_1 = require("./utils/encodeCallData.js");
const getNonceKey_js_1 = require("./utils/getNonceKey.js");
const isKernelV2_js_1 = require("./utils/isKernelV2.js");
const isWebAuthnAccount_js_1 = require("./utils/isWebAuthnAccount.js");
const signMessage_js_1 = require("./utils/signMessage.js");
const signTypedData_js_1 = require("./utils/signTypedData.js");
const migrationHelperAbi = [
{
type: "function",
name: "migrateWithCall",
inputs: [
{
name: "validators",
type: "address[]",
internalType: "contract IValidator[]"
},
{
name: "permissionIds",
type: "bytes4[]",
internalType: "bytes4[]"
},
{ name: "execMode", type: "bytes32", internalType: "ExecMode" },
{ name: "execData", type: "bytes", internalType: "bytes" }
],
outputs: [],
stateMutability: "nonpayable"
}
];
const createAccountAbi = [
{
inputs: [
{
internalType: "address",
name: "_implementation",
type: "address"
},
{
internalType: "bytes",
name: "_data",
type: "bytes"
},
{
internalType: "uint256",
name: "_index",
type: "uint256"
}
],
name: "createAccount",
outputs: [
{
internalType: "address",
name: "proxy",
type: "address"
}
],
stateMutability: "payable",
type: "function"
}
];
exports.MIGRATION_HELPER_ADDRESS = "0x03EB97959433D55748839D27C93330Cb85F31A93";
exports.KERNEL_VERSION_TO_ADDRESSES_MAP = {
"0.2.1": {
ECDSA_VALIDATOR: "0xd9AB5096a832b9ce79914329DAEE236f8Eea0390",
ACCOUNT_LOGIC: "0xf048AD83CB2dfd6037A43902a2A5Be04e53cd2Eb",
FACTORY_ADDRESS: "0x5de4839a76cf55d0c90e2061ef4386d962E15ae3"
},
"0.2.2": {
ECDSA_VALIDATOR: "0xd9AB5096a832b9ce79914329DAEE236f8Eea0390",
ACCOUNT_LOGIC: "0x0DA6a956B9488eD4dd761E59f52FDc6c8068E6B5",
FACTORY_ADDRESS: "0x5de4839a76cf55d0c90e2061ef4386d962E15ae3"
},
"0.2.3": {
ECDSA_VALIDATOR: "0xd9AB5096a832b9ce79914329DAEE236f8Eea0390",
ACCOUNT_LOGIC: "0xD3F582F6B4814E989Ee8E96bc3175320B5A540ab",
FACTORY_ADDRESS: "0x5de4839a76cf55d0c90e2061ef4386d962E15ae3"
},
"0.2.4": {
ECDSA_VALIDATOR: "0xd9AB5096a832b9ce79914329DAEE236f8Eea0390",
ACCOUNT_LOGIC: "0xd3082872F8B06073A021b4602e022d5A070d7cfC",
FACTORY_ADDRESS: "0x5de4839a76cf55d0c90e2061ef4386d962E15ae3"
},
"0.3.0-beta": {
ECDSA_VALIDATOR: "0x8104e3Ad430EA6d354d013A6789fDFc71E671c43",
ACCOUNT_LOGIC: "0x94F097E1ebEB4ecA3AAE54cabb08905B239A7D27",
FACTORY_ADDRESS: "0x6723b44Abeec4E71eBE3232BD5B455805baDD22f",
META_FACTORY_ADDRESS: "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
WEB_AUTHN_VALIDATOR: "0x7ab16Ff354AcB328452F1D445b3Ddee9a91e9e69"
},
"0.3.1": {
ECDSA_VALIDATOR: "0x845ADb2C711129d4f3966735eD98a9F09fC4cE57",
ACCOUNT_LOGIC: "0xBAC849bB641841b44E965fB01A4Bf5F074f84b4D",
FACTORY_ADDRESS: "0xaac5D4240AF87249B3f71BC8E4A2cae074A3E419",
META_FACTORY_ADDRESS: "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
WEB_AUTHN_VALIDATOR: "0x7ab16Ff354AcB328452F1D445b3Ddee9a91e9e69"
},
"0.3.2": {
ECDSA_VALIDATOR: "0x845ADb2C711129d4f3966735eD98a9F09fC4cE57",
ACCOUNT_LOGIC: "0xD830D15D3dc0C269F3dBAa0F3e8626d33CFdaBe1",
FACTORY_ADDRESS: "0x7a1dBAB750f12a90EB1B60D2Ae3aD17D4D81EfFe",
META_FACTORY_ADDRESS: "0xd703aaE79538628d27099B8c4f621bE4CCd142d5"
},
"0.3.3": {
ECDSA_VALIDATOR: "0x845ADb2C711129d4f3966735eD98a9F09fC4cE57",
ACCOUNT_LOGIC: "0xd6CEDDe84be40893d153Be9d467CD6aD37875b28",
FACTORY_ADDRESS: "0x2577507b78c2008Ff367261CB6285d44ba5eF2E9",
META_FACTORY_ADDRESS: "0xd703aaE79538628d27099B8c4f621bE4CCd142d5"
}
};
const getDefaultKernelVersion = (entryPointVersion, version, eip7702) => {
if (eip7702) {
return "0.3.3";
}
if (version) {
return version;
}
return (entryPointVersion === "0.6" ? "0.2.2" : "0.3.0-beta");
};
const getDefaultAddresses = ({ validatorAddress: _validatorAddress, accountLogicAddress: _accountLogicAddress, factoryAddress: _factoryAddress, metaFactoryAddress: _metaFactoryAddress, kernelVersion, isWebAuthn }) => {
const addresses = exports.KERNEL_VERSION_TO_ADDRESSES_MAP[kernelVersion];
const validatorAddress = _validatorAddress ??
(isWebAuthn ? addresses.WEB_AUTHN_VALIDATOR : addresses.ECDSA_VALIDATOR);
const accountLogicAddress = _accountLogicAddress ?? addresses.ACCOUNT_LOGIC;
const factoryAddress = _factoryAddress ?? addresses.FACTORY_ADDRESS;
const metaFactoryAddress = _metaFactoryAddress ?? addresses?.META_FACTORY_ADDRESS ?? viem_1.zeroAddress;
return {
validatorAddress,
accountLogicAddress,
factoryAddress,
metaFactoryAddress
};
};
const getEcdsaRootIdentifierForKernelV3 = (validatorAddress, eip7702 = false) => {
return (0, viem_1.concatHex)([
eip7702 ? constants_js_1.VALIDATOR_TYPE.EIP7702 : constants_js_1.VALIDATOR_TYPE.VALIDATOR,
eip7702 ? "0x" : validatorAddress
]);
};
exports.getEcdsaRootIdentifierForKernelV3 = getEcdsaRootIdentifierForKernelV3;
const getInitializationData = ({ entryPoint: { version: entryPointVersion }, kernelVersion, validatorData, validatorAddress }) => {
if (entryPointVersion === "0.6") {
return (0, viem_1.encodeFunctionData)({
abi: KernelAccountAbi_js_1.KernelInitAbi,
functionName: "initialize",
args: [validatorAddress, validatorData]
});
}
if (kernelVersion === "0.3.0-beta") {
return (0, viem_1.encodeFunctionData)({
abi: KernelV3AccountAbi_js_1.KernelV3InitAbi,
functionName: "initialize",
args: [
(0, exports.getEcdsaRootIdentifierForKernelV3)(validatorAddress),
viem_1.zeroAddress,
validatorData,
"0x"
]
});
}
return (0, viem_1.encodeFunctionData)({
abi: KernelV3AccountAbi_js_1.KernelV3_1AccountAbi,
functionName: "initialize",
args: [
(0, exports.getEcdsaRootIdentifierForKernelV3)(validatorAddress),
viem_1.zeroAddress,
validatorData,
"0x",
[]
]
});
};
const getValidatorData = async (owner) => {
if (owner.type === "local") {
return owner.address;
}
if ((0, isWebAuthnAccount_js_1.isWebAuthnAccount)(owner)) {
const { PublicKey, Hex, Base64 } = await (0, ox_js_1.getOxExports)();
const parsedPublicKey = PublicKey.fromHex(owner.publicKey);
const authenticatorIdHash = (0, viem_1.keccak256)(Hex.fromBytes(Base64.toBytes(owner.id)));
return (0, viem_1.encodeAbiParameters)([
{
components: [
{ name: "x", type: "uint256" },
{ name: "y", type: "uint256" }
],
name: "webAuthnData",
type: "tuple"
},
{
name: "authenticatorIdHash",
type: "bytes32"
}
], [
{
x: parsedPublicKey.x,
y: parsedPublicKey.y
},
authenticatorIdHash
]);
}
throw new Error("Invalid owner type");
};
const getAccountInitCode = async ({ entryPointVersion, kernelVersion, validatorData, index, factoryAddress, accountLogicAddress, validatorAddress, useMetaFactory }) => {
const initializationData = getInitializationData({
entryPoint: { version: entryPointVersion },
kernelVersion,
validatorAddress,
validatorData
});
if (entryPointVersion === "0.6") {
return (0, viem_1.encodeFunctionData)({
abi: createAccountAbi,
functionName: "createAccount",
args: [accountLogicAddress, initializationData, index]
});
}
if (!useMetaFactory) {
return (0, viem_1.encodeFunctionData)({
abi: KernelV3FactoryAbi_js_1.KernelV3FactoryAbi,
functionName: "createAccount",
args: [initializationData, (0, viem_1.toHex)(index, { size: 32 })]
});
}
return (0, viem_1.encodeFunctionData)({
abi: KernelV3MetaFactoryAbi_js_1.KernelV3MetaFactoryDeployWithFactoryAbi,
functionName: "deployWithFactory",
args: [factoryAddress, initializationData, (0, viem_1.toHex)(index, { size: 32 })]
});
};
async function toKernelSmartAccount(parameters) {
const { client, address, index = 0n, version, validatorAddress: _validatorAddress, factoryAddress: _factoryAddress, metaFactoryAddress: _metaFactoryAddress, accountLogicAddress: _accountLogicAddress, useMetaFactory = true, eip7702 = false } = parameters;
const owners = (() => {
if (eip7702 && "owner" in parameters) {
return [parameters.owner];
}
if ("owners" in parameters) {
return parameters.owners;
}
throw new Error("Invalid parameters");
})();
const isWebAuthn = owners[0].type === "webAuthn";
const owner = await (() => {
if (isWebAuthn) {
return owners[0];
}
return (0, toOwner_js_1.toOwner)({
owner: owners[0]
});
})();
const entryPoint = (() => {
const address = parameters.entryPoint?.address ?? account_abstraction_1.entryPoint07Address;
const version = parameters.entryPoint?.version ?? "0.7";
let abi = account_abstraction_1.entryPoint07Abi;
if (version === "0.6") {
abi = account_abstraction_1.entryPoint06Abi;
}
return {
address,
abi,
version
};
})();
const kernelVersion = getDefaultKernelVersion(entryPoint.version, version, eip7702);
const { accountLogicAddress, validatorAddress, factoryAddress, metaFactoryAddress } = getDefaultAddresses({
validatorAddress: _validatorAddress,
accountLogicAddress: _accountLogicAddress,
factoryAddress: _factoryAddress,
metaFactoryAddress: _metaFactoryAddress,
kernelVersion,
isWebAuthn
});
if (!validatorAddress) {
throw new Error("Validator address is required");
}
const generateInitCode = async (_useMetaFactory) => getAccountInitCode({
entryPointVersion: entryPoint.version,
kernelVersion,
validatorData: await getValidatorData(owner),
index,
factoryAddress,
accountLogicAddress,
validatorAddress,
useMetaFactory: _useMetaFactory
});
let chainId;
const getMemoizedChainId = async () => {
if (chainId)
return chainId;
chainId = client.chain
? client.chain.id
: await (0, utils_1.getAction)(client, actions_1.getChainId, "getChainId")({});
return chainId;
};
const getFactoryArgsFunc = (_useMetaFactory) => async () => {
return {
factory: entryPoint.version === "0.6" || _useMetaFactory === false
? factoryAddress
: metaFactoryAddress,
factoryData: (await generateInitCode(_useMetaFactory))
};
};
const { accountAddress, getFactoryArgs } = await (async () => {
if (eip7702) {
return {
accountAddress: owner.address,
getFactoryArgs: async () => {
return {
factory: undefined,
factoryData: undefined
};
}
};
}
let getFactoryArgs = getFactoryArgsFunc(useMetaFactory === "optional" ? true : useMetaFactory);
if (address && useMetaFactory !== "optional") {
return { accountAddress: address, getFactoryArgs };
}
const { factory, factoryData } = await getFactoryArgs();
let accountAddress = await (0, getSenderAddress_js_1.getSenderAddress)(client, {
factory,
factoryData,
entryPointAddress: entryPoint.address
});
if (address === accountAddress) {
return { accountAddress, getFactoryArgs };
}
if (useMetaFactory === "optional" && accountAddress === viem_1.zeroAddress) {
getFactoryArgs = getFactoryArgsFunc(false);
const { factory, factoryData } = await getFactoryArgs();
accountAddress = await (0, getSenderAddress_js_1.getSenderAddress)(client, {
factory,
factoryData,
entryPointAddress: entryPoint.address
});
}
return { accountAddress, getFactoryArgs };
})();
const getKernelAccountPatchedStatus = async () => {
const rootValidator = await (0, utils_1.getAction)(client, actions_1.readContract, "readContract")({
address: accountAddress,
abi: [
{
type: "function",
name: "rootValidator",
inputs: [],
outputs: [
{
name: "",
type: "bytes21",
internalType: "ValidationId"
}
],
stateMutability: "view"
}
],
functionName: "rootValidator",
args: []
});
const patchedRootValidator = "0x017ab16ff354acb328452f1d445b3ddee9a91e9e69";
return rootValidator.toLowerCase() === patchedRootValidator;
};
let isKernelAccountPatched = validatorAddress !== "0xbA45a2BFb8De3D24cA9D7F1B551E14dFF5d690Fd";
return (0, account_abstraction_1.toSmartAccount)({
client,
entryPoint,
getFactoryArgs,
extend: eip7702
? {
implementation: accountLogicAddress
}
: undefined,
authorization: eip7702
? {
address: accountLogicAddress,
account: owner
}
: undefined,
async getAddress() {
return accountAddress;
},
async encodeCalls(calls) {
if (!isKernelAccountPatched) {
const isDeployed = "isDeployed" in this && (await this.isDeployed());
isKernelAccountPatched =
isDeployed && (await getKernelAccountPatchedStatus());
}
if (!isKernelAccountPatched) {
const [installFallbackCall] = (0, encodeInstallModule_js_1.encodeInstallModule)({
account: this,
modules: [
{
type: "fallback",
address: exports.MIGRATION_HELPER_ADDRESS,
context: "0x36f541c10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000"
}
]
});
const [uninstallFallbackCall] = (0, encodeUninstallModule_js_1.encodeUninstallModule)({
account: this,
modules: [
{
type: "fallback",
address: exports.MIGRATION_HELPER_ADDRESS,
context: "0x36f541c1"
}
]
});
const executeCallData = (0, encodeCallData_js_1.encodeCallData)({
calls,
kernelVersion
});
const { args } = (0, viem_1.decodeFunctionData)({
abi: [
{
type: "function",
name: "execute",
inputs: [
{
name: "execMode",
type: "bytes32",
internalType: "ExecMode"
},
{
name: "executionCalldata",
type: "bytes",
internalType: "bytes"
}
],
outputs: [],
stateMutability: "payable"
}
],
data: executeCallData
});
const migrationCallData = (0, viem_1.encodeFunctionData)({
abi: migrationHelperAbi,
functionName: "migrateWithCall",
args: [[], [], ...args]
});
if (kernelVersion !== "0.3.0-beta") {
return (0, encode7579Calls_js_1.encode7579Calls)({
mode: {
type: "delegatecall",
revertOnError: false,
selector: "0x",
context: "0x"
},
callData: [
{
to: exports.MIGRATION_HELPER_ADDRESS,
data: migrationCallData,
value: 0n
}
]
});
}
return (0, encodeCallData_js_1.encodeCallData)({
calls: [
installFallbackCall,
{
to: accountAddress,
data: migrationCallData,
value: 0n
},
uninstallFallbackCall
],
kernelVersion
});
}
return (0, encodeCallData_js_1.encodeCallData)({ calls, kernelVersion });
},
async decodeCalls(callData) {
return (0, decodeCallData_js_1.decodeCallData)({ callData, kernelVersion });
},
async getNonce(_args) {
return (0, getAccountNonce_js_1.getAccountNonce)(client, {
address: await this.getAddress(),
entryPointAddress: entryPoint.address,
key: (0, getNonceKey_js_1.getNonceKeyWithEncoding)(kernelVersion, validatorAddress, parameters.nonceKey ?? 0n)
});
},
async getStubSignature() {
if ((0, isKernelV2_js_1.isKernelV2)(kernelVersion)) {
return (0, viem_1.concatHex)([constants_js_1.ROOT_MODE_KERNEL_V2, constants_js_1.DUMMY_ECDSA_SIGNATURE]);
}
if ((0, isWebAuthnAccount_js_1.isWebAuthnAccount)(owner)) {
return (0, viem_1.encodeAbiParameters)([
{ name: "authenticatorData", type: "bytes" },
{ name: "clientDataJSON", type: "string" },
{ name: "responseTypeLocation", type: "uint256" },
{ name: "r", type: "uint256" },
{ name: "s", type: "uint256" },
{ name: "usePrecompiled", type: "bool" }
], [
"0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97631d00000000",
'{"type":"webauthn.get","challenge":"tbxXNFS9X_4Byr1cMwqKrIGB-_30a0QhZ6y7ucM0BOE","origin":"http://localhost:3000","crossOrigin":false, "other_keys_can_be_added_here":"do not compare clientDataJSON against a template. See https://goo.gl/yabPex"}',
1n,
44941127272049826721201904734628716258498742255959991581049806490182030242267n,
9910254599581058084911561569808925251374718953855182016200087235935345969636n,
false
]);
}
return constants_js_1.DUMMY_ECDSA_SIGNATURE;
},
async sign({ hash }) {
return this.signMessage({ message: hash });
},
async signMessage({ message }) {
if ("isDeployed" in this &&
!(await this.isDeployed()) &&
eip7702) {
throw new Error("Kernel with EIP-7702 isn't 1271 compliant before delegation.");
}
const signature = await (0, signMessage_js_1.signMessage)({
owner,
message,
accountAddress: await this.getAddress(),
kernelVersion: kernelVersion,
chainId: await getMemoizedChainId(),
eip7702: eip7702
});
if ((0, isKernelV2_js_1.isKernelV2)(kernelVersion)) {
return signature;
}
return (0, viem_1.concatHex)([
(0, exports.getEcdsaRootIdentifierForKernelV3)(validatorAddress, eip7702),
signature
]);
},
async signTypedData(typedData) {
if ("isDeployed" in this &&
!(await this.isDeployed()) &&
eip7702) {
throw new Error("Kernel with EIP-7702 isn't 1271 compliant before delegation.");
}
const signature = await (0, signTypedData_js_1.signTypedData)({
owner: owner,
chainId: await getMemoizedChainId(),
...typedData,
accountAddress: await this.getAddress(),
kernelVersion: kernelVersion,
eip7702
});
if ((0, isKernelV2_js_1.isKernelV2)(kernelVersion)) {
return signature;
}
return (0, viem_1.concatHex)([
(0, exports.getEcdsaRootIdentifierForKernelV3)(validatorAddress, eip7702),
signature
]);
},
async signUserOperation(parameters) {
const { chainId = await getMemoizedChainId(), ...userOperation } = parameters;
const hash = (0, account_abstraction_1.getUserOperationHash)({
userOperation: {
...userOperation,
sender: userOperation.sender ?? (await this.getAddress()),
signature: "0x"
},
entryPointAddress: entryPoint.address,
entryPointVersion: entryPoint.version,
chainId: chainId
});
const signature = (0, isWebAuthnAccount_js_1.isWebAuthnAccount)(owner)
? await (0, signMessage_js_1.signMessage)({
owner,
message: { raw: hash },
chainId,
accountAddress: await this.getAddress(),
kernelVersion,
eip7702: false
})
: await owner.signMessage({
message: { raw: hash }
});
if ((0, isKernelV2_js_1.isKernelV2)(kernelVersion)) {
return (0, viem_1.concatHex)(["0x00000000", signature]);
}
return signature;
}
});
}
//# sourceMappingURL=toKernelSmartAccount.js.map