Alchemy's Account Kit with Particle Auth

Deep dive into leveraging Particle Auth with Alchemy's Account Kit through aa-signers.

Particle Auth with Alchemy's Account Kit

Alchemy's "Account Kit" is a comprehensive, full-stack suite of tools for leveraging ERC-4337 account abstraction. From a core AA SDK, custom Smart Account implementation, Paymaster, and Bundler, to their various Signer libraries (as we'll cover here), Account Kit positions itself as a complete solution for account abstraction. The completeness of this solution is largely aided by, as mentioned, its native compatibility with various different Signers, including (but not limited to) Particle Auth. Thus, for those looking to implement account abstraction through non-native infrastructure (SDKs, Bundlers, Paymasters, and Smart Accounts outside of Particle Network's native AA stack), Account Kit presents itself as a solid choice.

This document will walk through the process of leveraging Particle Auth with Alchemy's Account Kit for social logins and gasless transactions through the aa-signers library.

Part 1: Particle Auth as a Signer

Getting Started

As mentioned, Alchemy has built various Signer-specific libraries to make the implementation of signers, such as Particle Auth, seamless and native to Account Kit. Specifically, we'll be using @alchemy/aa-signers/particle, an extension of @particle-network/auth that plugs directly into objects from @alchemy/aa-alchemy and @alchemy/aa-accounts. Thus, we'll be building an application that initiates and assigns an instance of LightAccount (Alchemy's in-house Smart Account implementation) to an EOA generated by Particle Auth. We'll then use this smart account to send a gasless transaction through Alchemy's natively compatible "Gas Manager API."

To begin, you'll need to install the libraries listed above, among a few others. Thus, the full list of libraries needed here are:

  • @alchemy/aa-alchemy, to initiate an AlchemyProvider object used to send transactions, configure the Paymaster, and make raw JSON-RPC calls.
  • @alchemy/aa-accounts, for the initialization and assignment of a LightAccount instance (through LightSmartContractAccount).
  • viem/chains, to retrieve a specific chain configuration object (polygonMumbai in this case).
  • ethers, for unit conversions.
  • @alchemy/aa-signers/particle, to leverage Particle Auth as a Signer.

📘

These libraries can be installed through either of the following commands.

yarn add @alchemy/aa-alchemy @alchemy/aa-accounts viem/chains ethers @alchemy/aa-signers/particle

// OR

npm install @alchemy/aa-alchemy @alchemy/aa-accounts viem/chains ethers @alchemy/aa-signers/particle

You'll need numerous objects from these libraries to be used within the initialization of Particle Auth and Alchemy (AlchemyProvider). Thus, the top of your App.tsx (or equivalent) file should look like the below snippet:

import React, { useState } from 'react';
import { AlchemyProvider } from '@alchemy/aa-alchemy';
import { LightSmartContractAccount } from '@alchemy/aa-accounts';
import { polygonMumbai } from 'viem/chains';
import { ethers } from 'ethers';
import { ParticleSigner } from '@alchemy/aa-signers/particle';

With these libraries installed and imported, we'll need to initialize Particle Auth through an instance of ParticleSigner. This object will allow an EOA generated via a social login with Particle Auth to be assigned to an instance of LightAccount and subsequently be called for interaction. Thus, whenever we execute a gasless transaction (see below), it'll automatically be routed through Particle Auth, allowing you to interact with the full extent of Account Kit using Particle Auth as a conduit.

To create a new instance of ParticleSigner, we'll need a few key parameters. These parameters mimic the constructor of ParticleNetwork from @particle-network/auth 1:1, and include:

  • projectId, retrieved after signing up and creating a new project on the Particle dashboard.
  • clientKey, also retrieved after signing up and creating a new project on the Particle dashboard.
  • appId, from an application created within a project on the Particle dashboard.
  • chainName + chainId, the specific details of the chain you intend to use. Within this example, thats "polygon" and 80001, for Polygon Mumbai.
const particleSigner = new ParticleSigner({
  projectId: process.env.REACT_APP_PROJECT_ID as string,
  clientKey: process.env.REACT_APP_CLIENT_KEY as string,
  appId: process.env.REACT_APP_APP_ID as string,
  chainName: "polygon",
	chainId: 80001,
});

Additionally, we'll need to initiate social login and thus unlock the full extent of ParticleSigner. If the SDK hasn't loaded an account (via social login), all methods will return an error. To avoid this, you can call the custom authenticate method on your instance of ParticleSigner, passing in a specific (potentially customized) call to the standard login method on a constructed PartneNetwork object (in this case represented through particleSigner.inner.auth.login). Your call to authenticate should look adjacent to the following snippet:

await particleSigner.authenticate({
  loginOptions: { preferredAuthType },
  login: async (loginOptions) => {
		await particleSigner.inner.auth.login(loginOptions);
	},
});

With ParticleSigner initialized and authenticated (by having a user log in with their social account), we're ready to set up an instance of AlchemyProvider, which will act as our master object facilitating everything from Paymaster configuration to sending transactions. Configuring AlchemyProvider is quite straightforward and involves the following set of parameters:

  • apiKey, your Alchemy API key.
    • To retrieve an API key from Alchemy, you'll need to first go to the Alchemy dashboard, upon signing up, navigate to Apps, and Create new app. After going through the process of creating an app, copy the corresponding API key.
  • chain, the specific chain object previously imported from viem/chains.In this example that's polygonMumbai.
  • entryPointAddress, a string representing the EntryPoint address you'd like to use. This should, and will typically be 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789, although feel free to call supportedEntryPoints on Alchemy's provider/bundler for a list of options (currently restricted to the former address though).

From here, to assign the aforementioned LightAccount implementation (through LightSmartContractAccount) you'll need to call connect on the constructed object and once again pass the entryPointAddress and chain alongside the owner as your instance of ParticleSigner and the factoryAddress as 0x000000893A26168158fbeaDD9335Be5bC96592E2.

const alchemyProvider = new AlchemyProvider({
	apiKey: process.env.ALCHEMY_API_KEY,
  chain: polygonMumbai,
  entryPointAddress,
 }).connect(rpcClient => new LightSmartContractAccount({
  entryPointAddress,
  chain: rpcClient.chain,
  owner: particleSigner,
  factoryAddress: "0x000000893A26168158fbeaDD9335Be5bC96592E2",
  rpcClient,
}));

At this point, you've initialized ParticleSigner (adjacent to ParticleNetwork from @particle-network/auth), facilitated social logins, and spun up an instance of AlchemyProvider, assigning a LightAccount implementation to the EOA generated by Particle Network. Within this example application, we can separate each of these tasks into individual functions, initializeParticleSigner and initializeProvider. This results in a structure such as the following:

const initializeParticleSigner = async (preferredAuthType) => {
    const particleSigner = new ParticleSigner({
      projectId: process.env.REACT_APP_PROJECT_ID as string,
      clientKey: process.env.REACT_APP_CLIENT_KEY as string,
      appId: process.env.REACT_APP_APP_ID as string,
      chainName: "polygon",
      chainId: 80001,
    });

    await particleSigner.authenticate({
      loginOptions: { preferredAuthType },
      login: async (loginOptions) => {
        await particleSigner.inner.auth.login(loginOptions);
      },
    });

    return particleSigner;
  };

  const initializeProvider = async (particleSigner) => {
    return new AlchemyProvider({
      apiKey: process.env.ALCHEMY_API_KEY,
      chain: polygonMumbai,
      entryPointAddress,
    }).connect(rpcClient => new LightSmartContractAccount({
      entryPointAddress,
      chain: rpcClient.chain,
      owner: particleSigner,
      factoryAddress: "0x000000893A26168158fbeaDD9335Be5bC96592E2",
      rpcClient,
    }));
  };

  const handleLogin = async (preferredAuthType) => {
    const particleSigner = await initializeParticleSigner(preferredAuthType);

    const provider = await initializeProvider(particleSigner);
    setProviderState(provider);
  };

Part 2: Sending Gasless Transactions

Now that we've gone through the process of initializing ParticleSigner and AlchemyProvider, you're ready to begin using Account Kit and its associated functions (with Particle Auth acting as the core Signer).

To provide an example of one possibility here, we can send a sample gasless transaction. This will be a standard burn of 0.001 ETH, with 0x000000000000000000000000000000000000dEaD as the recipient. Additionally, the gas fee on this transaction should be sponsored, leaving us with a completely gasless burn. Before getting started programmatically, you'll need to head to the Alchemy dashboard to create a Paymaster policy through the following steps:

  1. Once on the dashboard, navigate to Account Abstraction, then Gas Manager.
  2. Click Create new policy, selecting a specific policy name and associated application.
  3. Set spending rules (per address, policy-wide, or neither).
  4. If applicable, set access controls for specific sender addresses.
  5. Finally, set the sponsorship expiry and policy duration.
  6. Then publish the policy and activate it. Ensure you save the Policy ID, as we'll be using it in a moment to configure usage of the Paymaster via AlchemyProvider.

From here, we're ready to set up our core method facilitating this transaction, executeUserOperation. This function will configure Alchemy's Gas Manager, then send a standard burn transaction in around ten lines of code. To begin with the Gas Manager configuration, we'll need to open the function by calling the withAlchemyGasManager method on an instance of AlchemyProvider, passing in:

  • policyId, the ID previously generated through the creation of your sponsorship policy.
  • entryPoint, the EntryPoint address to be used. This will be the same address that we used previously, 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789.

With Alchemy's Gas Manager configured, you can then execute a basic transaction through the sendTransaction method on your AlchemyProvider instance. The object used in this case should follow typical transaction conventions containing to and value (for an ETH transfer in this example). to will be the dead address, 0x000000000000000000000000000000000000dEaD, and value will be 0.001 ETH converted to wei.

const executeUserOperation = async () => {
  providerState.withAlchemyGasManager({
    policyId: process.env.ALCHEMY_POLICY_ID,
		entryPoint: entryPointAddress,
	});

	const txHash = await providerState.sendTransaction({
    to: "0x000000000000000000000000000000000000dEaD",
		value: ethers.utils.parseEther("0.001"),
	});
};

This will prompt a confirmation signature from Particle Auth (through a popup window), after which the transaction will immediately be sent as a UserOperation through the previously assigned LightAccount instance while covering all gas fees.

Conclusion

Particle Network's intrinsic compatibility with Account Kit is a great example of leveraging external AA infrastructure directly with Particle's Smart Wallet-as-a-Service. A major goal of Particle’s Smart Wallet-as-a-Service is allowing you to build your AA-enabled application using whichever library or toolkit is the most convenient, whether that by Particle's AA SDK, Account Kit, or something else.

For a full interactive code walkthrough on the example application covered here, take a look at the associated sample below, or alternatively check out the GitHub repository and deployed demo.

📹

A video diving into this topic and the example covered within this document is also available.