npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aspect-wallet/sdk

v0.1.2

Published

TypeScript SDK for ERC-4337 Account Abstraction smart wallets. Gasless transactions, social logins, passkeys, session keys, multi-factor auth, and account recovery.

Readme

@aspect-wallet/sdk

TypeScript SDK for building on the Aspect Wallet ERC-4337 Account Abstraction system. Create smart wallets, send gasless transactions, authenticate with social logins, and manage session keys -- all from your frontend.

Features

| Feature | Description | |---------|-------------| | Smart Wallets | Deterministic CREATE2 addresses, lazy deployment on first transaction | | Gasless Transactions | VerifyingPaymaster sponsors gas -- users pay nothing | | Social Login | Google, Apple, Facebook, Email OTP -- bridged to on-chain signers via Turnkey | | Passkeys | WebAuthn/P256 biometric signing with RIP-7212 precompile support | | Session Keys | Scoped ephemeral keys with allowlist, spend limits, time bounds. Signs entirely in-browser (no auth round-trip) | | Multi-Factor Auth | On-chain enforced: device+guardian, K-of-N multi-sig, timelock | | Account Recovery | Social recovery via guardians with timelock, two-step key rotation | | Emergency Freeze | Instantly revoke all session keys and freeze account on compromise | | Audit Trail | Full operation history, real-time events, webhook notifications | | Multi-Chain | Arbitrum, Base, Polygon, Ethereum -- same address across chains |

Installation

npm install @aspect-wallet/sdk viem

Quick Start

import { FcxWalletClient } from '@aspect-wallet/sdk';

const client = new FcxWalletClient({
  apiKey: 'fcx_live_abc123...',
  chain: 'arbitrum-sepolia',
  bundlerUrl: 'https://bundler.yourapp.com/rpc', // optional
  paymasterUrl: 'https://paymaster.yourapp.com', // optional
});

// Resolve the deterministic wallet address for an owner (no on-chain call needed)
const wallet = await client.wallet.getAddress({ owner: signerAddress });
// wallet.address -- the smart account address
// wallet.deployed -- false until the first UserOp

// Check the on-chain state (balance, modules, frozen flag, …)
const state = await client.wallet.getState(wallet.address);

FcxWalletClient exposes the following sub-modules:

| Property | Class | Purpose | |----------|-------|---------| | client.wallet | WalletFactory | Address + state queries | | client.sponsor | PaymasterClient | Gas sponsorship requests | | client.bundler | BundlerClient | JSON-RPC submission / receipts | | client.audit | AuditClient | History, events, webhooks | | client.chains | ChainRegistry | Chain configs (per project) |

Execution is performed by composing UserOpBuilder + a signer + client.sponsor + client.bundler. The end-to-end flow is shown below.


Wallet Lifecycle

Counterfactual Address

Users get a wallet address instantly -- no on-chain transaction needed. Assets can be sent to this address before the contract exists.

import { computeWalletAddress } from '@aspect-wallet/sdk';

// Pure computation -- no RPC, no API call
const address = computeWalletAddress(
  factoryAddress,         // from ChainRegistry / your project config
  implementationAddress,  // UUPS implementation behind the ERC-1167 clone
  ownerAddress,
  0n,                     // salt
);
// Or query through the platform (also returns deployment status)
const wallet = await client.wallet.getAddress({
  owner: signerAddress,
  salt: 0n,
});
// wallet.address, wallet.deployed, wallet.factory, wallet.salt, wallet.chain

Lazy Deployment

The wallet deploys automatically on the first UserOp, via initCode. UserOpBuilder.setInitCode({ factory, owner, salt }) encodes the factory call for you.


Sending a UserOperation

FcxWalletClient does not expose a monolithic execute() helper -- you assemble a UserOp from the three building blocks (UserOpBuilder, a signer, PaymasterClient, BundlerClient). This keeps every step visible and overridable.

import {
  UserOpBuilder,
  EoaSigner,
  getUserOpHash,
} from '@aspect-wallet/sdk';

const signer = new EoaSigner(privateKey); // 0x…hex
const chainId = BigInt(client.chainId);

// 1. Build
const userOp = await new UserOpBuilder(client.bundler, client.entryPoint, chainId)
  .setSender(walletAddress)
  .setCallData({ target, value, data })
  // First deploy only — binds the initCode
  // .setInitCode({ factory, owner, salt: 0n })
  .setNonce({ key: 0n })
  .build();

// 2. Sponsorship (optional — skip for self-paid gas)
const approval = await client.sponsor.approve(userOp);
userOp.paymasterAndData = approval.paymasterAndData;

// 3. Hash + sign
const hash = getUserOpHash(userOp, client.entryPoint, chainId);
userOp.signature = await signer.sign(hash);
// EoaSigner returns moduleId(4) + ecdsaSig(65) = 69 bytes

// 4. Submit
const userOpHash = await client.bundler.sendUserOperation(userOp);

// 5. Wait for on-chain receipt
const receipt = await client.bundler.waitForReceipt(userOpHash, {
  timeout: 60_000,
  pollingInterval: 2_000,
});

Hash Computation

getUserOpHash(userOp, entryPoint, chainId) matches EntryPoint.getUserOpHash() exactly -- variable-length fields (initCode, callData, paymasterAndData) are individually hashed, then keccak256(abi.encode(innerHash, entryPoint, chainId)) anti-replays across chains.

Batch Calls

const userOp = await new UserOpBuilder(client.bundler, client.entryPoint, chainId)
  .setSender(walletAddress)
  .setCallDataBatch([
    { target: tokenA, value: 0n, data: approveData },
    { target: dex,    value: 0n, data: swapData    },
  ])
  .build();
// All calls are atomic: all succeed, or the whole batch reverts.

Gas Sponsorship (Paymaster)

Users transact with zero ETH. The VerifyingPaymaster contract pays gas, authorised by your backend signing service. paymasterAndData is 97 bytes: address(20) + validUntil(6) + validAfter(6) + signature(65).

const approval = await client.sponsor.approve(userOp);
// approval.paymasterAndData, approval.validUntil, approval.validAfter, approval.estimatedCost

// Budget + policy queries
const budget = await client.sponsor.getBudget();
const policy = await client.sponsor.getPolicy();

PaymasterClient can also be constructed standalone if you need sponsorship without the full FcxWalletClient:

import { PaymasterClient, ApiClient } from '@aspect-wallet/sdk';

const api = new ApiClient('https://paymaster.yourapp.com', apiKey);
const paymaster = new PaymasterClient(api, {
  chainId: 421614,
  entryPointAddress: '0x5FF1…',
});

Authentication & Social Login

All auth clients take an ApiClient for the auth server. Wire them up once, share a SessionManager so a login via any method updates the same session.

import {
  ApiClient,
  AuthClient,
  OAuthClient,
  PasskeyAuthClient,
  SessionManager,
} from '@aspect-wallet/sdk';

const api        = new ApiClient('https://auth.yourapp.com', apiKey);
const session    = new SessionManager(api);
const email      = new AuthClient(api);            // AuthClient owns its own SessionManager
const oauth      = new OAuthClient(api, session);
const passkey    = new PasskeyAuthClient(api, session);

Email OTP

await email.sendEmailOtp('[email protected]');

// Rate limited: 3 attempts per OTP, 5 OTPs per hour per email
const info = await email.verifyEmailOtp({
  email: '[email protected]',
  code:  '847291',
});
// info.jwt, info.refreshToken, info.user { id, email, signerAddress, … }, info.expiresAt

OAuth (Google / Apple / Facebook)

const info = await oauth.loginWithGoogle();
const info = await oauth.loginWithApple();
const info = await oauth.loginWithFacebook();
// First login: creates a Turnkey sub-org + signing key
// Returning login: loads the existing key

Passkey Authentication

Passkeys use WebAuthn. The P256 public key is stored on-chain inside WebAuthnValidationModule — never in the backend database. Credentials live in the device secure enclave.

// Registration (call this after email/OAuth login)
const reg = await passkey.registerPasskey({ displayName: 'My MacBook' });
// reg.credentialId -- base64url; persist for future loginWithPasskey calls
// reg.pubKeyX / reg.pubKeyY -- hex uint256 for on-chain installModule

// Login from any device that has the credential
const info = await passkey.loginWithPasskey(walletAddress);

Installing the WebAuthn module on-chain (moduleId = 4) binds the public key to the account so on-chain verification works:

import { encodeAbiParameters } from 'viem';

const installData = encodeAbiParameters(
  [{ type: 'uint256' }, { type: 'uint256' }],
  [BigInt(reg.pubKeyX), BigInt(reg.pubKeyY)],
);

// Submit a UserOp that calls ModuleRouter.installModule(4, installData)
// using UserOpBuilder + a signer as shown above.

Signing

Signer Interface

All signers implement ISigner:

interface ISigner {
  type: 'eoa' | 'passkey' | 'turnkey' | 'session-key' | 'multi-sig';
  address: Address;
  moduleId: number;
  sign(userOpHash: Hex): Promise<Hex>;
}

Every signature is prefixed with a 4-byte moduleId so ModuleRouter on-chain routes it to the correct validator.

EOA Signer

import { EoaSigner } from '@aspect-wallet/sdk';

const signer = new EoaSigner(privateKey); // Hex
const sig    = await signer.sign(userOpHash);
// moduleId(4) + ecdsaSig(65) = 69 bytes, moduleId = 0 (SingleSigner)

Session Key Signer

import { SessionKeySigner } from '@aspect-wallet/sdk';

const signer = new SessionKeySigner(sessionPrivateKey);
const sig    = await signer.sign(userOpHash);
// Same 65-byte ECDSA wire format, moduleId = 1 (SessionKey)

Passkey Signer

PasskeySigner calls navigator.credentials.get() to trigger the biometric prompt, parses the DER-encoded P256 signature from the authenticator, normalises s to low-s, and ABI-encodes the result.

import { PasskeySigner } from '@aspect-wallet/sdk';

const signer = new PasskeySigner(walletAddress, credentialId);
const sig    = await signer.sign(userOpHash);
// moduleId(4) + abi.encode(authenticatorData, clientDataJSON, r, s)
// moduleId = 4 (WebAuthn); ~300–500 bytes total
// Verified on-chain via RIP-7212 precompile or daimo-eth/p256-verifier fallback.

For tests, pass the optional third signFn to skip the browser ceremony:

new PasskeySigner(walletAddress, credentialId, async (challenge) => ({
  authenticatorData: '0x…',
  clientDataJSON: '{"type":"webauthn.get",...}',
  r: 0x…n,
  s: 0x…n,
}));

Multi-Sig Collector

import { MultiSigCollector } from '@aspect-wallet/sdk';

const collector = new MultiSigCollector(threshold, signerAddresses);
collector.addSignature(sigFromOwner1);
collector.addSignature(sigFromOwner2);
if (collector.isComplete) {
  userOp.signature = await collector.sign(userOpHash); // concatenated packed sigs
}

Modular Signature Prefix (advanced)

import { encodeModuleSignature } from '@aspect-wallet/sdk';

const modularSig = encodeModuleSignature(0, ecdsaSignature);
// Module IDs: 0=SingleSigner, 1=SessionKey, 2=DeviceGuardian, 3=MultiSig, 4=WebAuthn

Session Keys

Session keys are ephemeral scoped keys. Once installed, they let a dApp execute transactions without user interaction per action — the key lives in the browser and signs locally. Permission hooks enforce limits on-chain.

Low-Level: SessionKeyManager (API-driven)

import { SessionKeyManager, SessionKeyTemplates } from '@aspect-wallet/sdk';

const keys = new SessionKeyManager(client.api);

// Custom permissions
const info = await keys.create({
  permissions: {
    allowlist: [
      { target: gameContract, selector: '0x12345678' },
      { target: gameContract, selector: '0xabcdef01' },
    ],
    spendLimit: { perTx: 0n, cumulative: 0n },
    timeRange: {
      validAfter: Math.floor(Date.now() / 1000),
      validUntil: Math.floor(Date.now() / 1000) + 4 * 3600,
    },
    requiredPaymaster: paymasterAddress,
  },
});

// Or use a pre-built template
const perms = SessionKeyTemplates.gaming({
  gameContract: '0x…',
  duration:     4 * 3600,
  paymaster:    '0x…',
});

// Execute via the managed API (server co-signs)
await keys.execute(info.moduleId, { target, value: 0n, data });

// Revoke
await keys.revoke(info.moduleId);

Available templates on SessionKeyTemplates: gaming, defi, subscription, nftMint.

High-Level: SessionKeyLifecycle (browser-native)

For apps that want the key to live entirely in the browser (no auth round-trip on execute), use SessionKeyLifecycle. It generates the keypair locally, persists it in pluggable storage, listens for server-sent expiry events over an authenticated SSE stream, and auto-renews before the key expires.

import {
  SessionKeyLifecycle,
  WebSessionKeyStorage,
} from '@aspect-wallet/sdk';

const lifecycle = new SessionKeyLifecycle({
  walletAddress: '0x…',
  platformUrl:   'https://platform.yourapp.com',
  paymasterUrl:  'https://paymaster.yourapp.com',
  bundlerUrl:    'https://bundler.yourapp.com/rpc',
  rpcUrl:        'https://arb-sepolia-rpc.example.com',
  chainId:       421614,
  jwt:           ownerSessionJwt,
  template:      'gaming',
  storage:       new WebSessionKeyStorage(),
  onExpiring: ({ remainingSeconds }) => toast(`Key expires in ${remainingSeconds}s`),
  onRenewed:  ({ newExpiresAt })      => toast(`Renewed until ${new Date(newExpiresAt * 1000)}`),
  onError:    (err)                   => console.error(err),
});

await lifecycle.install();  // generate + register with the platform
lifecycle.start();          // begin the SSE listener (auto-renew)

const result = await lifecycle.execute({
  target: noteStoreAddress,
  value:  0n,
  data:   addNoteCallData,
});
// result.userOpHash, result.txHash?

await lifecycle.revoke();   // on logout / unmount
lifecycle.stop();           // close the SSE stream

Standalone: executeViaSessionKey

Want the bare pipeline without the lifecycle manager? Call it directly:

import { executeViaSessionKey } from '@aspect-wallet/sdk';

const { userOpHash } = await executeViaSessionKey({
  sessionPrivateKey,
  moduleId:      1,
  walletAddress: '0x…',
  target,
  value: 0n,
  data,
  paymasterUrl:  'https://paymaster.yourapp.com',
  bundlerUrl:    'https://bundler.yourapp.com/rpc',
  rpcUrl:        'https://arb-sepolia-rpc.example.com',
  chainId:       421614,
});

Storage Adapters

| Platform | Implementation | Backend | Lifecycle | |----------|---------------|---------|-----------| | Web | WebSessionKeyStorage | window.sessionStorage | Cleared on tab close | | Node / test | MemorySessionKeyStorage | Map<string, string> | Process lifetime | | Native (BYO) | Implement SessionKeyStorage | iOS Keychain / Android KeyStore | Persists across launches |


Multi-Factor Authentication

On-chain enforced MFA -- the blockchain rejects transactions that don't meet the multi-factor requirements.

4-Tier Step-Up Selection

import { TierSelector } from '@aspect-wallet/sdk';

const tier = TierSelector.select({
  value:   parseEther('5'),
  target:  tokenContract,
  selector: '0xa9059cbb', // transfer(address,uint256)
});
// tier = 3  → requires device + guardian

| Tier | When | Signers | Latency | |------|------|---------|---------| | 1 | Low-value, scoped actions | Session key (auto-sign) | Instant | | 2 | Normal transactions | Owner key | 1 biometric prompt | | 3 | High-value transfers | Owner + Guardian | 2 approvals | | 4 | Account management | K-of-N multi-sig + timelock | Hours/days |

Guardian Management

import { GuardianManager } from '@aspect-wallet/sdk';

const guardians = new GuardianManager(client.api);

await guardians.addGuardian({ guardian: '0x…', label: 'Mom' });
await guardians.addGuardian({ guardian: '0x…', label: 'Backend Co-Signer' });
await guardians.setThreshold(2); // 2-of-3 required

const list       = await guardians.listGuardians(walletAddress);
const threshold  = await guardians.getThreshold(walletAddress);
await guardians.removeGuardian('0x…');

Multi-Sig Proposals

import { MultiSigManager } from '@aspect-wallet/sdk';

const multisig = new MultiSigManager(client.api);

// Owner 1 proposes
const proposal = await multisig.proposeMultiSig({ target, value: 0n, data });

// Owner 2 approves -- if threshold met, the UserOp is submitted automatically
await multisig.approveMultiSig(proposal.id);

const current  = await multisig.getProposal(proposal.id);
const pending  = await multisig.listPending(walletAddress);

Account Recovery

Social Recovery (Guardian-Based)

import { SocialRecovery } from '@aspect-wallet/sdk';

const recovery = new SocialRecovery(client.api);

await recovery.initiateRecovery({ account: userWallet, newOwner: newKeyAddress });
await recovery.approveRecovery({ account: userWallet });

// After timelock → execute
await recovery.executeRecovery({ account: userWallet });

// Owner cancellation (during timelock)
await recovery.cancelRecovery();

const status = await recovery.getStatus(walletAddress);
// { pending, approvals, threshold, executeAfter, canExecute, canCancel, newOwner? }

Two-Step Key Rotation

import { KeyRotation } from '@aspect-wallet/sdk';

const rotation = new KeyRotation(client.api);

await rotation.initiateKeyRotation({ newOwner: newSignerAddress }); // current owner signs
await rotation.acceptKeyRotation();                                 // new owner signs
// Old key loses access immediately; wallet address is unchanged.

const pending = await rotation.getPendingOwner(walletAddress);

Multi-Device

import { DeviceManager } from '@aspect-wallet/sdk';

const devices = new DeviceManager(client.api);

await devices.addDevice({ name: 'iPhone 15', type: 'passkey', moduleId: 2 });
await devices.removeDevice(moduleId); // revoke a lost device

const list = await devices.listDevices(walletAddress);

Security & Emergency

Emergency Freeze

import { FreezeManager } from '@aspect-wallet/sdk';

const freeze = new FreezeManager(client.api);

await freeze.freeze({ reason: 'suspicious_activity' }); // revokes all session keys
await freeze.unfreeze();                                // requires MFA Tier 3/4
const isFrozen = await freeze.isFrozen(walletAddress);

Bulk Session Key Revocation

import { RevokeManager } from '@aspect-wallet/sdk';

const revoke = new RevokeManager(client.api);

await revoke.revokeAllSessionKeys();
const { revokedCount } = await revoke.revokeSessionKeys({ expiredOnly: true });

Watchtower Alerts

import { WatchtowerClient } from '@aspect-wallet/sdk';

const watchtower = new WatchtowerClient(client.api);

await watchtower.subscribe({
  account: walletAddress,
  alerts: ['recovery_initiated', 'owner_changed', 'large_transfer'],
  channels: {
    email:   '[email protected]',
    webhook: 'https://myapp.com/webhooks/security',
  },
});

const alerts = await watchtower.getAlerts(walletAddress);

Audit & Events

const history = await client.audit.getHistory(walletAddress, {
  limit: 50,
  types: ['execute', 'deploy', 'module_install'],
});
// history.total, history.operations[] { userOpHash, transactionHash, target, value,
//   signer, signerType, sponsored, paymaster?, gasCost, success, blockNumber, timestamp }

// Real-time events
const unsubscribe = client.audit.subscribe(walletAddress, {
  events: ['userop.mined', 'security.alert'],
  onEvent: (e) => console.log(e.type, e.userOpHash),
  onError: console.error,
});

// Webhook management
await client.audit.webhooks.create({
  url:     'https://myapp.com/webhooks',
  secret:  'whsec_…',
  events:  ['userop.mined', 'session_key.installed'],
});

Chain Configuration

Contract addresses come from the platform (ChainRegistry) at runtime -- keyed to your project via the API key.

const chains = await client.chains.list();             // all supported chains
const config = await client.chains.getConfig('arbitrum-sepolia');
// config.chainId, config.entryPoint, config.contracts { factory, paymaster, …modules }

// Switch chains on the same client
client.switchChain('base');

// Or spin up a second client for cross-chain work
const baseClient = client.forChain('base');

Bundler Client

import { BundlerClient } from '@aspect-wallet/sdk';

const bundler = new BundlerClient('https://bundler.yourapp.com/rpc', entryPointAddress);

const hash    = await bundler.sendUserOperation(signedUserOp);
const est     = await bundler.estimateUserOperationGas(userOp);
// est.preVerificationGas, est.verificationGasLimit, est.callGasLimit (hex)

const receipt = await bundler.waitForReceipt(hash, { timeout: 60_000, pollingInterval: 2_000 });
const points  = await bundler.getSupportedEntryPoints();

Error Handling

All SDK errors are instances of FcxError and carry a stable code:

import { FcxError } from '@aspect-wallet/sdk';

try {
  await client.sponsor.approve(userOp);
} catch (e) {
  if (e instanceof FcxError) {
    switch (e.code) {
      case 'USEROP_SIGNATURE_INVALID':     // -32507
      case 'USEROP_SIMULATION_FAILED':     // -32500
      case 'SPONSOR_BUDGET_EXHAUSTED':
      case 'SPONSOR_POLICY_REJECTED':
      case 'SESSION_KEY_EXPIRED':
      case 'SESSION_KEY_NOT_FOUND':
      case 'SESSION_KEY_NOT_ALLOWED':
      case 'SESSION_STORAGE_UNAVAILABLE':
      case 'MFA_THRESHOLD_NOT_MET':
      case 'AUTH_SESSION_EXPIRED':
      case 'API_KEY_INVALID':
      case 'WALLET_FROZEN':
        // handle the specific failure mode
        break;
    }
    console.error(e.code, e.message, e.details, e.retryable);
  }
}

FcxError also exposes httpStatus, rpcCode, userOpHash?, and static helpers FcxError.fromRpcError() and FcxError.fromHttpError().


TypeScript Types

All domain types are exported from the package root and from the /types sub-export:

import type {
  UserOperation,
  ExecutionResult,
  SessionInfo,
  WalletInfo,
  WalletState,
  SessionKeyPermissions,
  SessionKeyInfo,
  PaymasterApproval,
  SponsorshipBudget,
  RecoveryStatus,
  AuditOperation,
  ChainId,
  ChainInfo,
  ChainConfig,
} from '@aspect-wallet/sdk';

// Or the narrow types-only import
import type { UserOperation } from '@aspect-wallet/sdk/types';

Architecture

Your Frontend App
       │
       ▼
@aspect-wallet/sdk (this package)
       │
       ├── BundlerClient ───── eth_sendUserOperation ──► EntryPoint (on-chain)
       │                                                       │
       ├── PaymasterClient ─── POST /api/paymaster/sign        ├── Smart Account (proxy)
       │                                                       ├── Factory (CREATE2 / ERC-1167)
       ├── AuthClient ──────── POST /auth/email/verify         ├── VerifyingPaymaster
       │   OAuthClient         POST /auth/oauth/{provider}     └── Validation Modules
       │   PasskeyAuthClient   POST /auth/passkey/*                ├── SingleSigner
       │                                                           ├── WebAuthn (P256)
       ├── SessionKeyLifecycle POST /session-keys/install          ├── MultiSig (K-of-N)
       │                        GET /session-keys/events (SSE)     ├── SessionKey + hooks
       │                                                           │   (Allowlist,
       └── ChainRegistry / AuditClient / WatchtowerClient          │    SpendLimit,
                                                                   │    TimeRange,
                                                                   │    PaymasterGuard)
                                                                   └── SocialRecovery

Supported Chains

  • Arbitrum One, Arbitrum Sepolia
  • Base, Base Sepolia
  • Polygon, Polygon Amoy
  • Ethereum, Sepolia

Same CREATE2 address across every chain where the factory is deployed at the same address.


Requirements

  • Node.js >= 18
  • viem 2.x
  • EVM chain with ERC-4337 EntryPoint v0.6 deployed at 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789

License

MIT