Particle Network Wallet Abstraction¶
Introduction¶
Particle Network offers Wallet Abstraction services with an Account Abstraction stack, providing a suite of SDKs focused on reducing user onboarding friction.
By embedding customizable Externally Owned Account (EOA) and Account Abstraction (AA) components, Particle allows quick 2-click onboarding via social logins like Google, email, and phone, as well as traditional Web3 methods. This approach removes the need for users to manage a conventional wallet, delivering a streamlined, application-specific experience for Web3 interactions.
Particle Network supports Moonbeam, Moonriver, and the Moonbase Alpha TestNet with both standard EOA interactions and native ERC-4337 SimpleAccount
implementations, providing full-stack account abstraction.
Key components of Particle Network's Moonbeam integration include:
- Particle Connect: Particle's flagship Wallet-as-a-Service solution, offering embedded wallets powered by MPC-TSS for smooth, Web2-like onboarding and interactions, with Account Abstraction support integrated within a single SDK
- Particle Network Modular AA Stack: Beyond the default EOA-based interactions, Particle also offers a modular AA stack for ERC-4337 account abstraction on Moonbeam, allowing flexibility in the smart account, bundler, and paymaster configurations to suit AA-enabled applications
In this guide, you'll go through a step-by-step example of using Particle Connect on Moonbeam.
Create an Application¶
To use Particle Connect on Moonbeam, you'll need to create an account on the Particle Network dashboard and spin up an application
-
Navigate to the Particle Network dashboard, then sign up or log in
-
Once logged in, click Add New Project to create a new project
-
Enter the project name and click Save
-
From the project's dashboard, scroll down to the Your Apps section and create a new app by selecting iOS, Android, or Web and providing the requested information
-
Finally, copy the Project ID, Client Key, and App ID
Install Dependencies¶
To integrate Particle Connect into your Moonbeam application, you'll need only a few dependencies. Particle Connect offers built-in Account Abstraction (AA) support; however, in this example, we'll install the Particle AA SDK to utilize EIP-1193 providers, such as ethers.
yarn add @particle-network/connectkit viem@^2 @particle-network/aa ethers
Note that this tutorial is based on a Next.js app with TypeScript and Tailwind CSS.
Configure Particle Connect¶
We’ll configure and initialize Particle Connect (Particle's flagship authentication SDK). Begin by creating a new file called ConnectKit.tsx
in your project’s root directory, where we’ll set up the ParticleConnectKit
component as the primary interface for configuration.
Before proceeding, head back to the Particle dashboard and retrieve the following API keys:
projectId
– your project’s unique IDclientKey
– your client-specific keyappId
– your application ID
These keys are essential as they connect your Particle Connect instance with the Particle dashboard, enabling features like no-code customization, user activity tracking, and API request authentication.
Place the API keys in a .env
file in the following format:
NEXT_PUBLIC_PROJECT_ID='INSERT_PROJECT_ID'
NEXT_PUBLIC_CLIENT_KEY='INSERT_CLIENT_KEY'
NEXT_PUBLIC_APP_ID='INSERT_APP_ID'
This setup ensures that your API keys are securely accessible to the Next.js application while protecting them from unauthorized access.
Here’s the code to add to your ConnectKit.tsx
file:
"use client";
import React from "react";
import { ConnectKitProvider, createConfig } from "@particle-network/connectkit";
import { authWalletConnectors } from "@particle-network/connectkit/auth";
import { moonbeam } from "@particle-network/connectkit/chains";
import { wallet, EntryPosition } from "@particle-network/connectkit/wallet";
import { aa } from "@particle-network/connectkit/aa";
const config = createConfig({
projectId: process.env.NEXT_PUBLIC_PROJECT_ID!,
clientKey: process.env.NEXT_PUBLIC_CLIENT_KEY!,
appId: process.env.NEXT_PUBLIC_APP_ID!,
walletConnectors: [authWalletConnectors({})],
plugins: [
wallet({
entryPosition: EntryPosition.BR, // Positions the modal button at the bottom right on login
visible: true, // Determines if the wallet modal is displayed
}),
aa({
name: "SIMPLE",
version: "2.0.0",
}),
],
chains: [moonbeam],
});
export const ParticleConnectkit = ({ children }: React.PropsWithChildren) => {
return <ConnectKitProvider config={config}>{children}</ConnectKitProvider>;
};
This setup initializes ParticleConnectKit
, a wrapper for the configured ConnectKitProvider
instance, using your project keys. It also defines essential SDK settings, such as supported chains (e.g., Moonbeam), wallet positioning and visibility options, and a SIMPLE
smart account instance.
For further customization options, refer to the Particle Connect documentation.
At this point, you've signed up and created an application, installed all required dependencies, and configured ParticleConnectKit
and SmartAccount,
if applicable.
Integrate the ParticleConnectKit
Component in Your App¶
After completing the configuration, wrap your application with the ParticleConnectKit
component to enable global access to the Particle Connect SDK. Update your layout.tsx
file in src
as shown below:
import { ParticleConnectkit } from "@/ConnectKit";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Particle Connectkit App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<ParticleConnectkit>{children}</ParticleConnectkit>
</body>
</html>
);
}
Wrapping your application in ParticleConnectKit
provides global access to the SDK, making features like social logins and wallet generation available throughout your app. This setup in layout.tsx
ensures all components can access Particle Connect’s capabilities.
Connecting Wallet¶
With the configured layout.tsx
file, the next step is to add a central Connect Wallet button for user connectivity. You can achieve this by importing ConnectButton
from @particle-network/connectkit
. Once the user logs in, the ConnectButton
transforms into an embedded widget.
"use client";
import { ConnectButton, useAccount } from "@particle-network/connectkit";
const HomePage = () => {
const { address, isConnected, chainId } = useAccount();
return (
<div className="flex justify-center items-center h-screen">
<div className="text-center">
<ConnectButton />
{isConnected && (
<>
<h2>Address: {address}</h2>
<h2>Chain ID: {chainId}</h2>
</>
)}
</div>
</div>
);
};
export default HomePage;
Sending transactions with an EIP-1193 provider¶
Using Particle Connect alongside the Particle AA SDK enables you to work with an EIP-1193 provider like ethers. This approach is beneficial because you're likely already familiar with these providers or if you integrate Particle Connect into an existing application.
To set this up, wrap the smart account provided by Particle Connect with an instance of ethers to create a customProvider.
You can use ethers as usual from there, with the smart account as the underlying transaction signer.
import {useSmartAccount } from "@particle-network/connectkit";
import { AAWrapProvider, SendTransactionMode } from "@particle-network/aa";
const smartAccount = useSmartAccount();
// Init custom provider with gasless transaction mode
const customProvider = smartAccount
? new ethers.BrowserProvider(
new AAWrapProvider(
smartAccount,
SendTransactionMode.Gasless
) as Eip1193Provider,
"any"
)
: null;
/**
* Sends a transaction using the ethers.js library.
* This transaction is gasless since the customProvider is initialized as gasless
*/
const executeTxEthers = async () => {
if (!customProvider) return;
const signer = await customProvider.getSigner();
const tx = {
to: recipientAddress,
value: parseEther("0.01").toString(),
};
const txResponse = await signer.sendTransaction(tx);
const txReceipt = await txResponse.wait();
console.log(txReceipt?.hash)
};
Example of Utilization¶
With those above established, Particle Connect can be used similarly, as shown in the example application below.
Specifically, this application creates a smart account on Moonbeam MainNet through social login, then uses it to send a gasless transaction of 0.001 GLMR with the ethers provider.
"use client";
import React, { useEffect, useState } from "react";
// Particle imports
import {
ConnectButton,
useAccount,
usePublicClient,
useSmartAccount,
} from "@particle-network/connectkit";
// Eip1193 and AA Provider
import { AAWrapProvider, SendTransactionMode } from "@particle-network/aa"; // Only needed with Eip1193 provider
import { ethers, type Eip1193Provider } from "ethers";
import { formatEther, parseEther } from "viem";
export default function Home() {
const { isConnected, chain } = useAccount();
const publicClient = usePublicClient();
const smartAccount = useSmartAccount();
const [userAddress, setUserAddress] = useState<string>("");
const [balance, setBalance] = useState<string | null>(null);
const [recipientAddress, setRecipientAddress] = useState<string>("");
const [transactionHash, setTransactionHash] = useState<string | null>(null);
// Init custom provider with gasless transaction mode
const customProvider = smartAccount
? new ethers.BrowserProvider(
new AAWrapProvider(
smartAccount,
SendTransactionMode.Gasless
) as Eip1193Provider,
"any"
)
: null;
/**
* Fetches the balance of a given address.
* @param {string} address - The address to fetch the balance for.
*/
const fetchBalance = async (address: string) => {
try {
const balanceResponse = await publicClient?.getBalance({
address: address as `0x${string}`,
});
if (balanceResponse) {
const balanceInEther = formatEther(balanceResponse).toString();
setBalance(balanceInEther);
} else {
setBalance("0.0");
}
} catch (error) {
console.error("Error fetching balance:", error);
setBalance("0.0");
}
};
/**
* Loads the user's account data, including address and balance.
*/
useEffect(() => {
const loadAccountData = async () => {
if (isConnected && smartAccount) {
try {
const address = await smartAccount.getAddress();
setUserAddress(address);
await fetchBalance(address);
} catch (error) {
console.error("Error loading account data:", error);
}
}
};
loadAccountData();
}, [isConnected, smartAccount]);
/**
* Sends a transaction using the ethers.js library.
* This transaction is gasless since the customProvider is initialized as gasless
*/
const executeTxEthers = async () => {
if (!customProvider) return;
const signer = await customProvider.getSigner();
try {
const tx = {
to: recipientAddress,
value: parseEther("0.01").toString(),
};
const txResponse = await signer.sendTransaction(tx);
const txReceipt = await txResponse.wait();
setTransactionHash(txReceipt?.hash || null);
} catch (error) {
console.error("Failed to send transaction using ethers.js:", error);
}
};
return (
<div className="container min-h-screen flex flex-col justify-center items-center mx-auto gap-4 px-4 md:px-8">
<div className="w-full flex justify-center mt-4">
<ConnectButton label="Click to login" />
</div>
{isConnected && (
<>
<div className="border border-purple-500 p-6 rounded-lg w-full">
<h2 className="text-lg font-semibold mb-2 text-white">
Address: <code>{userAddress || "Loading..."}</code>
</h2>
<h2 className="text-lg font-semibold mb-2 text-white">
Balance: {balance || "Loading..."} {chain?.nativeCurrency.symbol}
</h2>
<input
type="text"
placeholder="Recipient Address"
value={recipientAddress}
onChange={(e) => setRecipientAddress(e.target.value)}
className="mt-4 p-3 w-full rounded border border-gray-700 bg-gray-900 text-white focus:outline-none"
/>
<button
className="bg-purple-600 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded mt-4"
onClick={executeTxEthers}
disabled={!recipientAddress}
>
Send 0.001 {chain?.nativeCurrency.name}
</button>
{transactionHash && (
<p className="text-green-500 mt-4">
Transaction Hash: {transactionHash}
</p>
)}
</div>
</>
)}
</div>
);
}
That concludes the brief introduction to Particle's Smart Wallet-as-a-Service stack and how to get started with Particle on Moonbeam. For more information, you can check out Particle Network's documentation.
Find the repository with the complete code implementation on the Particle Network GitHub.
| Created: November 7, 2023