);
}
```
#### Key Components
* **Safe Transaction Building**: Creates a Safe transaction with the required parameters including nonce, target address, and operation type
* **Multi-Owner Signing**: Collects EIP-712 signatures from multiple Safe owners
* **Signature Sorting**: Sorts signatures by signer address (required by Safe protocol)
* **Relayer Execution**: Uses `sendSafeRelayerTxWithRealTimeUpdates` to execute the transaction through the relayer
* **Real-Time Updates**: WebSocket events provide instant feedback on transaction status
:::warning
⚠️ **Security:** Never expose private keys in client-side code. This example is for demonstration purposes. In production, handle signing on the server side or use secure wallet connections.
:::
:::tip
💡 **Configuration:** Make sure to set the `isSafeTx: true` flag in the relayer configuration when working with Safe transactions. This ensures the relayer properly handles Safe-specific transaction formats.
:::
***
### API Reference
* new Relayer (options)
* `options.relayerUrl: string` (required)
* `options.apiKey: string` (required)
* `options.chainId: number | ChainId`
* `options.signer` (ethers/web3 signer)
* `options.provider` (ethers/web3 provider)
* `options.webSocket?: { enabled: boolean, autoConnect?: boolean, reconnection?: boolean }`
* `buildRelayerTx(params: BuildRelayerTxParams)` → prepares payload for relay
* `buildRelayerTxEIP712(params)` → prepare EIP-712 signed payload flows
* `sendRelayerTx(payload)` → submit prepared payload to relayer service
* `sendRelayerTxWithRealTimeUpdates(payloadWithWsOptions)` → submit + subscribe to WS updates
* `getRelayerTxStatus(transactionId)` → poll current state
* `subscribeToTransaction(transactionId, events)` → register handlers for updates
* `unsubscribeFromTransaction(transactionId)`
* `connectWebSocket()` / `disconnectWebSocket()` / `isWebSocketConnected()`
***
### Polling vs Real-Time — When to use which?
| Mode | When to use | Pros | Cons |
| --------- | ----------------------------------------------- | --------------------------------------- | -------------------------------------------------- |
| Polling | Simple server workflows, short-lived tasks | Easy to implement, works behind proxies | Latency, repeated requests |
| WebSocket | Real-time UIs, mobile clients, instant feedback | Instant updates, better UX | Requires persistent connection, reconnect handling |
***
### Integrations & Best Practices
* **Server-side relay orchestration:** Keep API keys and critical signing on the server.
* **Intent validation:** Validate user intent before creating relayer payloads. Agents should sanitize and verify prior to send.
* **Retries & idempotency:** Implement idempotency keys / dedupe when re-sending in case of network issues.
* **Fallbacks:** If WebSocket connectivity fails, gracefully fallback to polling.
* **Monitoring:** Log transactionId, status, and relayer response codes for observability.
***
### Security Notes
* Never expose apiKey in client-side code.
* Use short-lived server tokens and rotate keys when possible.
* Validate contract ABI and method inputs to avoid unintended on-chain calls.
* Enforce authorization: ensure only approved apps/users can request relayer submits.
***
### Troubleshooting
**Q: sendRelayerTx returns an error / 4xx or 5xx** - Verify relayerUrl and apiKey are correct. - Check server time skew (if signatures used). - Inspect payload (ABI, method, args) for mismatches.
**Q: WebSocket won't connect** - Ensure your environment allows outgoing WS connections. - Check CORS / firewall rules. - Confirm webSocket.enabled and autoConnect flags are set.
**Q: Transactions stuck in pending** - Check blockchain network congestion and relayer health page (dashboard). - Verify gas estimation and paymaster configuration if used.
***
### Quickstart Checklist
* Create an App on dashboard → Add Relayer → copy Relayer URL + API Key
* Install `@abstraxn/relayer` in your server environment
* Instantiate Relayer with secure credentials
* `buildRelayerTx()` → `sendRelayerTx()` or `sendRelayerTxWithRealTimeUpdates()`
* Monitor via `getRelayerTxStatus()` or WebSocket events
* Integrate with Smart Accounts, Paymaster, Bundler for full agent flows
***
### TL;DR
The `@abstraxn/relayer` SDK enables agent-driven, gasless on-chain execution with both polling and real-time capabilities. Keep keys server-side, validate intent, and prefer WebSocket for best UX.
***
### Useful Links
* NPM package: [https://www.npmjs.com/package/@abstraxn/relayer](https://www.npmjs.com/package/@abstraxn/relayer)
* Dashboard: [https://dashboard.abstraxn.com/](https://dashboard.abstraxn.com/)
* Contact / support: [https://www.abstraxn.com/contact-us/](https://www.abstraxn.com/contact-us/)
## Solana Relayer SDK
The **@abstraxn/solana-relayer** package lets your app build, sign, and relay Solana transactions — **gaslessly**. The relayer reserves a fee-payer on your behalf so end-users never need SOL for fees. Gas sponsorship works the same way as the [Paymaster](/guides/Paymaster/paymasterIntegration) on EVM chains — you fund a gas pool from the dashboard and the relayer draws from it to cover transaction fees on behalf of your users.
**What you can do:**
* Send native SOL transfers
* Send SPL-token transfers (with automatic ATA creation)
* Submit arbitrary program instructions
* Track transaction status until finality
* Sponsor gas for your users (same model as EVM Paymaster)
***
### Installation
:::code-group
```bash [npm]
npm install @abstraxn/solana-relayer
```
```bash [Yarn]
yarn add @abstraxn/solana-relayer
```
```bash [pnpm]
pnpm add @abstraxn/solana-relayer
```
```bash [Bun]
bun add @abstraxn/solana-relayer
```
:::
***
### Setup
#### Relayer URL Format
The Solana relayer URL follows this pattern:
```
https://solana-paymaster.abstraxn.com/api/v1/{chainId}/?apikey={your_api_key}
```
| Placeholder | Description | Example |
| ---------------- | ------------------------------------------- | ------------------------------------ |
| `{chainId}` | Solana chain identifier from your dashboard | `103` (devnet), `101` (mainnet-beta) |
| `{your_api_key}` | API key from your Abstraxn app | `ak_live_abc123...` |
#### Get Your Relayer URL
1. Visit [https://dashboard.abstraxn.com/](https://dashboard.abstraxn.com/) and log in or sign up
2. Go to **Apps** → create or select an app with **Solana** as a supported chain
3. Navigate to **Paymaster** — if your app has Solana selected, you will see the Solana paymaster link
4. Copy the **Paymaster URL** — this is your relayer URL
The Solana Relayer uses the same gas sponsorship model as the EVM [Paymaster](/guides/Paymaster/paymasterIntegration). Fund your gas pool from the Paymaster section in the dashboard, and the relayer draws from it to cover transaction fees on behalf of your users.
:::warning
Your relayer URL contains your API key. Store it in environment variables — never hardcode it in client-side code.
:::
#### Create the Client
```ts
import { SolanaRelayer } from "@abstraxn/solana-relayer";
const client = new SolanaRelayer({
relayerUrl: process.env.SOLANA_RELAYER_URL!,
// e.g. "https://solana-paymaster.abstraxn.com/api/v1/103/?apikey=your_api_key"
});
```
That's it — one config, one client. All four SDK methods use this instance.
***
### Core Flow
Every relayed transaction follows four steps:
```
buildTx → signTx → sendTx → getTxStatus
```
| Step | What happens |
| --------------- | ----------------------------------------------------------------------------------------------- |
| **buildTx** | SDK fetches an available relayer, reserves it as fee-payer, and builds a `VersionedTransaction` |
| **signTx** | User's wallet adapter signs the transaction |
| **sendTx** | Signed transaction is submitted to the relayer hub along with the reservation token |
| **getTxStatus** | Poll until the transaction reaches `confirmed` or `failed` |
:::tip
`sendTx` **must** be called after a successful `buildTx` — the SDK stores the reservation token from the build step internally.
:::
***
### Native SOL Transfer
Send SOL from one wallet to another, with gas paid by the relayer:
```ts
import { Connection, PublicKey } from "@solana/web3.js";
import { SolanaRelayer } from "@abstraxn/solana-relayer";
const client = new SolanaRelayer({
relayerUrl: process.env.SOLANA_RELAYER_URL!,
});
const connection = new Connection(
"https://api.devnet.solana.com",
"confirmed",
);
// 1. Build the transaction
const { tx, lastValidBlockHeight } = await client.buildTx({
connection,
sender: new PublicKey(""),
to: new PublicKey(""),
lamports: 1_000_000, // 0.001 SOL
});
// 2. Sign with the user's wallet
const serializedUserSignedTx = await client.signTx({
wallet: walletAdapter, // e.g. from @solana/wallet-adapter-react
transaction: tx,
});
// 3. Submit to the relayer
const submit = await client.sendTx({
signedTransaction: serializedUserSignedTx,
lastValidBlockHeight,
});
// 4. Track status
const finalStatus = await client.getTxStatus({
txnId: submit.txnId,
});
console.log(finalStatus.status); // "confirmed" or "failed"
console.log(finalStatus.signature); // on-chain transaction signature
```
***
### SPL Token Transfer
Transfer any SPL token. If the recipient's Associated Token Account (ATA) does not exist, the SDK automatically creates it with the relayer as rent payer.
```ts
const { tx, lastValidBlockHeight } = await client.buildTx({
connection,
sender: new PublicKey(""),
mint: new PublicKey(""),
recipientOwner: new PublicKey(""),
amountUi: "1.25", // human-readable amount
decimals: 6, // fallback — SDK uses on-chain decimals when available
splTokenProgramId: new PublicKey(""),
splAssociatedTokenProgramId: new PublicKey(""),
});
// Then sign, send, and track status as above
const signed = await client.signTx({ wallet: walletAdapter, transaction: tx });
const submit = await client.sendTx({ signedTransaction: signed, lastValidBlockHeight });
const status = await client.getTxStatus({ txnId: submit.txnId });
```
#### Token transfer parameters
| Parameter | Type | Required | Description |
| ----------------------------- | ------------ | -------- | ------------------------------------- |
| `connection` | `Connection` | Yes | Solana RPC connection |
| `sender` | `PublicKey` | Yes | Token owner's public key |
| `mint` | `PublicKey` | Yes | SPL token mint address |
| `recipientOwner` | `PublicKey` | Yes | Recipient's wallet address (not ATA) |
| `amountUi` | `string` | Yes | Human-readable amount (e.g. `"1.25"`) |
| `decimals` | `number` | Yes | Fallback decimals if RPC lookup fails |
| `splTokenProgramId` | `PublicKey` | Yes | Token program (Token or Token-2022) |
| `splAssociatedTokenProgramId` | `PublicKey` | Yes | Associated Token Account program |
:::tip
When the mint supports `jsonParsed` RPC, the SDK reads **on-chain decimals** and uses `TransferChecked`. The `decimals` parameter is only a fallback. If the sender or recipient ATA is missing, the SDK prepends a `createAssociatedTokenAccount` instruction with the relayer as payer.
:::
***
### Custom Program Transaction
For any instruction set that isn't a simple transfer — swaps, NFT mints, program calls — pass your own instructions:
#### Static instructions
```ts
import { TransactionInstruction, PublicKey } from "@solana/web3.js";
const instructions: TransactionInstruction[] = [
// Your custom instructions here
];
const { tx, lastValidBlockHeight } = await client.buildTx({
connection,
sender: new PublicKey(""),
instructions,
});
```
#### Dynamic instructions with `buildInstructions`
When your instructions need the **relayer's public key** before they can be built (e.g. ATA creation where the relayer pays rent), use the `buildInstructions` callback:
```ts
const { tx, lastValidBlockHeight } = await client.buildTx({
connection,
sender: new PublicKey(""),
buildInstructions: async ({ feePayer }) => {
// feePayer is the reserved relayer's public key
return [
createAssociatedTokenAccountInstruction(
feePayer, // payer (relayer)
ata, // ATA to create
recipient, // owner of the new ATA
mint, // token mint
),
createTransferCheckedInstruction(
senderAta, // source
mint, // mint
ata, // destination
senderPubkey, // owner
amount, // raw amount
decimals, // token decimals
),
];
},
});
```
:::warning
`instructions`, `buildInstructions`, and `transaction` are **mutually exclusive** — pass exactly one. Passing more than one will throw an error.
:::
***
### `buildTx` Input Variants
`buildTx` accepts exactly **one** of three transaction types:
| Type | Required fields | Use case |
| ------------------- | --------------------------------------------------------------------------------------------------------- | ---------------- |
| **Native transfer** | `to` + `lamports` | Send SOL |
| **Token transfer** | `mint` + `recipientOwner` + `amountUi` + `decimals` + `splTokenProgramId` + `splAssociatedTokenProgramId` | Send SPL tokens |
| **Custom** | `instructions`, `buildInstructions`, or `transaction` (pick one) | Any program call |
All variants require `connection` and `sender`.
***
### API Reference
#### `new SolanaRelayer(config)`
| Option | Type | Description |
| ------------ | -------- | -------------------------------------------- |
| `relayerUrl` | `string` | Full relayer endpoint URL (includes API key) |
#### `buildTx(params)` → `{ tx, lastValidBlockHeight }`
Fetches an available relayer, reserves it, and builds a `VersionedTransaction`. Returns the transaction and block height for expiry tracking.
#### `signTx(params)` → `serializedTransaction`
| Parameter | Type | Description |
| ------------- | ---------------------- | ------------------------------ |
| `wallet` | Wallet Adapter | Solana wallet adapter instance |
| `transaction` | `VersionedTransaction` | Transaction from `buildTx` |
#### `sendTx(params)` → `{ txnId }`
| Parameter | Type | Description |
| ---------------------- | -------- | ------------------------------------------- |
| `signedTransaction` | `string` | Serialized signed transaction from `signTx` |
| `lastValidBlockHeight` | `number` | Block height from `buildTx` |
#### `getTxStatus(params)` → `{ status, signature }`
| Parameter | Type | Description |
| --------- | -------- | ---------------------------- |
| `txnId` | `string` | Transaction ID from `sendTx` |
Returns:
| Field | Type | Values |
| ----------- | -------- | ------------------------------ |
| `status` | `string` | `"confirmed"` or `"failed"` |
| `signature` | `string` | On-chain transaction signature |
***
### How It Works Under the Hood
The SDK communicates with `sol-relayer-hub` using a JSON-RPC style protocol over a single POST endpoint:
```
POST
Content-Type: application/json
{ "method": "", "params": [...], "id": 1, "jsonrpc": "2.0" }
```
| RPC Method | SDK Method | Purpose |
| ------------------------------------ | ------------- | --------------------------- |
| `sol_getAvailableRelayer` | `buildTx` | Reserve a fee-payer relayer |
| `sol_sendTx` / `sol_sendProgrammeTx` | `sendTx` | Submit signed transaction |
| `sol_getTxStatus` | `getTxStatus` | Poll transaction status |
You never call these directly — the SDK handles serialization, reservation tokens, and retries.
***
### Integrating with Wallet Adapters
The `signTx` method accepts any standard Solana wallet adapter. Here's a React example using `@solana/wallet-adapter-react`:
```tsx
import { useWallet, useConnection } from "@solana/wallet-adapter-react";
import { PublicKey } from "@solana/web3.js";
import { SolanaRelayer } from "@abstraxn/solana-relayer";
function SendButton() {
const { publicKey, wallet } = useWallet();
const { connection } = useConnection();
const handleSend = async () => {
if (!publicKey || !wallet) return;
const client = new SolanaRelayer({
relayerUrl: process.env.NEXT_PUBLIC_SOLANA_RELAYER_URL!,
});
const { tx, lastValidBlockHeight } = await client.buildTx({
connection,
sender: publicKey,
to: new PublicKey("11111111111111111111111111111112"),
lamports: 1_000_000,
});
const signed = await client.signTx({
wallet: wallet.adapter,
transaction: tx,
});
const { txnId } = await client.sendTx({
signedTransaction: signed,
lastValidBlockHeight,
});
// Poll until confirmed
let status;
do {
await new Promise((r) => setTimeout(r, 2000));
status = await client.getTxStatus({ txnId });
} while (status.status !== "confirmed" && status.status !== "failed");
console.log("Final:", status.status, status.signature);
};
return (
);
}
```
***
### Troubleshooting
**Q: `buildTx` fails with "no relayer available"**
All relayers are currently reserved. Wait a moment and retry — relayers are released after submission or timeout.
**Q: `sendTx` returns an error**
* Ensure you call `sendTx` **after** a successful `buildTx` (the reservation token is stored internally).
* Check that the `lastValidBlockHeight` has not expired — if the block height has passed, rebuild the transaction.
**Q: Token transfer fails with "account not found"**
* Verify the `mint` address is correct on the target network (devnet vs mainnet).
* Ensure `splTokenProgramId` matches the token standard (Token vs Token-2022).
**Q: Transaction stuck in pending**
* Check Solana network status for congestion.
* Verify the relayer hub is healthy via its health endpoint.
***
### Security Notes
* Store the relayer URL (which contains your API key) in server-side environment variables.
* Validate all user inputs (addresses, amounts) before building transactions.
* The relayer only pays gas from your sponsored pool — it cannot move user funds. Users must still sign with their own wallet.
* Monitor your gas pool balance in the [dashboard](https://dashboard.abstraxn.com/) to avoid failed sponsorships.
***
### Useful Links
* Dashboard: [https://dashboard.abstraxn.com/](https://dashboard.abstraxn.com/)
* EVM Relayer Docs: [Relayer (EVM)](/guides/relayer)
* Support: [https://www.abstraxn.com/contact-us/](https://www.abstraxn.com/contact-us/)
## Supported Chains
Abstraxn AI agents operate on a broad set of **EVM-compatible chains**. Below is a list of **production-ready** mainnets and their corresponding testnets, each identified by chain ID and slug for use in SDK config, RPC routing, or agent initialization.
### Mainnets
| Chain / Network | Chain ID | Slug |
| ------------------- | -------- | ------------- |
| Ethereum | 1 | ethereum |
| BNB Chain (BSC) | 56 | binance |
| Polygon (PoS) | 137 | polygon |
| Avalanche (C-Chain) | 43114 | avalanche |
| Optimism | 10 | optimism |
| Arbitrum One | 42161 | arbitrum |
| Arbitrum Nova | 42170 | arbitrum-nova |
| Base | 8453 | base |
| Celo | 42220 | celo |
| Zora | 7777777 | zora |
| Mantle | 5000 | mantle |
| Berachain | 80094 | berachain |
| Blast | 81457 | blast |
| Bob | 60808 | bob |
| Gnosis Chain | 100 | gnosis |
| Linea | 59144 | linea |
| Moonbeam (Polkadot) | 1284 | moonbeam |
| OP BNB | 204 | opbnb |
| Scroll | 534352 | scroll |
| Sei | 1329 | sei |
### Testnets & Previews
| Network | Chain ID | Slug |
| ----------------- | --------- | ---------------------- |
| Goerli (Ethereum) | 5 | goerli |
| Base Sepolia | 84532 | base-sepolia |
| Optimism Sepolia | 11155420 | optimism-sepolia |
| Arbitrum Sepolia | 421614 | arbitrum-sepolia |
| Polygon Amoy | 80002 | polygon-amoy |
| Zora Sepolia | 999999999 | zora-sepolia |
| Mantle Sepolia | 5003 | mantle-sepolia |
| Blast Sepolia | 168587773 | blast-sepolia |
| Bob Sepolia | 808813 | bob-sepolia |
| Celo Alfajores | 44787 | celo-alfajores-testnet |
### Why It Matters
* **Uniform UX across chains** — agents behave identically regardless of network
* **Consistent SDK config** — use chain IDs or slugs to route intent and execution
* **Gas-optimized routing** — choose lower-cost chains for actions; fall back to Ethereum when needed
* **Future expansion** — we keep pace with emerging EVM chains; want one added? Let us know.
### Example Integration Snippet
```js
import { Agent } from '@abstraxn/sdk'
const supportedChains = [
{ name: 'Ethereum', chainId: 1, slug: 'ethereum', rpc: 'https://mainnet.infura.io/v3/…' },
{ name: 'Polygon', chainId: 137, slug: 'polygon', rpc: 'https://polygon-rpc.com' },
{ name: 'Arbitrum', chainId: 42161, slug: 'arbitrum', rpc: 'https://arb1.arbitrum.io/rpc' },
// …other chains
]
Agent.initialize({
apiKey: 'YOUR_API_KEY',
chains: supportedChains
})
```
## Why Choose Abstraxn?
**Seamless onboarding meets intelligent interactions**: at Abstraxn, we believe Web3 should be accessible to everyone. We start with frictionless wallet integration, then layer on AI agents that understand user intent and execute blockchain actions effortlessly.
***
### Wallet Integration That Just Works
* **Zero-friction onboarding** with social logins (Google, Twitter, Discord, GitHub) and email authentication
* **Passwordless security** with passkey support using WebAuthn standards
* **Fully customizable UI** to match your brand and provide a native experience
* **Smart account ready** with built-in support for Safe, Kernel, ThirdWeb, and other leading providers
* **Multi-chain by default** supporting 100+ EVM-compatible chains from day one
* **Production-ready** with enterprise-grade security and reliability
Why build wallet infrastructure from scratch when you can integrate in minutes and focus on your core product?
***
### AI-First, Infra-Powered
* **Effortless Web3 interaction** through conversational agents
* Users chat. Agents handle all the complexity behind the scenes.
* Our lightweight infra stack (relayers, bundlers, EIP-7702 paymasters, smart accounts/signers) powers the actions invisibly.
***
### Users Stay Because You Understand Them
* **Users stick with platforms that understand them**, not the other way around
* Embed an AI Chat Agent that listens, answers, guides—and even acts.
* No confusing forms, no wallet juggling, just natural language intent.
***
### Built to Deliver Impact
* Boost engagement, retention, and transaction volume with intuitive user flows
* Drive developer velocity with modular SDKs, React components, and API-first interfaces
* Scale easily—AI agents manage the state, infra executes the logic
***
### Secure by Design
* Only trusted flows reach the chain—agents vet intent before action
* Gasless UX with EIP-7702-backed paymasters and bundled transactions
* Pluggable smart accounts and signers (Safe, Kernel, ThirdWeb, Dynamic, Web3Auth, Privy)
***
### Future-Ready & Modular
* **Agent Marketplace** (coming soon): plug-and-play use-case agents for trading, staking, NFTs…
* **Agentic Framework** (coming soon): build and orchestrate your own AI agents atop our infrastructure
* One integration & limitless UX possibilities
***
### Quick Summary
| Benefit | Delivered By Abstraxn |
| ------------------------ | -------------------------------------------------------------------------- |
| **Seamless Onboarding** | Social logins, email auth, and passkey support for instant wallet creation |
| **AI-Driven UX** | Chat-first interface powered by natural language intent |
| **Invisible Web3 Infra** | Relayers, bundles, paymasters, smart accounts handled in-agent |
| **Developer Friendly** | JS SDK, React embeds, REST APIs for any stack |
| **Secure & Compliant** | Enterprise-grade security, intent checking, and wallet integrations |
| **Scalable & Modular** | Multi-chain support, marketplace & framework for future use cases |
***
### TL;DR: Why Us?
* Because **Onboarding should be instant**, not a barrier
* Because **Intent is the new interface**
* So you get **Effortless Web3 interaction** from wallet creation to transaction execution
* And because **Users stick with platforms that understand them**, not the other way around
***
**Next Steps for Developers**
1. **Integrate Wallets**—enable social logins, email auth, and passkeys in minutes with our [Wallet Quickstart](/guides/Wallets/Quickstart).
2. **Embed the Chat AI Agent**—make your documentation interactive and let users express intent naturally.
3. **Enable chain actions**—powered by bundlers, relayers, and paymasters working seamlessly beneath the surface.
4. **Explore upcoming tools**: **Agent Marketplace** & **Agentic Framework** to expand capabilities.
Abstraxn provides everything from secure wallet onboarding to AI-powered intent execution—building frictionless Web3 experiences has never been easier. Let's build the future together.
## Wallets API Quickstart (SDK)
The `@abstraxn/signer-react` and `@abstraxn/signer-core` packages help you add embedded and external wallet onboarding to your dApp.
### 1. Installation
Install both SDK packages using your preferred package manager:
:::code-group
```bash [npm]
npm i @abstraxn/signer-react @abstraxn/signer-core
```
```bash [Yarn]
yarn add @abstraxn/signer-react @abstraxn/signer-core
```
```bash [Bun]
bun add @abstraxn/signer-react @abstraxn/signer-core
```
```bash [pnpm]
pnpm add @abstraxn/signer-react @abstraxn/signer-core
```
:::
### 2. Get Your API Key
1. Visit [https://dashboard.abstraxn.com/](https://dashboard.abstraxn.com/)
2. Log in or sign up
3. Go to **Apps** and click **Create New App**
4. Navigate to **Wallets** and click **Add Wallet Service**
5. Click **View Details** and copy your API key
:::warning
Keep your API key in environment variables. Do not hardcode it in client code.
:::
### 3. Setup Provider
Wrap your app with `AbstraxnProvider`:
```tsx
import { AbstraxnProvider } from "@abstraxn/signer-react";
export function AppProviders({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
### 3b. Enable Solana Gasless Transactions (Optional)
If your app supports Solana, you can enable gasless transactions by adding the `solanaGasless` option to your provider config. This uses the same gas sponsorship model as the [Solana Relayer](/guides/solana-relayer) — fees are paid from your paymaster gas pool.
```tsx
import { AbstraxnProvider } from "@abstraxn/signer-react";
export function AppProviders({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
To get your Solana relayer URL, go to your [Abstraxn Dashboard](https://dashboard.abstraxn.com/) → **Paymaster** — if your app has Solana selected, you will see the Solana paymaster link.
### 4. Connect Wallet
Use the built-in connect UI:
```tsx
import { ConnectButton } from "@abstraxn/signer-react";
export function WalletButton() {
return ;
}
```
### 5. Send a Transaction (Current Flow)
Use `usePrepareRawTxn` -> `useSignAndSendTxn` -> `useWaitForTxnReceipt`.
```tsx
import { useState } from "react";
import {
useAddress,
useIsConnected,
usePublicClient,
usePrepareRawTxn,
useSignAndSendTxn,
useWaitForTxnReceipt,
useEstimateGas,
useGetGasPrice,
} from "@abstraxn/signer-react";
import { polygonAmoy } from "viem/chains";
import { parseEther } from "viem";
export function SendTransaction() {
const address = useAddress();
const isConnected = useIsConnected();
const [sending, setSending] = useState(false);
const [hash, setHash] = useState<`0x${string}` | null>(null);
const { publicClient } = usePublicClient(
polygonAmoy,
"https://rpc-amoy.polygon.technology"
);
const { prepareRawTxn } = usePrepareRawTxn(publicClient);
const { signAndSendTxn } = useSignAndSendTxn(publicClient);
const { waitForTxnReceipt } = useWaitForTxnReceipt(publicClient);
const { estimateGas } = useEstimateGas(publicClient);
const { getGasPrice } = useGetGasPrice(publicClient);
const handleSend = async () => {
if (!isConnected || !address) return;
setSending(true);
try {
const rawTx = await prepareRawTxn({
from: address as `0x${string}`,
to: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
value: parseEther("0.001"),
});
const { gasLimit } = await estimateGas({
account: address as `0x${string}`,
to: rawTx.to,
data: rawTx.data,
value: rawTx.value,
});
const fees = await getGasPrice();
const result = await signAndSendTxn({
from: address as `0x${string}`,
...rawTx,
gas: {
gasLimit,
maxFeePerGas: fees.maxFeePerGas,
maxPriorityFeePerGas: fees.maxPriorityFeePerGas,
gasPrice: fees.gasPrice,
},
});
setHash(result.hash as `0x${string}`);
await waitForTxnReceipt({ hash: result.hash as `0x${string}`, confirmations: 1 });
} finally {
setSending(false);
}
};
return (
);
}
```
### Useful Links
* Dashboard: [https://dashboard.abstraxn.com/](https://dashboard.abstraxn.com/)
* Support: [https://www.abstraxn.com/contact-us/](https://www.abstraxn.com/contact-us/)
## Authentication
The server wallet authentication flow is designed to be **idempotent** — calling `authenticate()` multiple times with the same identity is safe and will reuse existing credentials when possible.
### How It Works
```
authenticate()
│
├─ Token already in store? ──► Reuse it (no network call)
│
└─ No token?
│
├─ Derive public key from accessKey
├─ POST /auth/create (register identity)
├─ POST /auth/exchange (get access token)
└─ Store token ──► Return session
```
On the **first call**, the SDK:
1. Generates or accepts an `accessKey` (64-char hex string)
2. Derives a public key from it
3. Calls `create` to register the identity with the backend
4. Calls `exchange` to obtain an access token
5. Stores the token for future use
On **subsequent calls** with the same identity, the stored token is returned immediately.
### Usage
```ts
import { ServerSignerClient } from '@abstraxn/server-signer';
const client = new ServerSignerClient({
apiKey: process.env.ABSTRAXN_API_KEY!,
});
const session = await client.authenticate({
userIdentity: 'merchant-backend-user-001',
accessKey: process.env.SERVER_WALLET_ACCESS_KEY, // optional on first run
userName: 'Merchant Backend',
userEmail: 'backend@example.com',
});
```
#### Parameters
| Parameter | Type | Required | Description |
| -------------- | -------- | -------- | --------------------------------------------------------------- |
| `userIdentity` | `string` | Yes | Unique identifier for this server wallet user |
| `accessKey` | `string` | No | 64-char hex key. If omitted on first run, the SDK generates one |
| `userName` | `string` | No | Display name for the wallet user |
| `userEmail` | `string` | No | Email for the wallet user |
#### Return Value
| Field | Type | Description |
| ----------------- | --------- | -------------------------------------------------------------------- |
| `didCreate` | `boolean` | `true` if this was a new identity, `false` if reusing existing token |
| `accessToken` | `string` | Current JWT access token |
| `accessKey` | `string` | The 64-char hex access key (generated or provided) |
| `targetPublicKey` | `string` | Public key derived from the access key |
| `organizationId` | `string` | Organization ID for this wallet |
| `walletAddress` | `string` | On-chain wallet address |
### Access Key Management
The `accessKey` is the **root secret** for a server wallet identity. It is used to derive the public key that identifies the wallet on-chain.
:::warning
If you lose the access key, you **cannot recover** the same wallet identity. Always persist it in a secure secrets manager (AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, etc.).
:::
**First run** — let the SDK generate the key, then save it:
```ts
const session = await client.authenticate({
userIdentity: 'merchant-backend-user-001',
});
// Persist this immediately
await secretsManager.set('SERVER_WALLET_ACCESS_KEY', session.accessKey);
```
**Subsequent runs** — provide the saved key:
```ts
const session = await client.authenticate({
userIdentity: 'merchant-backend-user-001',
accessKey: process.env.SERVER_WALLET_ACCESS_KEY,
});
```
### Automatic Token Refresh
Authenticated API calls automatically handle expired tokens:
1. A call receives a `401 Unauthorized` response
2. The SDK sends `POST /auth/refresh` with session credentials
3. If the refresh succeeds, the access token is updated and the original call is retried
4. If the refresh fails, the error is thrown to the caller
No configuration is needed — this behavior is built into every authenticated method (`whoami`, `signTransaction`, `exportPrivateKey`, etc.).
### Identity Conflicts
If you call `authenticate` with a `userIdentity` that already exists but provide a **different** `accessKey`, the backend returns a `409 CONFLICT` error:
```
ConflictError: userIdentity 'merchant-backend-user-001'
already exists with a different access key
```
This protects against accidental identity collisions. To resolve:
* Use the **original** access key for this identity, or
* Choose a **different** `userIdentity` string
### Next Steps
* \[Transactions & Signing]\(./Transactions and Signing) — send transactions, sign messages and typed data
* \[Export & Security]\(./Export and Security) — export private keys and follow security best practices
## Export & Security
### Export Private Key
You can export the decrypted private key from a server wallet. This is useful for migration or backup scenarios.
```ts
const decryptedPrivateKey = await client.exportPrivateKey({
organizationId: 'fdbbad46-047f-4ff7-a13c-9e14bca69857',
address: '0x5bBF0b7847A7a35E419eD431069fE92542B4f5c3',
embeddedPrivateKey: process.env.SERVER_WALLET_ACCESS_KEY!,
blockchain: 'evm',
});
```
| Parameter | Type | Description |
| -------------------- | -------- | ----------------------------------------------- |
| `organizationId` | `string` | Organization ID from your authenticated session |
| `address` | `string` | Wallet address to export |
| `embeddedPrivateKey` | `string` | Your 64-char hex access key |
| `blockchain` | `string` | `'evm'` or `'solana'` |
:::warning
The exported key gives **full control** over the wallet. Handle it with extreme care — encrypt it at rest, never log it, and never transmit it over insecure channels.
:::
### Error Handling
The SDK throws typed errors that preserve the backend's error code and message. Wrap your calls in try/catch to handle them gracefully:
```ts
import {
ValidationError,
UnauthorizedError,
ConflictError,
} from '@abstraxn/server-signer';
try {
const session = await client.authenticate({
userIdentity: 'merchant-backend-user-001',
accessKey: process.env.SERVER_WALLET_ACCESS_KEY,
});
} catch (error) {
if (error instanceof ConflictError) {
// 409: identity exists with a different access key
console.error('Access key mismatch for this identity');
} else if (error instanceof UnauthorizedError) {
// 401: token expired and refresh failed
console.error('Authentication failed');
} else if (error instanceof ValidationError) {
// 400: missing or invalid parameters
console.error('Invalid input:', error.message);
} else {
throw error;
}
}
```
#### Common Error Codes
| Status | Error Type | When It Happens |
| ------ | ------------------- | --------------------------------------------------------------------------------------- |
| `400` | `ValidationError` | Missing required fields (e.g. stamped payload fields for `whoami` or `signTransaction`) |
| `401` | `UnauthorizedError` | Token missing or expired, and automatic refresh failed |
| `409` | `ConflictError` | `userIdentity` already registered with a different access key or public key |
### MFA (Multi-Factor Authentication)
The SDK includes MFA methods for additional security on sensitive operations:
```ts
// Check MFA status
const status = await client.mfaStatus();
// Enable MFA
await client.mfaEnable();
// Verify MFA setup with a TOTP code
await client.mfaVerifySetup({ code: '123456' });
// Verify MFA for a standard operation
await client.mfaVerify({ code: '123456' });
// Verify MFA specifically for signing
await client.mfaVerifySign({ code: '123456' });
// Disable MFA
await client.mfaDisable({ code: '123456' });
```
### Security Best Practices
#### Store secrets securely
Never hardcode `accessKey` or API keys in source code. Use a secrets manager:
```ts
// ✅ Good — loaded from environment / secrets manager
const client = new ServerSignerClient({
apiKey: process.env.ABSTRAXN_API_KEY!,
});
const session = await client.authenticate({
userIdentity: 'payout-service',
accessKey: process.env.SERVER_WALLET_ACCESS_KEY,
});
```
```ts
// ❌ Bad — hardcoded secrets
const client = new ServerSignerClient({
apiKey: 'ak_live_abc123...',
});
```
#### Never expose keys in frontend code
The `@abstraxn/server-signer` package is designed for Node.js backends. If you need wallet functionality in a browser, use [`@abstraxn/signer-react`](../Quickstart) instead.
#### Avoid logging sensitive data
Do not log raw access keys, private keys, or full signatures in production:
```ts
// ❌ Bad
console.log('Access key:', session.accessKey);
console.log('Private key:', decryptedPrivateKey);
// ✅ Good — log only non-sensitive identifiers
console.log('Authenticated identity:', session.userIdentity);
console.log('Wallet address:', session.walletAddress);
```
#### Rotate and audit
* Rotate API keys periodically from the [Abstraxn Dashboard](https://dashboard.abstraxn.com/)
* Monitor your wallet transactions for unexpected activity
* Use separate `userIdentity` values for different services (e.g. `payout-service`, `minting-service`)
### Deprecated Aliases
These methods still work but will be removed in a future version:
| Deprecated | Use Instead |
| -------------------------------- | ------------------------- |
| `ensureServerWalletSession(...)` | `authenticate(...)` |
| `createViemClient(...)` | `createPublicClient(...)` |
### Next Steps
* [Quickstart](./Quickstart) — get up and running in 5 minutes
* [Authentication](./Authentication) — understand the full auth model
* \[Transactions & Signing]\(./Transactions and Signing) — send transactions, sign messages and typed data
## Server Wallet Quickstart
The `@abstraxn/server-signer` package lets your **backend** create wallets, sign transactions, and execute on-chain operations — without any user-facing UI.
Use it when you need:
* Automated server-to-server transactions (payouts, treasury ops, batch minting)
* Backend-controlled signing (no browser, no popup)
* Custodial or semi-custodial wallet flows managed entirely in Node.js
:::warning
This SDK is for **server environments only**. Never expose your API key or access key in frontend code.
:::
### 1. Install the SDK
:::code-group
```bash [npm]
npm install @abstraxn/server-signer
```
```bash [Yarn]
yarn add @abstraxn/server-signer
```
```bash [pnpm]
pnpm add @abstraxn/server-signer
```
```bash [Bun]
bun add @abstraxn/server-signer
```
:::
### 2. Get Your API Key
1. Go to [https://dashboard.abstraxn.com/](https://dashboard.abstraxn.com/)
2. Log in or sign up
3. Navigate to **Apps** → **Create New App**
4. Go to **Wallets** → **Add Wallet Service**
5. Click **View Details** and copy your **API key**
### 3. Authenticate
Create a client and authenticate a server wallet identity:
```ts
import { ServerSignerClient } from '@abstraxn/server-signer';
const client = new ServerSignerClient({
apiKey: process.env.ABSTRAXN_API_KEY!,
});
const session = await client.authenticate({
userIdentity: 'merchant-backend-user-001',
userName: 'Merchant Backend',
userEmail: 'backend@example.com',
});
console.log(session.didCreate); // true on first run, false on subsequent calls
console.log(session.accessToken); // JWT access token
console.log(session.accessKey); // 64-char hex key — persist this securely!
console.log(session.targetPublicKey); // derived public key
```
:::tip
On the first call, `authenticate` generates a new `accessKey` for you. **Save it** in a secrets manager — you will need it to recover the same identity later.
:::
### 4. Verify the Session
Confirm your wallet identity:
```ts
const me = await client.whoami();
console.log(me);
// { userId: '...', organizationId: '...', walletAddress: '0x...' }
```
### 5. Send Your First Transaction
Create a public client and send a native transfer:
```ts
const publicClient = client.createPublicClient({
rpcUrl: process.env.RPC_URL!,
chainId: 137, // Polygon mainnet
organizationId: session.organizationId,
fromAddress: session.walletAddress,
});
// Prepare the transaction (adds nonce, chain ID, gas fields)
const prepared = await publicClient.prepareTransaction({
to: '0x1111111111111111111111111111111111111111',
value: 1_000_000_000_000_000n, // 0.001 MATIC
});
// Sign and broadcast in one step
const txHash = await publicClient.signAndSendPreparedTransaction(
prepared.unsignedTransaction,
);
// Wait for on-chain confirmation
const receipt = await publicClient.waitForTransactionReceipt(txHash);
console.log('Confirmed in block:', receipt.blockNumber);
```
### Next Steps
* [Authentication](./Authentication) — understand the full auth model, token refresh, and access key management
* \[Transactions & Signing]\(./Transactions and Signing) — prepare transactions, sign messages, typed data, and raw payloads
* \[Export & Security]\(./Export and Security) — export private keys, handle errors, and follow security best practices
## Transactions & Signing
After [authenticating](./Authentication), create a **public client** to interact with the blockchain. The public client wraps RPC calls with backend-stamped signing so your server can prepare, sign, and broadcast transactions.
### Create a Public Client
```ts
const publicClient = client.createPublicClient({
rpcUrl: process.env.RPC_URL!,
chainId: 137,
organizationId: 'fdbbad46-047f-4ff7-a13c-9e14bca69857',
fromAddress: '0x5bBF0b7847A7a35E419eD431069fE92542B4f5c3',
});
```
| Parameter | Type | Description |
| ---------------- | -------- | --------------------------------------------------- |
| `rpcUrl` | `string` | JSON-RPC endpoint for the target chain |
| `chainId` | `number` | Chain ID (e.g. `137` for Polygon, `1` for Ethereum) |
| `organizationId` | `string` | Organization ID from your authenticated session |
| `fromAddress` | `string` | Wallet address to sign and send from |
### Transaction Flow
The SDK follows a **prepare → sign → send → receipt** pattern, similar to Viem.
#### Step 1: Prepare
Build the unsigned transaction with nonce, chain ID, and gas parameters populated automatically:
```ts
const prepared = await publicClient.prepareTransaction({
to: '0x1111111111111111111111111111111111111111',
value: 1_000_000_000_000_000n, // 0.001 ETH/MATIC
});
```
You can also pass `data` for contract calls:
```ts
const prepared = await publicClient.prepareTransaction({
to: '0xContractAddress...',
value: 0n,
data: '0xa9059cbb...', // encoded function call
});
```
#### Step 2: Sign and Send
Sign the prepared transaction with your server wallet key and broadcast it to the network:
```ts
const txHash = await publicClient.signAndSendPreparedTransaction(
prepared.unsignedTransaction,
);
console.log('Transaction hash:', txHash);
```
#### Step 3: Wait for Receipt
Block until the transaction is confirmed on-chain:
```ts
const receipt = await publicClient.waitForTransactionReceipt(txHash);
console.log('Block:', receipt.blockNumber);
console.log('Status:', receipt.status); // 'success' or 'reverted'
```
#### Full Example
```ts
import { ServerSignerClient } from '@abstraxn/server-signer';
const client = new ServerSignerClient({
apiKey: process.env.ABSTRAXN_API_KEY!,
});
const session = await client.authenticate({
userIdentity: 'payout-service',
accessKey: process.env.SERVER_WALLET_ACCESS_KEY,
});
const publicClient = client.createPublicClient({
rpcUrl: 'https://polygon-rpc.com',
chainId: 137,
organizationId: session.organizationId,
fromAddress: session.walletAddress,
});
const prepared = await publicClient.prepareTransaction({
to: '0xRecipientAddress...',
value: 1_000_000_000_000_000n,
});
const txHash = await publicClient.signAndSendPreparedTransaction(
prepared.unsignedTransaction,
);
const receipt = await publicClient.waitForTransactionReceipt(txHash);
console.log('Payout confirmed:', receipt.transactionHash);
```
### Sign a Message
Sign an arbitrary string message (EIP-191 personal sign):
```ts
const signature = await publicClient.signMessage({
message: 'hello from server-signer',
});
console.log('Signature:', signature);
```
### Sign Typed Data (EIP-712)
Sign structured data following the EIP-712 standard:
```ts
const signature = await publicClient.signTypedData({
typedData: {
domain: {
name: 'Abstraxn',
version: '1',
chainId: 137,
},
message: {
contents: 'Hi',
},
primaryType: 'Mail',
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
],
Mail: [
{ name: 'contents', type: 'string' },
],
},
},
});
```
### Sign Raw Payload
Sign an arbitrary hex-encoded payload with a specific hash function:
```ts
const signature = await publicClient.signRawPayload({
payload: '0x1234',
encoding: 'PAYLOAD_ENCODING_HEXADECIMAL',
hashFunction: 'HASH_FUNCTION_SHA256',
});
```
| Parameter | Type | Options |
| -------------- | -------- | ------------------------------------------------------------------------ |
| `payload` | `string` | Hex-encoded data to sign |
| `encoding` | `string` | `PAYLOAD_ENCODING_HEXADECIMAL`, `PAYLOAD_ENCODING_UTF8` |
| `hashFunction` | `string` | `HASH_FUNCTION_SHA256`, `HASH_FUNCTION_KECCAK256`, `HASH_FUNCTION_NO_OP` |
### Next Steps
* \[Export & Security]\(./Export and Security) — export private keys and follow security best practices
* [Authentication](./Authentication) — learn about the auth model and token lifecycle
## React Quickstart
> Learn how to get started with Abstraxn Smart Wallets in React.
Create a new app with embedded wallets, social login, and gasless transactions.
> You can also follow the [Quickstart in the Dashboard](https://dashboard.abstraxn.com/services/smart-wallets/quickstart).
### 1. Clone template repo
```bash
git clone https://github.com/Abstraxn-Labs/wallets-quickstart.git my-abstraxn-wallets-app
cd my-abstraxn-wallets-app
npm install
```
### 2. Set up environment
Once you have your project cloned down and you are in the `my-abstraxn-wallets-app` level in your terminal, go to your project's root, create a `.env` file and copy-paste the following into it:
```shell
# Paste this in your .env file
VITE_ABSTRAXN_API_KEY=YOUR_ABSTRAXN_API_KEY
```
:::tip
**Note:** This template uses Vite, so environment variables must be prefixed with `VITE_` to be accessible in the browser. See `env.example` in the repository for reference.
:::
#### Get Your API Key
1. Visit [https://dashboard.abstraxn.com/](https://dashboard.abstraxn.com/)
2. Log in or sign up
3. Go to **Apps** → click **Create New App**
4. Navigate to **Wallets** → click **Add Wallet Service**
5. Click **View Details** → copy your **API Key**
### 3. Run app
In your terminal, run:
```bash
npm run dev
```
Your app will be available at `http://localhost:5173` (or the port shown in the terminal). When you first open the app, you'll see the initial screen with the connect wallet button:
### 4. Connect your wallet
Click the **Connect Wallet** button to open the Abstraxn onboarding modal. You can use a service like [Temp Mail](https://temp-mail.org/en/) to test logins with throwaway email accounts.
:::info
**Note:** Social login (Google, Twitter, Discord) is available once configured in your dashboard. Email OTP authentication works out of the box.
:::
#### Email OTP Authentication
1. Enter your email address in the onboarding modal
2. You will receive a 6-digit authentication code via email
3. Enter the code in the verification screen:
#### Success! 🎉
Once you have successfully signed in and your wallet is connected, you'll see the main app interface displaying your connection status and wallet information:
The connected wallet view shows:
* **Connection Status**: Displays "Connected" in green, confirming your wallet is active
* **Wallet Address**: Your wallet's Ethereum address is displayed and can be copied
* **Open Wallet Button**: Click to open the Abstraxn wallet modal with additional features like:
* Chain/network selector
* Send and receive functionality
* Transaction history
* Wallet management options
* **Disconnect Button**: Disconnect your wallet when needed
### 5. Next steps
Congratulations! You've successfully set up the Abstraxn Smart Wallets quickstart. Now you can:
* **Send transactions**: \[Learn how to send transactions]\(/guides/Wallets/Get Started/React/From scratch/App Integration) using the SDK hooks
* **Customize the UI**: \[Customize the wallet UI]\(./From scratch/UI Customization) to match your brand
* **Configure authentication**: \[Set up social login and passkeys]\(./From scratch/Dashboard customization) in the dashboard
* **Explore advanced features**:
* [Smart Accounts](/guides/Accounts/SmartAccounts/Accounts) - Integrate with various smart account providers
* [Paymasters](/guides/Paymaster/Paymaster) - Enable gasless transactions
* [AI Chat Agents](/guides/AI/chatAI) - Add conversational interfaces to your app
### Manual Setup (Alternative)
If you prefer to set up from scratch instead of using the template, check out our \[From Scratch guide]\(/guides/Wallets/Get Started/React/From scratch/Initialization) for step-by-step instructions.
### Useful Links
* Dashboard: [https://dashboard.abstraxn.com/](https://dashboard.abstraxn.com/)
* Support: [https://www.abstraxn.com/contact-us/](https://www.abstraxn.com/contact-us/)
* Documentation: [Getting Started](/guides/getting-started)
## App Integration
> Integrate Abstraxn Smart Wallets into your Next.js application
Now that you've set up your provider configuration, it's time to integrate Abstraxn Smart Wallets into your Next.js application.
### Using the Connect Button
The `ConnectButton` component is a ready-to-use button that triggers the onboarding modal:
```tsx
"use client";
import { ConnectButton } from "@abstraxn/signer-react";
export default function Home() {
return (
Welcome to My App
);
}
```
### Using Hooks
You can use the provided hooks to access wallet state and functionality:
```tsx
"use client";
import { useIsConnected, useAddress, useAbstraxnWallet } from "@abstraxn/signer-react";
export function WalletInfo() {
const isConnected = useIsConnected();
const address = useAddress();
const { disconnect, loading } = useAbstraxnWallet();
if (!isConnected) {
return
);
}
```
### Alternative: Using Hook to Trigger Onboarding
You can also trigger the onboarding modal programmatically using the `showOnboarding` method:
```tsx
"use client";
import { useAbstraxnWallet } from "@abstraxn/signer-react";
export function CustomConnectButton() {
const { showOnboarding, loading } = useAbstraxnWallet();
return (
);
}
```
### Complete Example
Here's a complete example combining all the pieces:
```tsx
"use client";
import { useState } from "react";
import {
ConnectButton,
useIsConnected,
useAddress,
useAbstraxnWallet,
usePublicClient,
usePrepareRawTxn,
useSignAndSendTxn,
useWaitForTxnReceipt
} from "@abstraxn/signer-react";
import { polygonAmoy } from "viem/chains";
import { parseEther } from "viem";
export default function Home() {
const isConnected = useIsConnected();
const address = useAddress();
const { disconnect } = useAbstraxnWallet();
const { publicClient } = usePublicClient(
polygonAmoy,
"https://rpc-amoy.polygon.technology"
);
const { prepareRawTxn } = usePrepareRawTxn(publicClient);
const { signAndSendTxn } = useSignAndSendTxn(publicClient);
const { waitForTxnReceipt } = useWaitForTxnReceipt(publicClient);
const [loading, setLoading] = useState(false);
const [txHash, setTxHash] = useState(null);
const handleSend = async () => {
if (!isConnected || !address) {
alert("Please connect your wallet first");
return;
}
setLoading(true);
try {
const rawTx = await prepareRawTxn({
from: address as `0x${string}`,
to: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
value: parseEther("0.001"),
});
const result = await signAndSendTxn({
from: address as `0x${string}`,
...rawTx,
});
await waitForTxnReceipt({
hash: result.hash as `0x${string}`,
confirmations: 1,
});
setTxHash(result.hash);
alert(`Transaction sent! Hash: ${result.hash}`);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
return (
My Abstraxn App
{isConnected && (
Address: {address}
{txHash &&
Tx Hash: {txHash}
}
)}
);
}
```
### Next Steps
* Learn about \[customizing the UI]\(./UI Customization)
* Learn how to enable [MFA](./MFA)
* Explore [Solana support](./Solana)
* Explore \[authentication methods]\(./Dashboard customization)
* Check out more in the [Wallets Guide](/guides/Wallets/Quickstart)
## Dashboard Customization
> Customize your wallet dashboard settings including email templates, social login providers, and security configurations
The Abstraxn Dashboard provides comprehensive customization options for your wallet service. You can configure email templates, enable social login providers, and set up security settings to match your brand and requirements.
### Email Template Customization
Customize the email verification template that users receive when signing in with email OTP. This allows you to brand the authentication emails to match your application's identity.
#### Accessing Email Template Settings
1. Navigate to your app in the [Abstraxn Dashboard](https://dashboard.abstraxn.com/)
2. Go to **Wallets** → Select your wallet service
3. Click on the **Email Login** tab
#### Available Customization Options
##### Project Name
Set your application name that will appear in the email subject and body:
* **Field**: Project name
* **Default**: "YOUR APP NAME"
* **Usage**: This name appears in the email subject line and verification code message
##### Support URL (Optional)
Add a support URL where users can get help:
* **Field**: Support URL (optional)
* **Format**: e.g., `support@dapp.xyz` or `https://support.yourdomain.com`
* **Purpose**: Provides users with a way to contact support if they encounter issues
##### Logo (Optional)
Upload your application logo to appear in the email:
* **Field**: Logo (optional)
* **Accepted formats**: `.png`, `.jpg`, or `.webp`
* **Size limit**: Up to 2MB
* **Display**: Logo appears in the email template to reinforce your brand identity
##### Primary Color (Optional)
Set your brand's primary color for the email template:
* **Field**: Primary color (optional)
* **Format**: Hex color code (e.g., `#9333ea`)
* **Usage**: Applied to buttons, highlights, and accent elements in the email
#### Email Preview
The dashboard provides a live preview of how your verification email will appear to users. The preview shows:
* Email subject line with verification code
* Sender information
* Branded email body with your logo and colors
* 6-digit verification code display
* Security disclaimer text
#### Saving Settings
After configuring your email template settings, click **Save Settings** to apply your changes. The updated template will be used for all future email verification codes sent to your users.
### Social Login Configuration
Enable and configure social authentication providers to allow users to sign in with their existing accounts from popular services.
#### Accessing Social Login Settings
1. Navigate to your app in the [Abstraxn Dashboard](https://dashboard.abstraxn.com/)
2. Go to **Wallets** → Select your wallet service
3. Click on the **Social Login** tab
#### Available Social Providers
##### Google Social Login
Enable Google OAuth authentication for your application.
**Configuration Options:**
* **Toggle**: Enable/disable Google login
* **Client ID (optional)**: Your Google OAuth Client ID
* Format: `e.g. ey1289x....`
* Leave empty to use Abstraxn's default credentials
* **Client Secret (optional)**: Your Google OAuth Client Secret
* Format: Secret key from Google Console
* Toggle visibility with the eye icon
**Using Default Credentials:**?
* Start quickly with Abstraxn's default Google credentials
* No additional setup required
* Suitable for development and testing
**Using Your Own Credentials:**
For production or greater control over security and branding:
1. Create a project in [Google Cloud Console](https://console.cloud.google.com/)
2. Enable Google+ API
3. Create OAuth 2.0 credentials
4. Add the callback URL provided by Abstraxn to your Google Console API client
5. Copy your Client ID and Client Secret to the dashboard
6. Click the copy icon next to the callback URL message to copy the required callback URL
##### X (Twitter) Social Login
Enable X (formerly Twitter) OAuth authentication for your application.
**Configuration Options:**
* **Toggle**: Enable/disable X login
* **Client ID (optional)**: Your X OAuth Client ID
* Format: `e.g. ey1289x....`
* Leave empty to use Abstraxn's default credentials
* **Client Secret (optional)**: Your X OAuth Client Secret
* Format: Secret key from X Developer Portal
* Toggle visibility with the eye icon
**Using Default Credentials:**
* Start quickly with Abstraxn's default X credentials
* No additional setup required
* Suitable for development and testing
**Using Your Own Credentials:**
For production or greater control:
1. Create an app in [X Developer Portal](https://developer.twitter.com/)
2. Configure OAuth 2.0 settings
3. Add the callback URL provided by Abstraxn to your X Console API client
4. Copy your Client ID and Client Secret to the dashboard
5. Click the copy icon next to the callback URL message to copy the required callback URL
##### Discord Social Login
Enable Discord OAuth authentication for your application.
**Configuration Options:**
* **Toggle**: Enable/disable Discord login
* **Client ID (optional)**: Your Discord OAuth Client ID
* **Client Secret (optional)**: Your Discord OAuth Client Secret
**Setup Steps:**
1. Create an application in [Discord Developer Portal](https://discord.com/developers/applications)
2. Configure OAuth2 redirect URI
3. Add the callback URL provided by Abstraxn
4. Copy your Client ID and Client Secret to the dashboard
#### Enabling Social Login in Your App
After configuring social login providers in the dashboard, you can enable them in your application code:
```tsx
const providerConfig: AbstraxnProviderConfig = {
apiKey: process.env.NEXT_PUBLIC_ABSTRAXN_API_KEY,
ui: {
authMethods: ["otp", "google", "twitter", "discord"], // Enable configured providers
},
};
```
### Passkey Authentication
Enable Passkey (WebAuthn) authentication to allow users to sign in using biometric authentication or security keys.
#### Accessing Passkey Settings
1. Navigate to your app in the [Abstraxn Dashboard](https://dashboard.abstraxn.com/)
2. Go to **Wallets** → Select your wallet service
3. Click on the **Passkey** tab
#### Configuration
**Enable Passkey:**
* **Toggle**: Enable/disable Passkey authentication
* **Description**: Allow users to authenticate with Passkeys using biometric authentication (fingerprint, face recognition) or security keys
**Benefits:**
* Enhanced security with public key cryptography
* Passwordless authentication experience
* Support for biometric authentication
* Cross-device compatibility
#### Enabling Passkey in Your App
After enabling Passkey in the dashboard, add it to your authentication methods:
```tsx
const providerConfig: AbstraxnProviderConfig = {
apiKey: process.env.NEXT_PUBLIC_ABSTRAXN_API_KEY,
ui: {
authMethods: ["otp", "passkey", "google"], // Include 'passkey'
},
};
```
### Security Settings
Configure security settings to protect your application and control access.
#### Accessing Security Settings
1. Navigate to your app in the [Abstraxn Dashboard](https://dashboard.abstraxn.com/)
2. Go to **Wallets** → Select your wallet service
3. Click on the **Security** tab
#### Origin Whitelist (Recommended)
The Origin Whitelist restricts which domains can initiate wallet connections, preventing unauthorized access and potential security vulnerabilities.
**Configuration:**
* **Field**: Origin Whitelist
* **Format**: Full URL with protocol (e.g., `https://example.com`)
* **Purpose**: Only allow wallet connections from whitelisted origins
* **Security**: Helps prevent cross-site request forgery (CSRF) attacks
**Adding Origins:**
1. Enter the full URL of your application domain
2. Include the protocol (`https://` or `http://`)
3. Click **Submit** to add the origin to the whitelist
4. You can add multiple origins for different environments (development, staging, production)
**Example Origins:**
```
https://myapp.com
https://www.myapp.com
https://staging.myapp.com
http://localhost:3000 // For local development
```
**Best Practices:**
* Always whitelist your production domain
* Include staging and development URLs as needed
* Remove unused origins regularly
* Never whitelist wildcard domains (`*`) in production
:::info
**Note:** While the origin whitelist is optional, it's highly recommended for production applications to enhance security and prevent unauthorized access.
:::
### Next Steps
After configuring your dashboard settings:
* \[Customize the UI]\(./UI Customization) - Further customize the login experience in your app
* \[Integrate your app]\(./App Integration) - Connect your application to the configured wallet service
* [Set up Smart Accounts](/guides/Accounts/SmartAccounts/Accounts) - Configure smart account providers
* [Configure Paymaster](/guides/Paymaster/Paymaster) - Enable gasless transactions
### Useful Links
* Dashboard: [https://dashboard.abstraxn.com/](https://dashboard.abstraxn.com/)
* Support: [https://www.abstraxn.com/contact-us/](https://www.abstraxn.com/contact-us/)
* Documentation: [Getting Started](/guides/getting-started)
## Environment Setup
> How to set up Abstraxn Smart Wallets using the Abstraxn Dashboard.
### Get Your API Key
To use Abstraxn Smart Wallets, you need to obtain an API key from the Abstraxn Dashboard.
:::tip
**Why create an App first?**
An **App** in the Abstraxn Dashboard represents your application or project. It acts as a container that organizes all your services, including Wallet Services, Paymasters, Bundlers, and more.
Think of it this way:
* **App** = Your project/application (e.g., "My DApp", "NFT Marketplace")
* **Wallet Service** = A specific wallet service configuration within that app
You need to create an App first because Wallet Services are associated with a specific App. This allows you to:
* Organize multiple services under one project
* Manage different configurations for different apps
* Track usage and analytics per application
Once you create an App, you can then add a Wallet Service to it, which will provide you with the API Key needed for your React application.
:::
1. Visit [https://dashboard.abstraxn.com/](https://dashboard.abstraxn.com/) and log in or sign up
2. Navigate to the **Apps** section:
* Direct link: [https://dashboard.abstraxn.com/auth/apps](https://dashboard.abstraxn.com/auth/apps)
:::info
**First-time users:** If you're visiting for the first time and haven't created any apps yet, you'll see an empty table with a **Create New App** button at the top. This is normal - proceed to step 3 to create your first app.
:::
If you already have apps, you'll see a list of your existing apps and a **Create New App** button at the top.
3. Click the **Create New App** button to open the creation modal
Fill in the form:
* **Chain**: Select the blockchain (e.g., Ethereum, Polygon, etc.)
* **Network**: Select the network (Mainnet, Testnet, etc.)
* **Name**: Enter a name for your app
* **Description**: (Optional) Add a description
* Click **Create App**
4. After creating your app, navigate to **Wallets** section:
* Direct link: [https://dashboard.abstraxn.com/auth/wallets](https://dashboard.abstraxn.com/auth/wallets)
5. Click **Add Wallets** button to create a new wallet service
Fill in the form:
* **Wallet Name**: Enter a name for your wallet service
* **App**: Select the app you created earlier
* Click **Create Wallet**
6. After creating the wallet, click **View Details** on your wallet from the wallets list
7. On the wallet details page, you'll see your **API Key** displayed. Click the copy button to copy it.
The wallet details page URL format is: `https://dashboard.abstraxn.com/auth/wallets/{walletId}`
:::warning
**Warning:** Keep your API key secure by storing it in environment variables. Never expose it in public client-side code or commit it to version control.
:::
### Configure Environment Variables
Create a `.env` file at the root of your project and add your API key.
#### For Vite Projects
Create a `.env` file in your project root:
```env
VITE_ABSTRAXN_API_KEY=your_api_key_here
```
The `VITE_` prefix is required for Vite to expose the variable to your client-side code. Access it in your code using:
```tsx
import.meta.env.VITE_ABSTRAXN_API_KEY;
```
#### For Next.js Projects
Create a `.env.local` file in your project root:
```env
NEXT_PUBLIC_ABSTRAXN_API_KEY=your_api_key_here
```
The `NEXT_PUBLIC_` prefix is required for Next.js to expose the variable to the browser. Access it in your code using:
```tsx
process.env.NEXT_PUBLIC_ABSTRAXN_API_KEY;
```
:::tip
**Note:** For Next.js, you can also use `.env` for server-side only variables, but use `.env.local` for variables that should be available in the browser. The `.env.local` file is automatically ignored by git.
:::
### Additional Configuration (Optional)
#### Wallet Service Configuration
To enable specific login methods and customize your wallet service:
1. Navigate to **Wallets** → **Configuration** in the [dashboard](https://dashboard.abstraxn.com/services/smart-wallets/configuration)
2. Configure your preferred authentication methods (OTP, Passkey, Google, Discord, Twitter, etc.)
3. Customize wallet settings as needed
## Hooks
> Comprehensive guide to using React hooks from the Abstraxn Wallet SDK
This document provides comprehensive usage examples for all hooks available in the Abstraxn Wallet SDK React library.
### Table of Contents
1. [Connection & Status Hooks](#connection--status-hooks)
2. [Authentication Hooks](#authentication-hooks)
3. [Transaction Hooks](#transaction-hooks)
4. [Contract Interaction Hooks](#contract-interaction-hooks)
5. [MFA Hooks](#mfa-hooks)
6. [Typed Signing](#typed-signing)
7. [External Wallet Client Hook](#external-wallet-client-hook)
8. [Solana Hooks](#solana-hooks)
9. [Complete Transaction Flow Example](#complete-transaction-flow-example)
10. [Notes](#notes)
***
### Connection & Status Hooks
#### `useIsConnected()`
Check if the wallet is currently connected.
```tsx
import { useIsConnected } from '@abstraxn/signer-react';
function MyComponent() {
const isConnected = useIsConnected();
return (
{isConnected ? 'Wallet Connected' : 'Wallet Not Connected'}
);
}
```
**Returns:** `boolean` - `true` if wallet is connected, `false` otherwise.
***
#### `useAddress()`
Get the current wallet address.
```tsx
import { useAddress } from '@abstraxn/signer-react';
function MyComponent() {
const address = useAddress();
return (
);
}
```
**Returns:** `string | null` - The wallet address or `null` if not connected.
***
#### `useChainId()`
Get the current chain ID.
```tsx
import { useChainId } from '@abstraxn/signer-react';
function MyComponent() {
const chainId = useChainId();
return (
{chainId ? `Chain ID: ${chainId}` : 'No chain ID available'}
);
}
```
**Returns:** `number | null` - The chain ID or `null` if not connected.
***
#### `useError()`
Get the current error state.
```tsx
import { useError } from '@abstraxn/signer-react';
function MyComponent() {
const error = useError();
if (error) {
return
Error: {error.message}
;
}
return
No errors
;
}
```
**Returns:** `Error | null` - The current error or `null` if no error.
***
#### `useWallet()`
Get the wallet instance for advanced usage.
```tsx
import { useWallet } from '@abstraxn/signer-react';
function MyComponent() {
const wallet = useWallet();
// Use wallet for advanced operations
// Note: Most operations should use dedicated hooks instead
}
```
**Returns:** Wallet instance or `null` if not connected.
***
#### `useConnectionType()`
Get the connection type (how the user connected).
```tsx
import { useConnectionType } from '@abstraxn/signer-react';
function MyComponent() {
const { connectionType, connectorMeta } = useConnectionType();
if (!connectionType) {
return
);
}
```
***
### Notes
1. **Provider Setup**: Most hooks require a `PublicClient` instance. Create it using `usePublicClient(chain, rpcUrl)`.
2. **Connection Types**:
* Social logins (Google, email, X, Discord, passkey) use embedded wallets
* External wallets (MetaMask, WalletConnect) use the connected wallet directly
3. **Transaction Flow**:
* For embedded wallets: Use `usePrepareRawTxn` → `useSignAndSendTxn` → `useWaitForTxnReceipt`
* For external wallets: Use `useWriteContract` or `useExternalWalletInfo().walletClient`
4. **Error Handling**: Always wrap hook calls in try-catch blocks for proper error handling.
5. **Loading States**: Many hooks provide loading states. Use them to show loading indicators in your UI.
6. **Chain Switching**: Use `useSwitchChain()` to switch chains. It works for both embedded and external wallets.
7. **MFA Step-Up**: Signing hooks may require an MFA step-up verification code when enhanced security is enabled.
### Next Steps
* Learn about \[App Integration]\(./App Integration) - Integrate hooks into your application
* Explore \[UI Customization]\(./UI Customization) - Customize the wallet UI
* Configure [MFA](./MFA) - Enable or disable two-factor flows
* Build with [Solana](./Solana) - Embedded and external Solana wallets
* Check out \[Dashboard customization]\(./Dashboard customization) - Configure authentication methods
## Initialization
> Build Abstraxn Smart Wallets in a new app
In this doc and the following ones, we will build a React application with Abstraxn Smart Wallets from scratch. This guide supports both **Vite** and **Next.js** setups.
### Create a new React app
Choose your preferred framework:
#### Option 1: Vite + React (Recommended)
Start by initializing a new React application with Vite:
```bash
npm create vite@latest my-abstraxn-app -- --template react-ts
cd my-abstraxn-app
```
Or with other package managers:
```bash pnpm
pnpm create vite my-abstraxn-app --template react-ts
cd my-abstraxn-app
```
```bash yarn
yarn create vite my-abstraxn-app --template react-ts
cd my-abstraxn-app
```
#### Option 2: Next.js
Start by initializing a new [Next.js app](https://nextjs.org/):
```bash
npx create-next-app@latest \
--typescript \
--tailwind \
--app
cd my-abstraxn-app
```
This command passes in flags to use Typescript, Tailwind and the next app router.
### Install Dependencies
You will need these core libraries:
* **@abstraxn/signer-react**: Abstraxn Signer SDK React package for handling wallet interactions and transaction signing
* **@abstraxn/signer-core**: Abstraxn Signer SDK core package (required dependency)
* **@tanstack/react-query**: A required async state library for managing smart wallet state ([learn more](https://tanstack.com/query/latest/docs/framework/react/overview))
Install them with:
```bash npm
npm install @abstraxn/signer-react @abstraxn/signer-core @tanstack/react-query
```
```bash pnpm
pnpm add @abstraxn/signer-react @abstraxn/signer-core @tanstack/react-query
```
```bash yarn
yarn add @abstraxn/signer-react @abstraxn/signer-core @tanstack/react-query
```
### Setup AbstraxnProvider
The Abstraxn SDK requires wrapping your application with the `AbstraxnProvider` component. This provides the wallet context to all components in your app.
#### For Vite Projects
Update your `src/main.tsx` (or `src/main.jsx`) file:
```tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import {
AbstraxnProvider,
type AbstraxnProviderConfig,
} from "@abstraxn/signer-react";
const providerConfig: AbstraxnProviderConfig = {
apiKey: import.meta.env.VITE_ABSTRAXN_API_KEY || "YOUR_API_KEY",
autoConnect: false,
autoInit: true,
enableLogging: true,
ui: {
authMethods: ["otp", "passkey", "google", "discord", "twitter"],
theme: "light",
logoName: "My App",
welcomeMessage: "Welcome to My App",
showFooter: true,
},
};
createRoot(document.getElementById("root")!).render(
);
```
#### For Next.js Projects
Update your `app/layout.tsx` (App Router) or `pages/_app.tsx` (Pages Router):
**app/layout.tsx** (App Router):
```tsx
import {
AbstraxnProvider,
type AbstraxnProviderConfig,
} from "@abstraxn/signer-react";
const providerConfig: AbstraxnProviderConfig = {
apiKey: process.env.NEXT_PUBLIC_ABSTRAXN_API_KEY || "YOUR_API_KEY",
autoConnect: false,
autoInit: true,
enableLogging: true,
ui: {
authMethods: ["otp", "passkey", "google", "discord", "twitter"],
theme: "light",
logoName: "My App",
welcomeMessage: "Welcome to My App",
showFooter: true,
},
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{children}
);
}
```
**pages/\_app.tsx** (Pages Router):
```tsx
import type { AppProps } from "next/app";
import {
AbstraxnProvider,
type AbstraxnProviderConfig,
} from "@abstraxn/signer-react";
const providerConfig: AbstraxnProviderConfig = {
apiKey: process.env.NEXT_PUBLIC_ABSTRAXN_API_KEY || "YOUR_API_KEY",
autoConnect: false,
autoInit: true,
enableLogging: true,
ui: {
authMethods: ["otp", "passkey", "google", "discord", "twitter"],
theme: "light",
logoName: "My App",
welcomeMessage: "Welcome to My App",
showFooter: true,
},
};
export default function App({ Component, pageProps }: AppProps) {
return (
);
}
```
> **Note**: Replace `YOUR_API_KEY` with your actual Abstraxn API key. You can obtain one from your [Abstraxn Dashboard](https://dashboard.abstraxn.com/).
### Enable Solana Gasless Transactions (Optional)
If your app supports Solana, add the `solanaGasless` option to your provider config. This sponsors transaction fees from your paymaster gas pool so users never need SOL for gas.
```tsx
const providerConfig: AbstraxnProviderConfig = {
apiKey: import.meta.env.VITE_ABSTRAXN_API_KEY || "YOUR_API_KEY",
autoConnect: false,
autoInit: true,
enableLogging: true,
solanaGasless: {
enabled: true,
relayerUrl: import.meta.env.VITE_SOLANA_RELAYER_URL,
// e.g. "https://solana-paymaster.abstraxn.com/api/v1/103/?apikey=your_api_key"
},
ui: {
authMethods: ["otp", "passkey", "google", "discord", "twitter"],
theme: "light",
logoName: "My App",
welcomeMessage: "Welcome to My App",
showFooter: true,
},
};
```
| Option | Type | Description |
| -------------------------- | --------- | ----------------------------------------------------------------------------------------------- |
| `solanaGasless.enabled` | `boolean` | Set to `true` to enable gasless Solana transactions |
| `solanaGasless.relayerUrl` | `string` | Your Solana paymaster URL from the [dashboard](https://dashboard.abstraxn.com/) → **Paymaster** |
:::tip
To get your relayer URL, go to the dashboard → **Paymaster**. If your app has Solana selected as a chain, you will see the Solana paymaster link there.
:::
### Next Steps
Now that you have the basic setup complete, you can:
* \[Configure your environment]\(./Environment setup) - Set up environment variables and configuration
* \[Use the SDK hooks]\(./App Integration) - Learn how to integrate wallet functionality in your app
* \[Customize the UI]\(./UI Customization) - Customize the onboarding and wallet UI components
## MFA
Use MFA hooks to enable two-factor authentication, verify disable flows, and handle signing step-up challenges.
### Prerequisites
* `AbstraxnProvider` is already configured.
* User is authenticated and wallet is connected.
### Enable MFA Flow
The high-level flow is:
1. `start()` -> returns setup payload (`qrCode`, `secret`)
2. user scans QR in authenticator app
3. `verifyCode("123456")` -> returns backup codes
```tsx
import { useEnableMfa } from "@abstraxn/signer-react";
export function EnableMfaPanel() {
const {
canEnableMfaFlow,
setupState,
isLoading,
error,
start,
goToVerify,
verifyCode,
} = useEnableMfa();
if (!canEnableMfaFlow) {
return
);
}
```
### Provider MFA Cache Fields
`useAbstraxnWallet()` exposes MFA status caching helpers:
* `mfaStatus`: cached result from `/mfa/status`
* `mfaStatusLoading`: loading state for cache fetch
* `prefetchMfaStatus({ force?: boolean })`: fetch status (or refresh with `force`)
* `invalidateMfaStatus()`: clear cache (for logout/disconnect flows)
```tsx
import { useAbstraxnWallet } from "@abstraxn/signer-react";
export function MfaStatusBanner() {
const { mfaStatus, mfaStatusLoading, prefetchMfaStatus, invalidateMfaStatus } =
useAbstraxnWallet();
return (
{mfaStatusLoading ?
Loading...
:
Status: {JSON.stringify(mfaStatus)}
}
);
}
```
### Signing Step-Up Notes
`useSignTxn`, `useSignAndSendTxn`, `useSignTypedTx`, `useSignSolanaTxn`, and `useSignAndSendSolanaTxn` can require MFA step-up.
* On `SIGN_MFA_REQUIRED`, SDK prompts for a code and retries signing.
* If verification expires, users should retry the signing action.
* Handle errors in UI and show a clear retry path.
### Next Steps
* Integrate flows in \[App Integration]\(./App Integration)
* Review all hook APIs in [Hooks](./Hooks)
* Set chain/network behavior in [Initialization](./Initialization)
## Solana
This guide covers Solana support for embedded wallets and external Solana wallets.
### Provider Setup
Enable Solana in provider chain configuration:
```tsx
import { AbstraxnProvider } from "@abstraxn/signer-react";
;
```
### Connection and Balance
Use `useSolanaPublicKey` and `useSolanaBalance` for active account info:
```tsx
import { useSolanaPublicKey, useSolanaBalance } from "@abstraxn/signer-react";
export function SolanaWalletInfo() {
const { solanaAddress, isConnected } = useSolanaPublicKey();
const { formattedBalance, lamports, loading } = useSolanaBalance();
if (!isConnected) return
Connect wallet first
;
if (loading) return
Loading balance...
;
return (
Address: {solanaAddress}
Balance: {formattedBalance} SOL ({lamports?.toString()} lamports)
// [!code focus]
)} // [!code focus]
> // [!code focus]
) // [!code focus]
}
```
::::
Should additional authentication requirements or integration considerations emerge, we remain prepared to conduct further analysis and refine our Passkey integration strategy to maintain optimal alignment with evolving security standards and user experience expectations within the Abstraxn ecosystem.
## How to use Web3Auth with Abstraxn
Our comprehensive evaluation of embedded wallet providers has identified [Web3Auth](https://web3auth.io) as a strategic authentication solution that supports diverse login methodologies within the Abstraxn ecosystem. Web3Auth provides sophisticated native account abstraction capabilities, enabling streamlined user experience through social authentication, sponsored transaction mechanisms, and comprehensive transaction batching functionality.
### Strategic Configuration Framework
To enable Account Abstraction integration within your Web3Auth implementation, navigate to your [Web3Auth dashboard](https://dashboard.web3auth.io) and activate the Smart Accounts functionality through the sidebar interface. Once enabled, configure the Abstraxn bundler and paymaster endpoints according to your strategic infrastructure requirements.
Our deliberative analysis indicates that comprehensive dashboard configuration represents a critical foundation for optimal account abstraction performance within the platform ecosystem.
> Detailed configuration guidance can be referenced through specialized documentation resources for advanced implementation scenarios.
### Implementation Architecture
After establishing your Web3Auth project configuration, implement the following strategic modifications to your client-side application architecture.
#### Web3Auth Provider Architecture
Following Web3Auth's established integration methodology, you will obtain access to a `web3auth` object that can be strategically utilized as an owner parameter for `createSmartAccountClient` operations:
```typescript
import { WEB3AUTH_NETWORK, type Web3AuthOptions } from "@web3auth/modal"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { WagmiProvider } from "@web3auth/modal/react/wagmi"
import { type ReactNode } from "react"
import { Web3AuthProvider } from "@web3auth/modal/react"
if (!process.env.NEXT_PUBLIC_WEB3AUTH_CLIENT_ID) {
// get from https://dashboard.web3auth.io
throw new Error("missing NEXT_PUBLIC_WEB3AUTH_CLIENT_ID env var")
}
const web3AuthOptions: Web3AuthOptions = {
clientId: process.env.NEXT_PUBLIC_WEB3AUTH_CLIENT_ID,
web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_DEVNET
}
const web3authConfig = {
web3AuthOptions
}
const queryClient = new QueryClient()
export function AppProvider({ children }: { children: ReactNode }) {
return (
{children}
)
}
```
#### Application Provider Integration
Configure your application's entry point to utilize the previously established provider architecture:
```typescript
"use client"
import { Home } from "@/components/home"
import { AppProvider } from "@/components/provider"
export default function Main() {
return (
)
}
```
#### Web3Auth Authentication Component
Leverage Web3Auth's sophisticated authentication hooks for comprehensive session management. This component provides strategic authentication state handling, displaying login interfaces when disconnected and presenting smart contract wallet information when connected:
```typescript
import { useAccount } from "wagmi"
import {
useWeb3AuthConnect,
useWeb3AuthDisconnect
} from "@web3auth/modal/react"
export function Web3AuthConnect() {
const { connect, isConnected, connectorName } = useWeb3AuthConnect()
const { disconnect } = useWeb3AuthDisconnect()
const { address } = useAccount()
if (isConnected && address) {
return (
Connected to {connectorName}
Smart Contract Wallet: {address}
)
}
return (
)
}
```
#### Strategic User Operation Execution
Once authentication is established, you can execute User Operations through wagmi hooks integration. The smart account configuration from your Web3Auth dashboard will manage bundling operations and gas sponsorship mechanisms when paymaster integration is configured:
```typescript
import { useSendTransaction, useWaitForTransactionReceipt } from "wagmi"
import { zeroAddress } from "viem"
export function SendUserOperation() {
const {
data: hash,
error,
isPending,
sendTransaction
} = useSendTransaction()
const { isLoading: isConfirming, isSuccess: isConfirmed } =
useWaitForTransactionReceipt({ hash })
const handleSendTransaction = () => {
sendTransaction({
to: zeroAddress,
data: "0x",
value: BigInt(0)
})
}
return (
{hash && (
Transaction Hash:
{hash}
)}
{isConfirming &&
Waiting for confirmation...
}
{isConfirmed &&
Transaction confirmed
}
{error &&
Error: {error.message}
}
)
}
```
Our collaborative synthesis of Web3Auth's authentication capabilities with Abstraxn's account abstraction framework represents a strategic advancement in user onboarding and transaction management workflows.
For comprehensive implementation examples, reference specialized demonstration applications that showcase complete Web3Auth integration patterns within account abstraction environments.
## Paymaster Client Integration
### Understanding Paymasters in the EIP-7702 Ecosystem
Imagine being able to pay for someone else's gas fees on Ethereum, or even better, allowing users to interact with your application without needing ETH in their wallets at all. This is exactly what paymasters enable in the account abstraction world. Think of a paymaster as a benevolent sponsor that agrees to cover transaction costs under certain conditions, fundamentally transforming how users interact with blockchain applications.
When we talk about paymasters in the context of EIP-7702 transactions, we're discussing services that implement the ERC-7677 specification. This standard defines how paymasters communicate with bundlers and user operations, creating a standardized way to sponsor gas fees. The beauty of this system lies in its flexibility—you can sponsor transactions for specific users, certain types of operations, or even all interactions with your application.
The relationship between users, paymasters, and bundlers creates a powerful triangle of functionality. Users submit operations without worrying about gas fees, paymasters evaluate whether to sponsor these operations based on predefined rules, and bundlers coordinate the entire process to ensure smooth execution on the blockchain. Understanding this relationship helps clarify why we need specialized client tooling to interact with paymaster services effectively.
### The Role of Paymaster Clients
A Paymaster Client serves as your application's interface to paymaster services, handling the complex negotiations and data formatting required to sponsor user operations. While bundler clients focus on submitting and tracking user operations, paymaster clients specialize in the sponsorship aspect—determining eligibility, calculating costs, and providing the cryptographic proofs that bundlers need to include sponsored operations in their bundles.
Think of the Paymaster Client as a specialized diplomatic service. It speaks the language of both your application and the paymaster service, translating your sponsorship intentions into the technical formats that the ERC-7677 specification requires. This abstraction allows you to focus on your sponsorship business logic rather than worrying about the underlying protocol mechanics.
The client also provides crucial validation and safety features. It can verify that paymaster responses are properly formatted, ensure that gas estimates are reasonable, and provide feedback when sponsorship requests fail. These features become essential when building production applications that depend on reliable transaction sponsorship.
### Obtaining Paymaster URL & API Key
To begin, you’ll need a Bundler URL and API Key. Retrieve these from your Abstraxn Dashboard by following these steps:
* Visit [https://dashboard.abstraxn.com/](https://dashboard.abstraxn.com/)
* Log in or sign up
* Go to Apps → click Create New App → enter name, chain, description
* Within the app, navigate to Paymaster → click Add Paymaster, assign a name, select app
* Click View Details → copy your Paymaster URL and API Key
### Dashboard Flow (Gas Sponsorship + Paymaster Setup)
1. Go to the dashboard and open **Gas sponsorship**.
2. Fill your gas budget (you can fund via **crypto** or **fiat**). The UI shows the budget in **USD**.
3. Review **deposit history** and **gas usage history** so you can see how much was consumed.
4. Configure **thresholds** and **notifications**, and **enable/disable** gas sponsorship based on your policy.
5. Go to **Paymaster** and create your paymaster instance.
6. Copy the **API key** from **View details** and use it in your paymaster client integration.
### Setting Up Your Paymaster Integration
Let's begin with the fundamental imports and understand how paymaster clients fit into the broader account abstraction ecosystem:
```javascript
import { http } from 'viem'
import {
createBundlerClient,
createPaymasterClient
} from 'viem/account-abstraction'
import { sepolia } from 'viem/chains'
```
Notice that we're importing both bundler and paymaster client creation functions. This reflects the collaborative nature of these services—while they serve different purposes, they work together to enable sponsored transactions. The separation allows you to use different service providers for bundling and payment sponsorship, giving you flexibility in choosing the best services for each function.
Let's create a basic paymaster client and understand each component:
```javascript
// Create the paymaster client with minimal configuration
const paymasterClient = createPaymasterClient({
transport: http(
'https://paymaster.abstraxn.com/api/v2/11155111/?apikey={API_KEY}',
) // Paymaster v2 endpoint
})
// Integrate the paymaster with a bundler client for complete functionality
const bundlerClient = createBundlerClient({
chain: sepolia, // Specify the blockchain network for proper operation
paymaster: paymasterClient, // Delegate sponsorship decisions to the paymaster client
transport: http('https://bundler.abstraxn.com/v1/sepolia') // Separate endpoint for bundler operations
})
```
This configuration demonstrates the typical integration pattern where paymaster and bundler services work together but maintain separate responsibilities. The bundler client receives the paymaster client as a parameter, allowing it to consult the paymaster during user operation preparation and submission.
The separation of transports here is intentional and important. Paymaster services often have different performance characteristics, rate limits, and availability patterns compared to bundler services. By using separate transports, you can optimize each connection independently and implement different retry strategies or fallback mechanisms for each service type.
### Paymaster v2: Required `paymasterContext` fields (ERC20/token mode)
Paymaster v2 supports two sponsorship modes:
* `mode: "sponsor"`: native gas sponsorship (no ERC20 fee payment).
* `mode: "token"`: ERC20 fee payment (paymaster charges fees in your chosen token).
When using `mode: "token"`, `tokenAddress` is required in `paymasterContext`.
Other token policy fields are configured in dashboard and enforced by validation-service.
* `tokenAddress`: the ERC20 token contract address to use for fee payment.
* `treasuryWalletAddress`: configured in paymaster settings (dashboard).
* `constantFee`: configured in paymaster settings (dashboard, range `0..10`).
`supportedTokens` is optional and can be used to pass an allowlist priority (validation happens server-side).
### ERC20 `approve(...)` (what it means and why you must do it)
In `mode: "token"`, the paymaster will deduct ERC20 fees during the same user operation (post-op). That deduction requires the sender (your smart account) to have an ERC20 allowance for the paymaster address.
So your `UserOperation.calls` must include an ERC20 approval *before* the real action call. This is exactly what your example does:
* you encode `erc20.approve(PAYMASTER_ADDRESS, amount)`
* you include that call in `calls` along with the actual transaction
Common practice is approving a large allowance (for example `maxUint256`) once per token, or approving an amount that covers your expected `constantFee`/fee budget.
Example (include `approve` in the same `UserOperation`):
```ts
const approveData = encodeFunctionData({
abi: erc20Abi,
functionName: "approve",
args: [PAYMASTER_ADDRESS, maxUint256],
})
calls: [
{ to: tokenAddress, value: 0n, data: approveData }, // ERC20 approve
{ to: target, value: 0n, data: "0x" }, // your real call(s)
]
paymasterContext: {
mode: "token",
tokenAddress,
// treasury/constantFee are taken from paymaster config
}
```
### Configuration Parameters and Their Strategic Impact
Understanding each configuration parameter helps you optimize your paymaster integration for specific use cases and operational requirements. Let's explore how each parameter affects your application's behavior and performance.
#### Client Identification and Management
The identification parameters might seem purely administrative, but they serve important operational purposes:
```javascript
const paymasterClient = createPaymasterClient({
key: 'abstraxn-premium-paymaster', // Used for internal client management and caching
name: 'Abstraxn Premium User Paymaster', // Human-readable identifier for logging and monitoring
transport: http('https://paymaster.abstraxn.com/api/v2/1/?apikey={API_KEY}')
})
```
The `key` parameter affects how Viem manages client instances internally, particularly for caching and connection pooling. When you create multiple paymaster clients in your application, unique keys ensure that each client maintains its own connection state and cache. This becomes particularly valuable when you're working with multiple paymaster services or different configurations for different user tiers.
The `name` parameter appears in logs, error messages, and debugging output. Choosing descriptive names becomes crucial when troubleshooting issues in production environments. Consider including information about the paymaster's purpose or target audience in the name to make operational tasks easier.
#### Performance Tuning with Polling Intervals
The polling interval configuration affects how responsive your application feels to users while balancing resource consumption:
```javascript
const paymasterClient = createPaymasterClient({
pollingInterval: 2000, // Check sponsorship status every 2 seconds instead of default 4 seconds
transport: http('https://paymaster.abstraxn.com/api/v2/1/?apikey={API_KEY}')
})
```
This setting becomes particularly important when your application needs to provide real-time feedback about sponsorship availability or when tracking the status of sponsored operations. Shorter intervals provide faster feedback but increase API usage and can strain both your application and the paymaster service.
Consider your application's user experience requirements when setting this value. Applications that display real-time sponsorship availability might benefit from shorter intervals, while batch processing applications might prefer longer intervals to reduce resource consumption. Remember that paymaster services often have rate limits, so aggressive polling can lead to request throttling.
#### Extended Functionality with Custom RPC Schemas
Many paymaster services provide additional functionality beyond the standard ERC-7677 specification. Custom RPC schemas allow you to access these features while maintaining type safety:
```javascript
import { rpcSchema } from 'viem'
// Define additional methods that your paymaster service supports
type EnhancedPaymasterSchema = [
{
Method: 'abstraxn_getSponsorshipQuota'
Parameters: [string] // User address
ReturnType: { remainingQuota: bigint, resetTime: number }
},
{
Method: 'abstraxn_estimateSponsorshipCost'
Parameters: [object] // User operation preview
ReturnType: { estimatedCost: bigint, confidence: number }
}
]
const paymasterClient = createPaymasterClient({
rpcSchema: rpcSchema(),
transport: http('https://paymaster.abstraxn.com/api/v2/1/?apikey={API_KEY}')
})
// Now you can use extended functionality with full TypeScript support
const quota = await paymasterClient.request({
method: 'abstraxn_getSponsorshipQuota',
params: ['0x...'] // User address
})
```
This approach enables you to leverage provider-specific features such as quota management, cost estimation, or analytics while maintaining the benefits of TypeScript's type system. When working with paymaster services that offer sophisticated sponsorship logic, these extended methods often provide the visibility and control you need to implement complex user experience patterns.
### Transport Configuration and Resilience Strategies
The transport layer represents a critical component of paymaster integration because sponsorship decisions often happen in real-time during user interactions. Understanding transport configuration helps you build applications that remain responsive even when paymaster services experience issues:
```javascript
const paymasterClient = createPaymasterClient({
transport: http('https://paymaster.abstraxn.com/api/v2/1/?apikey={API_KEY}', {
timeout: 15000, // 15 second timeout for sponsorship decisions
retryCount: 2, // Retry failed sponsorship requests up to 2 times
retryDelay: 500, // Wait 500ms between retries for quick recovery
})
})
```
These transport settings require careful consideration of the user experience implications. Sponsorship decisions often happen during user interface interactions where delays become immediately noticeable. The timeout setting should balance thoroughness with responsiveness—too short, and legitimate requests might fail during network congestion; too long, and users experience frustrating delays.
The retry configuration becomes particularly important for paymaster services because sponsorship decisions can fail for transient reasons such as temporary quota exhaustion or service overload. However, aggressive retry policies can exacerbate problems during service outages, so consider implementing exponential backoff or circuit breaker patterns for production deployments.
### Integration Patterns and Best Practices
Successful paymaster integration requires understanding common patterns and potential pitfalls that emerge when building production applications.
#### Graceful Degradation Strategies
Consider how your application behaves when paymaster services become unavailable:
```javascript
// Example of a resilient paymaster integration with fallback behavior
const createResilientPaymasterClient = () => {
try {
return createPaymasterClient({
name: 'Primary Paymaster',
transport: http('https://paymaster.abstraxn.com/api/v2/1/?apikey={API_KEY}', {
timeout: 10000 // Shorter timeout for faster fallback detection
})
})
} catch (error) {
console.warn('Primary paymaster unavailable, falling back to self-pay mode')
return null // Indicates that users should pay their own gas fees
}
}
const paymasterClient = createResilientPaymasterClient()
const bundlerClient = createBundlerClient({
chain: mainnet,
paymaster: paymasterClient, // Will be null if paymaster is unavailable
transport: http('https://bundler.abstraxn.com/v1/mainnet')
})
```
This pattern ensures that your application remains functional even when paymaster services experience outages. Users might need to pay their own gas fees during service interruptions, but they can continue using your application rather than encountering complete failures.
#### Testing and Development Workflows
Developing applications with paymaster integration requires careful attention to testing strategies that account for the various states and failure modes your application might encounter:
```javascript
// Example development configuration with enhanced observability
const developmentPaymasterClient = createPaymasterClient({
name: 'Development Paymaster Client', // Clear identification in logs
pollingInterval: 5000, // Longer polling to reduce API usage during development
transport: http('https://paymaster.abstraxn.com/api/v2/11155111/?apikey={API_KEY}', {
timeout: 30000, // Extended timeout for development environments
// Additional logging can be configured here for debugging
})
})
```
Development configurations should prioritize visibility and debugging capability over performance optimization. Extended timeouts reduce the likelihood of spurious failures during development, while descriptive naming helps distinguish between different client instances in logs and debugging output.
### Understanding Service Integration Dynamics
The relationship between bundler clients and paymaster clients creates interesting operational dynamics that become important when scaling your application.
When you configure a bundler client with a paymaster client, the bundler automatically consults the paymaster during user operation preparation. This consultation involves multiple round trips—first to get sponsorship eligibility and gas estimates, then to get final sponsorship data with signatures. Understanding this flow helps you optimize performance and implement appropriate error handling.
The paymaster consultation process can add significant latency to user operation submission, particularly when paymaster services need to perform complex eligibility checks or when network conditions cause delays. Consider implementing user interface patterns that communicate this processing time to users, such as progress indicators or estimated completion times.
Production deployments often benefit from monitoring the relationship between bundler and paymaster performance. High paymaster response times can create bottlenecks in user operation submission, while paymaster service outages can cause user experience degradation even when bundler services remain healthy. Implementing separate health checks and monitoring for each service type helps you diagnose issues more effectively.
The Paymaster Client provides the foundation for implementing sophisticated sponsorship strategies in your EIP-7702 applications, but successful deployment requires careful attention to the operational and user experience considerations that emerge when these services interact in production environments. By understanding both the technical integration patterns and the broader service dynamics, you can build applications that provide reliable sponsored transaction experiences even under challenging conditions.
## Dashboard Setup
Follow this flow before code integration.
### 1) Configure Gas Sponsorship
1. Open Dashboard -> **Gas sponsorship**.
2. Add gas budget using:
* Crypto, or
* Fiat
3. Your deposit is converted and tracked as **USD gas credit** in the dashboard.
### 2) Review History
Check both:
* **Deposit history**
* **Gas usage history**
This helps you track top-ups and sponsorship consumption.
### 3) Configure Controls
Set and manage:
* low balance thresholds
* notification settings
* sponsorship enable/disable
### 4) Create Paymaster and get API Key
1. Go to **Paymaster** in the dashboard.
2. Create a Paymaster for your app and fill these settings:
* **Paymaster Name**: a readable name for your team (for example, `Mainnet Sponsor`).
* **Token Mode Enabled** (`isERC20`): turn this on only if you want ERC20 fee collection mode.
* **Sponsorship Enabled** (`isSponsored`): controls whether sponsor mode is allowed for this paymaster.
* **Treasury Wallet Address** (`treasuryWalletAddress`): the wallet that receives token compensation in token mode.
* **Constant Fee** (`constantFee`, range `0` to `10`): extra token fee charged per operation in token mode.
* **Paymaster Validity (Minutes)** (`validUntilMinutes`): how long generated paymaster data remains valid.
* **Wallet Restriction Type** (`restrictionType`):
* `NONE`: no wallet restriction
* `ALLOWLIST`: only listed wallets are allowed
* `BLOCKLIST`: listed wallets are blocked
* **Restricted Wallets** (`restrictedWallets`): wallet list used when allowlist/blocklist mode is selected.
3. Important conditional rule:
* If **Token Mode Enabled** is ON (`isERC20=true`), **Treasury Wallet Address** and **Constant Fee** are mandatory.
4. Open **View details**.
5. Copy the **Paymaster API key**.
Use this API key in your `createPaymasterClient(...)` configuration.
### Credit and settlement behavior
* In both `mode: "sponsor"` and `mode: "token"`, paymaster usage deducts from your **USD gas credit**.
* In `mode: "token"`, token charges are sent to the configured treasury wallet as compensation.
* `constantFee` is an additional token fee amount charged from the user per userOp/transaction.
* Validation-service enforces paymaster policy (`isSponsored`, `isERC20`, restrictions) and paymaster-v2 consumes that validated config.
## Paymaster Address and Supported Chains
Use this page to answer two questions quickly:
1. Which Paymaster address should I use?
2. Is my target chain supported?
### 1) Paymaster contract address
Use this address as the Paymaster contract in your integration:
* `0x9B7Fbf505863Af3a4a0103430A89Ae46Df28DDE0`
### 2) Supported chains
#### Mainnet chains
* `1` - Ethereum
* `8453` - Base
* `56` - BNB Smart Chain
* `137` - Polygon
* `1329` - SEI
* `6163` - Blockmaze
#### Testnet chains
* `11155111` - Ethereum Sepolia
* `84532` - Base Sepolia
* `97` - BSC Testnet
* `80002` - Polygon Amoy
* `1328` - SEI Testnet
### 3) Which mode can I use?
* **Mainnet:** both `mode: "sponsor"` and `mode: "token"` are available.
* **Testnet:** use only `mode: "sponsor"` (token mode is not enabled).
### 4) Before you continue
After confirming your chain here:
1. Go to **Supported tokens** (if you plan to use `mode: "token"`).
2. Complete **Dashboard setup**.
3. Follow **End-to-end integration**.
## Integration
This page covers only implementation steps.
Use the other pages in this section for:
* Paymaster address and supported chains
* Supported tokens
* Dashboard setup
### How billing works
* For both `mode: "sponsor"` and `mode: "token"`, usage deducts from the developer's **USD gas credit**.
* In `mode: "token"`, token deductions are transferred to the configured paymaster treasury wallet as compensation.
* `constantFee` is an extra token fee charged from the end user per transaction/userOp.
### Dashboard-managed paymaster config (new flow)
Paymaster v2 now reads token-mode policy from your paymaster config (validated by validation-service), not from per-request context fields.
Configure these in dashboard create/update paymaster:
* `isERC20`
* `isSponsored`
* `treasuryWalletAddress` (required when `isERC20=true`)
* `constantFee` (required when `isERC20=true`, `0..10`)
* `validUntilMinutes`
* `restrictionType`: `NONE | ALLOWLIST | BLOCKLIST`
* `restrictedWallets` (used for allowlist/blocklist checks)
### Step 1: Create a Paymaster client (v2)
Use the API key you copied from the dashboard.
```ts
import { http } from "viem";
import { createPaymasterClient } from "viem/account-abstraction";
const CHAIN_ID = 80002; // example: base-amoy / your chain id
const API_KEY = "";
const paymasterClient = createPaymasterClient({
transport: http(
`https://paymaster.abstraxn.com/api/v2/${CHAIN_ID}/?apikey=${API_KEY}`,
),
});
```
### Step 2: Call `sendUserOperation` (paymaster + paymasterContext)
`sendUserOperation` is where you pass:
* `paymaster: paymasterClient`
* `paymasterContext` (controls how v2 pays fees)
```ts
const hash = await bundlerClient.sendUserOperation({
account: kernelAccount, // your Smart Account
calls: [
// your call(s) live here
],
paymaster: paymasterClient,
paymasterContext: {
// mode goes here (sponsor or token)
},
});
```
### Step 3: Sponsor mode (native gas sponsorship)
Use `mode: "sponsor"` when you want the paymaster to sponsor gas using the native gas flow.
```ts
paymasterContext: {
mode: "sponsor",
},
```
### Step 4: ERC20 (token) mode
Use `mode: "token"` when you want fees paid in an ERC20 token.
#### Required `paymasterContext` fields
For `mode: "token"`, you must provide:
* `tokenAddress`: ERC20 contract address used for fee payment
`treasury` and `constantFee` are sourced from paymaster config in the dashboard (via validation-service). Do not send these in request context.
#### `paymasterContext` field definitions (focused)
##### `mode`
Defines how gas is paid:
* `"sponsor"`: native gas sponsorship by paymaster
* `"token"`: ERC20 token is deducted as fee payment
```ts
paymasterContext: {
mode: "sponsor", // or "token"
}
```
##### `tokenAddress` (required in `mode: "token"`)
The ERC-20 token address to use for payment.
```ts
paymasterContext: {
mode: "token",
tokenAddress: "0xTOKEN_ADDRESS",
}
```
##### `treasury` (dashboard-configured)
The receiver wallet for deducted ERC20 fees (compensation wallet).
```ts
paymasterContext: {
mode: "token",
// treasury is read from dashboard paymaster configuration
}
```
##### `constantFee` (dashboard-configured)
Additional token amount charged per transaction / userOp.
```ts
paymasterContext: {
mode: "token",
// constantFee is read from dashboard paymaster configuration
}
```
#### Example `paymasterContext` (token mode)
```ts
paymasterContext: {
mode: "token",
tokenAddress: TOKEN_ADDRESS,
// treasury and constantFee are read from paymaster configuration
},
```
#### ERC20 approval: what you must do
In ERC20 token mode, the paymaster deducts fees *during the same UserOperation* (post-op). That deduction requires the sender to have ERC20 allowance for the paymaster contract.
So you must add an `approve(...)` call into `calls` **before** your real action call.
Minimal pattern:
```ts
import { encodeFunctionData, erc20Abi, maxUint256 } from "viem";
const approveData = encodeFunctionData({
abi: erc20Abi,
functionName: "approve",
args: [PAYMASTER_ADDRESS, maxUint256], // or approve the exact needed amount
});
const hash = await bundlerClient.sendUserOperation({
account: kernelAccount,
calls: [
// 1) give allowance to paymaster
{ to: TOKEN_ADDRESS, value: 0n, data: approveData },
// 2) your real action call(s)
{ to: TARGET_CONTRACT, value: 0n, data: TARGET_CALLDATA },
],
paymaster: paymasterClient,
paymasterContext: {
mode: "token",
tokenAddress: TOKEN_ADDRESS,
// treasury and constantFee are fetched from paymaster configuration
},
});
```
#### Notes you must know
1. `PAYMASTER_ADDRESS` must be the Paymaster contract address used by your backend for the chain you are using.
2. If token-mode configuration is missing in dashboard (`isERC20`, `treasuryWalletAddress`, `constantFee`), v2 will reject the request.
## Supported Tokens
Use these token addresses in `paymasterContext.tokenAddress` when `mode: "token"`.
:::warning
Token mode is enabled only on mainnet chains.
:::
### Ethereum (`1`)
* **USDT**: `0xdAC17F958D2ee523a2206206994597C13D831ec7`
* **USDC**: `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48`
### BNB Smart Chain (`56`)
* **USDC**: `0x8965349fb649A33a30cbFDa057D8eC2C48AbE2A2`
### Polygon (`137`)
* **USDC**: `0x3c499c542cef5e3811e1192ce70d8cc03d5c3359`
* **USDT0**: `0xc2132D05D31c914a87C6611C10748AEb04B58e8F`
* **USDC.e**: `0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174`
### Base (`8453`)
* **USDC**: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`
### SEI (`1329`)
* **USDC**: `0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392`
### Not enabled for token mode yet
* `1328` (SEI Testnet)
* `6163` (Blockmaze)
* `11155111` (Ethereum Sepolia)
* `84532` (Base Sepolia)
* `97` (BSC Testnet)
* `80002` (Polygon Amoy)
## EIP-7702: Native Account Abstraction Made Simple
#### What is EIP-7702?
EIP-7702 empowers **Externally Owned Accounts (EOAs)** to temporarily delegate their execution logic to a smart contract—**without changing the address** or moving funds. This unlocks powerful features like **batching**, **gas sponsorship**, and **custom authorizations**—all within a native Ethereum transaction structure.
### EIP-4337 vs. EIP-7702: Know Your Layers
| Feature | **EIP-4337** | **EIP-7702** |
| ------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| Protocol Dependency | Application-layer via Bundlers & Paymasters; no consensus change required | Consensus-layer: needs Pectra hard fork; new transaction type (Type 4) |
| Account Model | Separate Smart Contract wallets | Original EOA delegates to smart logic at the same address |
| Operation Flow | UserOps → Bundler → EntryPoint | Native transaction with `authorization_list` for code delegation |
| Best Use Case | Gas sponsorship, social recovery, complex validation | Atomic batching, native UX; low overhead |
| Availability | Live today with robust tooling | Available post-Pectra upgrade; lightweight, atomic execution |
### Getting Started With Abstraxn
We give you everything you need to integrate EIP-7702 using Abstraxn SDK tailored for your platform.
#### 1. Get Your API Key
* Visit [dashboard.abstraxn.com](https://dashboard.abstraxn.com/), sign in or sign up
* Navigate to **Apps** → **Create New App** (enter name, chain, description)
* Inside your app, go to the **Bundler / Paymaster** section → click **Add** → assign name
* Click **View Details** → copy your **API Key**
#### 2. Install Dependencies
```bash
npm install @abstraxn/vcs-sdk
```
#### 3. EIP-7702 Integration
```ts
import {
createViemClient,
signAuthorization,
sendEip7702Transaction
} from "@abstraxn/vcs-sdk";
const client = createViemClient({
chain: 'sepolia',
rpcUrl: "https://rpc.sepolia.example"
});
async function activateEip7702(eoaPrivateKey: string, implAddress: string) {
const auth = await signAuthorization({
client,
ownerPrivateKey: eoaPrivateKey,
delegateTo: implAddress
});
const txHash = await sendEip7702Transaction({
client,
authorizationList: [{ chainId: client.chainId, address: implAddress, signature: auth }]
});
console.log("EIP-7702 activation tx hash:", txHash);
}
```
#### 4. Using Abstraxn’s Bundler/Paymaster for Sponsored Actions
Once EIP-7702 is active on a user’s account, you can send batch or sponsored RPCs seamlessly through our bundler:
```ts
import {
abxBundlerClient,
buildBatch,
sendUserOp
} from "@abstraxn/vcs-sdk";
const bundler = abxBundlerClient({
apiKey: process.env.ABX_API_KEY,
chain: 'sepolia',
bundlerUrl: "https://api.abstraxn.com/v1/bundler/sepolia"
});
async function sendBatchOperation(account, calls) {
const userOp = buildBatch({ account, calls });
const opHash = await sendUserOp(bundler, userOp);
console.log("UserOp submitted:", opHash);
}
```
### Summary
1. EIP-7702 brings native account abstraction, unlocking smart account features on an EOA.
2. EIP-4337 provides rich infrastructure today; EIP-7702 makes it lower overhead once your user is already using it.
3. To integrate Abstraxn:
* Obtain your API Key by creating an app and bundler/paymaster
* Use our SDK to sign authorizations and submit Type-4 transactions
* Then, interact with smart flows using our bundler client
## EIP-7702 Overview
EIP-7702 introduces a revolutionary transaction type (Type 0x04) that enables your account, an EOA, to act like a smart contract by assigning a **delegation designator**. This designator embeds `0xef0100 || contractAddress` in your code slot, redirecting execution to that contract whenever your EOA is invoked. It unlocks powerful capabilities like batching (Bundling), gas sponsorship (Relaying), multi-sig, passkeys, and more, all while keeping your familiar address and key.
***
#### Why It Matters
* **Upgrades without migration**: Your users keep their existing EOA. No address change or fund transfers needed.
* **Smart account features**: batch transactions, sponsored gas, custom auth, and passkey support become native.
* **Designed for coexistence**: with ERC-4337’s abstract architecture; they complement each other perfectly.
***
#### EIP-4337 vs EIP-7702 at a Glance
| Feature | **EIP-4337** | **EIP-7702** |
| ------------------- | --------------------------------------------- | ------------------------------------------------------ |
| Smart account model | External smart wallet using EntryPoint | EOA delegates to smart contract via designator |
| Migration required | Yes, EOAs must become SCAs | No, EOAs remain same address |
| Infrastructure | Off-chain bundlers/EntryPoint/Paymaster | On-chain native execution once supported by wallets |
| Best use case | Rich palettes today (multi-sig, custom flows) | Lean, backward-compatible UX enhancement, low friction |
***
#### Security & UX Considerations
* **Trust required**: Delegation gives execution power over your EOA. Only delegate to vetted, trusted contracts.
* **Control remains**: Your private key still governs the account—preserving flexibility but requiring strong custody.
* **Wallet adoption is key**: Until wallets support 7702, none of this matters in production environments.
***
#### TL;DR
EIP-7702 is Ethereum’s first on-chain account abstraction step—letting EOAs gain smart account powers **without migration**. Post-Pectra 2025, EOAs can delegate behavior securely and with seamless UX. When paired with ERC-4337 and Abstraxn, this unlocks the next frontier of conversational, developer-friendly Web3.
Let me know when you're ready to walk through integration steps, demos, or deeper technical breakdowns!
## EIP-7702 Quickstart — Send a Delegated Transaction from Your EOA
This quickstart demonstrates how to use **EIP-7702 with ERC-4337** to send a gas-sponsored, batched transaction directly from your EOA. We’ll use Abstraxn’s SDK for EIP-7702-compliant smart account operations. Feel free to substitute with other compatible EIP-7702 implementations if desired.
For the conceptual overview, jump back to the [EIP-7702 Overview](./Overview). For full protocol details, see the official EIP-7702 proposal.
***
### Installation
Make sure you've installed our EIP-7702 SDK, which follows the `viem` + `permissionless.js` style but built for Abstraxn’s stack:
For NPM users:
```bash
npm install permissionless viem
```
For Yarn users:
```bash
npm install permissionless viem
```
### Quickstart steps
1. Setup the smart account from your EOA
```ts
import { createPublicClient, Hex, http } from "viem";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { to7702KernelSmartAccount, to7702SimpleSmartAccount } from "permissionless/accounts";
import { sepolia } from "viem/chains";
// This is your EOA's private key
const privateKey = generatePrivateKey();
const eoa7702 = privateKeyToAccount(privateKey);
const client = createPublicClient({
chain: sepolia,
transport: http("https://sepolia.drpc.org"),
});
// you can use either simple smart account
const simple7702Account = await to7702SimpleSmartAccount({
client,
owner: eoa7702,
});
// or use a kernel smart account
const kernel7702Account = await to7702KernelSmartAccount({
client,
owner: eoa7702,
})
```
2. (Optional) Add a Paymaster for Gas Sponsorship
```ts
import {
createPaymasterClient
} from 'viem/account-abstraction'
const APIKey = "https://paymaster.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}";
export const paymasterClient = createPaymasterClient({
transport: http('https://paymaster.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}'),
})
```
3. Build a smart account client
```ts
import { createSmartAccountClient } from "permissionless";
import { zeroAddress } from "viem";
const smartAccountClient = createSmartAccountClient({
client,
chain: sepolia,
account: simple7702Account, // or kernel7702Account
paymaster: paymasterClient,
bundlerTransport: http(
`https://paymaster.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}`,
),
});
```
4. Send a transaction
```ts
const isSmartAccountDeployed = await smartAccountClient.account.isDeployed();
const kernelVersion = KERNEL_V3_3;
const kernelAddresses = KernelVersionToAddressesMap[kernelVersion];
let transactionHash: Hex;
// We only have to add the authorization field if the EOA does not have the authorization code set
if (!isSmartAccountDeployed) {
transactionHash = await smartAccountClient.sendTransaction({
to: zeroAddress,
value: 0n,
data: "0x",
authorization: await eoa7702.signAuthorization({
address: kernelAddresses.accountImplementationAddress,
chainId: sepolia.id,
nonce: await client.getTransactionCount({
address: eoa7702.address,
}),
}),
});
} else {
transactionHash = await smartAccountClient.sendTransaction({
to: zeroAddress,
value: 0n,
data: "0x",
});
}
```
5. Batch multile calls
```ts
let transactionHash_2: Hex;
const kernelVersion = KERNEL_V3_3;
const kernelAddresses = KernelVersionToAddressesMap[kernelVersion];
// We only have to add the authorization field if the EOA does not have the authorization code set
if (!isSmartAccountDeployed) {
transactionHash_2 = await smartAccountClient.sendTransaction({
calls: [
{
to: zeroAddress,
value: 0n,
data: "0x",
},
{
to: zeroAddress,
value: 0n,
data: "0x",
},
],
authorization: await eoa7702.signAuthorization({
address: kernelAddresses.accountImplementationAddress,
chainId: sepolia.id,
nonce: await client.getTransactionCount({
address: eoa7702.address,
}),
}),
});
} else {
transactionHash_2 = await smartAccountClient.sendTransaction({
calls: [
{
to: zeroAddress,
value: 0n,
data: "0x",
},
{
to: zeroAddress,
value: 0n,
data: "0x",
},
],
});
}
```
### Summary
You’ve just sent a delegated (EIP-7702) transaction from your EOA. When you inspect the transaction on-chain, you’ll see it's still from your EOA — but executed through the smart account delegation, often with a sponsored gas model.
### Ket Takeaways
* EIP-7702 lets EOAs gain smart account powers instantly, without migrations or new wallets.
* Authorization is required only during setup; repeated transactions don’t need re-authorization.
* Batch and gas sponsorship are native once the smart account is deployed.
* All powered by Abstraxn’s integrated SDK — clean, secure, and EOA-first.
## estimateUserOperationGas
Our gas estimation methodology provides comprehensive analysis and optimization for User Operation execution within the Abstraxn platform. This strategic approach ensures efficient resource allocation while maintaining optimal transaction performance.
### Implementation Strategy
After thorough evaluation of gas optimization approaches, our team has determined that precise estimation mechanisms offer the most compelling advantages for the Abstraxn ecosystem. Our analysis prioritized transaction efficiency, cost optimization, and seamless user experience.
:::code-group
```ts [example.ts]
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
const gas = await bundlerClient.estimateUserOperationGas({ // [!code focus:7]
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}]
})
```
```ts [config.ts] filename="config.ts"
import { createPublicClient, http } from 'viem'
import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
const client = createPublicClient({
chain: mainnet,
transport: http()
})
export const account = await toCoinbaseSmartAccount({
client,
owners: [privateKeyToAccount('0x...')],
version: '1.1',
})
export const bundlerClient = createBundlerClient({
client,
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}') // [!code focus]
})
```
:::
#### Strategic Account Management
Our deliberative process involved rigorous analysis of account management patterns. The hoisted account approach, while presenting alternative implementation strategies, demonstrates superior alignment with streamlined development workflows.
If you do not wish to pass an `account` to every `estimateUserOperationGas`, you can also hoist the Account on the Bundler Client (see `config.ts`).
:::code-group
```ts [example.ts]
import { parseEther } from 'viem'
import { bundlerClient } from './config'
const gas = await bundlerClient.estimateUserOperationGas({ // [!code focus:7]
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}]
})
```
```ts [config.ts]
import { createPublicClient, http } from 'viem'
import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
const client = createPublicClient({
chain: mainnet,
transport: http()
})
export const account = await toCoinbaseSmartAccount({
client,
owners: [privateKeyToAccount('0x...')],
version: '1.1',
})
export const bundlerClient = createBundlerClient({
account, // [!code ++]
client,
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
#### Advanced Contract Integration
Through comprehensive evaluation of multiple interaction methodologies, our research team has identified that structured contract calls provide the most robust foundation for complex application development within the Abstraxn ecosystem.
The `calls` property also accepts **Contract Calls**, and can be used via the `abi`, `functionName`, and `args` properties.
:::code-group
```ts [example.ts]
import { parseEther } from 'viem'
import { bundlerClient, publicClient } from './config'
import { wagmiAbi } from './abi' // [!code focus]
const gas = await bundlerClient.estimateUserOperationGas({ // [!code focus:7]
calls: [{
abi: wagmiAbi,
functionName: 'mint',
to: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
}],
})
```
```ts [abi.ts] filename="abi.ts"
export const wagmiAbi = [
// ...
{
inputs: [],
name: "mint",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
// ...
] as const;
```
```ts [config.ts]
import { createPublicClient, http } from 'viem'
import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
const client = createPublicClient({
chain: mainnet,
transport: http()
})
export const account = await toCoinbaseSmartAccount({
client,
owners: [privateKeyToAccount('0x...')],
version: '1.1',
})
export const bundlerClient = createBundlerClient({
account,
client,
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
### Response Architecture
The Abstraxn gas estimation framework returns a comprehensive analysis structure optimized for strategic decision-making:
```ts
{
callGasLimit: bigint;
preVerificationGas: bigint;
verificationGasLimit: bigint;
paymasterVerificationGasLimit: bigint | undefined;
paymasterPostOpGasLimit: bigint | undefined;
}
```
This response structure represents our commitment to providing detailed insights that enable informed optimization decisions across the development lifecycle.
### Configuration Parameters
Our technical architecture supports nuanced configuration approaches, allowing developers to fine-tune gas estimation according to specific application requirements and strategic objectives.
#### account
* **Type:** `SmartAccount`
The foundational account configuration for User Operation execution within the Abstraxn ecosystem.
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account, // [!code focus]
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}]
})
```
#### calls
* **Type:** `{ data: Hex, to: Address, value: bigint }[]`
Strategic operation definitions that constitute the core execution logic for User Operations.
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{ // [!code focus]
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code focus]
value: parseEther('1') // [!code focus]
}] // [!code focus]
})
```
:::tip
Alternative implementation strategies support direct call data specification through the `callData` property, providing flexibility for advanced integration scenarios:
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
callData: '0xdeadbeef', // [!code focus]
})
```
:::
#### callGasLimit (optional)
* **Type:** `bigint`
Precision resource allocation for primary execution logic, enabling strategic optimization of computational requirements.
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
callGasLimit: 69420n, // [!code focus]
})
```
#### factory (optional)
* **Type:** `Address`
Strategic account factory configuration for deployment scenarios within the Abstraxn infrastructure.
:::warning
This configuration parameter should be utilized exclusively during initial Smart Account deployment phases, ensuring optimal resource management.
:::
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
factory: '0x1234567890123456789012345678901234567890', // [!code focus]
factoryData: '0xdeadbeef',
})
```
#### factoryData (optional)
* **Type:** `Hex`
Structured deployment instructions for Smart Account initialization through factory contracts.
:::warning
This parameter maintains strategic relevance only during pre-deployment phases, supporting efficient account creation workflows.
:::
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
factory: '0x1234567890123456789012345678901234567890',
factoryData: '0xdeadbeef', // [!code focus]
})
```
#### maxFeePerGas (optional)
* **Type:** `bigint`
Strategic fee ceiling configuration for User Operation execution, enabling predictable cost management within the Abstraxn platform.
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
maxFeePerGas: 420n, // [!code focus]
})
```
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
Advanced priority fee optimization for enhanced transaction processing efficiency.
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
maxPriorityFeePerGas: 420n,
maxFeePerGas: 10n, // [!code focus]
})
```
#### nonce (optional)
* **Type:** `bigint`
Sequential operation identifier ensuring transaction ordering integrity within the Abstraxn ecosystem.
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
nonce: 10n, // [!code focus]
})
```
#### paymaster (optional)
* **Type:** `Address | true | PaymasterClient | PaymasterActions`
Comprehensive sponsorship configuration framework supporting multiple integration strategies:
* **Address Configuration**: Direct paymaster contract integration for streamlined sponsorship
* **PaymasterClient Integration**: Advanced client-based sponsorship with enhanced functionality
* **Bundler Integration**: Unified bundler-paymaster architecture for simplified implementation
* **Custom Function Support**: Flexible sponsorship logic through specialized function implementations
##### Strategic Paymaster Contract Integration
```ts
import { account, bundlerClient } from './config'
// ---cut---
const hash = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB', // [!code focus]
paymasterData: '0xdeadbeef',
})
```
##### Advanced Paymaster Client Architecture
```ts
import { account, bundlerClient } from './config'
// ---cut---
import { http, parseEther } from 'viem'
import { createPaymasterClient } from 'viem/account-abstraction'
const paymasterClient = createPaymasterClient({ // [!code focus]
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}') // [!code focus]
}) // [!code focus]
const hash = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: paymasterClient, // [!code focus]
})
```
##### Unified Bundler-Paymaster Approach
```ts
import { account, bundlerClient } from './config'
// ---cut---
const hash = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: true, // [!code focus]
})
```
#### paymasterContext (optional)
* **Type:** `unknown`
Specialized configuration parameters for advanced paymaster integration scenarios.
:::warning
This parameter maintains relevance exclusively within PaymasterClient-based architectures, supporting sophisticated sponsorship workflows.
:::
```ts
import { account, bundlerClient } from './config'
// ---cut---
import { http, parseEther } from 'viem'
import { createPaymasterClient } from 'viem/account-abstraction'
const paymasterClient = createPaymasterClient({
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
const hash = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: paymasterClient,
paymasterContext: { // [!code focus]
policyId: 'abc123' // [!code focus]
}, // [!code focus]
})
```
#### paymasterData (optional)
* **Type:** `Address`
Structured execution instructions for paymaster contract interactions.
:::warning
This configuration applies specifically to address-based paymaster implementations, enabling direct contract communication.
:::
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
paymasterData: '0xdeadbeef', // [!code focus]
})
```
#### paymasterPostOpGasLimit (optional)
* **Type:** `bigint`
Strategic resource allocation for post-operation paymaster logic execution.
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
paymasterData: '0xdeadbeef',
paymasterPostOpGasLimit: 69420n, // [!code focus]
})
```
#### paymasterVerificationGasLimit (optional)
* **Type:** `bigint`
Precision gas allocation for paymaster validation procedures within the Abstraxn framework.
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
paymasterData: '0xdeadbeef',
paymasterVerificationGasLimit: 69420n, // [!code focus]
})
```
#### preVerificationGas (optional)
* **Type:** `bigint`
Strategic bundler compensation configuration, ensuring sustainable operation within the Abstraxn ecosystem.
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
preVerificationGas: 69420n, // [!code focus]
})
```
#### signature (optional)
* **Type:** `Hex`
Authentication signature for User Operation validation, supporting secure transaction execution.
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
signature: '0x...', // [!code focus]
})
```
#### stateOverride (optional)
* **Type:** `StateOverride`
Advanced state manipulation framework enabling sophisticated testing and simulation scenarios within the Abstraxn platform.
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
stateOverride: [ // [!code focus]
{ // [!code focus]
address: '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC', // [!code focus]
balance: parseEther('1'), // [!code focus]
stateDiff: [ // [!code focus]
{ // [!code focus]
slot: '0x3ea2f1d0abf3fc66cf29eebb70cbd4e7fe762ef8a09bcc06c8edf641230afec0', // [!code focus]
value: '0x00000000000000000000000000000000000000000000000000000000000001a4', // [!code focus]
}, // [!code focus]
], // [!code focus]
} // [!code focus]
], // [!code focus]
})
```
#### verificationGasLimit (optional)
* **Type:** `bigint`
Strategic resource allocation for comprehensive verification procedures, ensuring robust security within User Operation execution.
```ts
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
// ---cut---
const gas = await bundlerClient.estimateUserOperationGas({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
verificationGasLimit: 69420n, // [!code focus]
})
```
## getChainId
Our chain identification methodology provides comprehensive network validation and strategic connectivity management for bundler operations within the Abstraxn platform. This foundational approach ensures seamless multi-chain compatibility while maintaining optimal transaction routing efficiency.
### Implementation Framework
After thorough evaluation of network identification approaches, our team has determined that direct chain ID retrieval offers the most compelling advantages for the Abstraxn ecosystem. Our analysis prioritized network reliability, cross-chain compatibility, and streamlined developer experience.
:::code-group
```ts [example.ts]
import { bundlerClient } from './client'
const chainId = await bundlerClient.getChainId() // [!code focus:99]
// @log: 1
```
```ts [client.ts] filename="client.ts"
import { http } from 'viem'
import { createBundlerClient } from 'viem/account-abstraction'
export const bundlerClient = createBundlerClient({
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
Key considerations in our decision-making process included:
* Minimizing network verification overhead
* Maintaining optimal bundler connectivity flow
* Ensuring long-term multi-chain adaptability
Our deliberative process involved rigorous analysis of alternative network identification strategies. While distributed chain detection mechanisms presented interesting perspectives, the direct retrieval approach demonstrated superior alignment with our strategic objectives regarding transaction efficiency and developer accessibility.
The research team provided nuanced insights, particularly highlighting the importance of consistent chain validation across diverse network environments. Our recommendation represents a collaborative synthesis of technical expertise and strategic vision, designed to drive meaningful reliability improvements in the Abstraxn ecosystem.
Should additional network requirements or architectural considerations emerge, we remain prepared to conduct further analysis and refine our chain identification strategy accordingly.
### Response Architecture
The Abstraxn chain identification framework returns precise network information optimized for strategic routing decisions in:
`number`
The current chain identifier, providing foundational network context for all subsequent bundler operations within the platform.
This response structure represents our commitment to transparent network management and supports informed decision-making across complex multi-chain deployment scenarios.
## getSupportedEntryPoints
Our EntryPoint discovery methodology provides strategic insight into bundler capabilities and compatibility matrices within the Abstraxn platform. This analytical approach ensures optimal contract interaction patterns while maintaining comprehensive support for diverse account abstraction implementations.
### Architectural Framework
After comprehensive evaluation of multiple EntryPoint validation approaches, our team has determined that direct bundler interrogation offers the most compelling advantages for the Abstraxn ecosystem. Our analysis prioritized compatibility verification, protocol adherence, and seamless integration pathways.
:::code-group
```ts [example.ts]
import { bundlerClient } from './client'
const entryPoints = await bundlerClient.getSupportedEntryPoints() // [!code focus:99]
// @log: ["0x0000000071727De22E5E9d8BAf0edAc6f37da032"]
```
```ts [client.ts] filename="client.ts"
import { http } from 'viem'
import { createBundlerClient } from 'viem/account-abstraction'
export const bundlerClient = createBundlerClient({
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
Key considerations in our decision-making process included:
* Minimizing protocol discovery overhead
* Maintaining optimal bundler compatibility verification
* Ensuring long-term account abstraction adaptability
Our deliberative process involved rigorous analysis of alternative EntryPoint discovery mechanisms. While distributed validation architectures presented interesting perspectives, the direct interrogation approach demonstrated superior alignment with our strategic objectives regarding transaction efficiency and developer accessibility.
The research team provided nuanced insights, particularly highlighting the importance of real-time compatibility assessment across evolving account abstraction standards. Our recommendation represents a collaborative synthesis of technical expertise and strategic vision, designed to drive meaningful reliability improvements in bundler integration workflows.
Should additional compatibility requirements or protocol considerations emerge, we remain prepared to conduct further analysis and refine our EntryPoint discovery strategy accordingly.
### Response Architecture
The Abstraxn EntryPoint discovery framework returns comprehensive compatibility information optimized for strategic integration decisions in:
`readonly Address[]`
The comprehensive array of EntryPoint addresses that the bundler actively supports, providing foundational protocol compatibility context for all account abstraction operations within the platform.
This response structure represents our commitment to transparent protocol management and supports informed decision-making across complex multi-version deployment scenarios. The read-only nature ensures immutable reference data while enabling strategic planning for account abstraction implementations.
## getUserOperation
Our User Operation intelligence methodology provides comprehensive transaction analysis and historical data retrieval within the Abstraxn platform. This analytical approach ensures detailed operational visibility while maintaining optimal performance for complex account abstraction workflows.
### Strategic Implementation
After comprehensive evaluation of multiple User Operation retrieval architectures, our team has determined that hash-based lookup mechanisms offer the most compelling advantages for the Abstraxn ecosystem. Our analysis prioritized data integrity, query performance, and comprehensive operational transparency.
:::code-group
```ts [example.ts]
import { bundlerClient } from './client'
const result = await bundlerClient.getUserOperation({ // [!code focus:99]
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d'
})
```
```ts [client.ts] filename="client.ts"
import { http } from 'viem'
import { createBundlerClient } from 'viem/account-abstraction'
import { mainnet } from 'viem/chains'
export const bundlerClient = createBundlerClient({
chain: mainnet,
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
Key considerations in our decision-making process included:
* Minimizing operational query overhead
* Maintaining optimal data retrieval flow
* Ensuring long-term historical data accessibility
Our deliberative process involved rigorous analysis of alternative User Operation discovery mechanisms. While distributed indexing architectures presented interesting perspectives, the direct hash-based approach demonstrated superior alignment with our strategic objectives regarding transaction transparency and developer accessibility.
The research team provided nuanced insights, particularly highlighting the importance of comprehensive operational context across diverse account abstraction implementations. Our recommendation represents a collaborative synthesis of technical expertise and strategic vision, designed to drive meaningful transparency improvements in transaction lifecycle management.
Should additional operational intelligence requirements or analytical considerations emerge, we remain prepared to conduct further analysis and refine our User Operation retrieval strategy accordingly.
### Response Architecture
The Abstraxn User Operation intelligence framework returns comprehensive transaction information optimized for strategic analysis and operational decision-making:
```ts
{
blockHash: Hash,
blockNumber: bigint,
entryPoint: Address,
transactionHash: Hash,
userOperation: UserOperation
}
```
This response structure represents our commitment to providing detailed operational insights that enable informed analysis across the complete transaction lifecycle within the platform.
### Configuration Parameters
Our technical architecture supports precision-based retrieval approaches, allowing developers to access specific User Operation intelligence according to strategic analytical requirements.
#### hash
* **Type:** `'0x${string}'`
The foundational transaction identifier enabling precise User Operation retrieval within the Abstraxn intelligence framework.
```ts
import { bundlerClient } from './client'
// ---cut---
const result = await publicClient.getUserOperation({
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d' // [!code focus]
})
```
## getUserOperationReceipt
Our User Operation receipt methodology provides comprehensive transaction verification and execution confirmation within the Abstraxn platform. This strategic approach ensures detailed operational accountability while maintaining optimal performance for complex account abstraction validation workflows.
### Architectural Framework
After comprehensive evaluation of multiple receipt verification architectures, our team has determined that hash-based receipt retrieval offers the most compelling advantages for the Abstraxn ecosystem. Our analysis prioritized transaction finality validation, execution transparency, and comprehensive audit trail accessibility.
:::code-group
```ts [example.ts]
import { bundlerClient } from './client'
const receipt = await bundlerClient.getUserOperationReceipt({ // [!code focus:99]
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d'
})
// @log: {
// @log: blockHash: '0xaf1dadb8a98f1282e8f7b42cc3da8847bfa2cf4e227b8220403ae642e1173088',
// @log: blockNumber: 15132008n,
// @log: sender: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
// @log: ...
// @log: status: 'success',
// @log: }
```
```ts [client.ts] filename="client.ts"
import { http } from 'viem'
import { createBundlerClient } from 'viem/account-abstraction'
import { mainnet } from 'viem/chains'
export const bundlerClient = createBundlerClient({
chain: mainnet,
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
Key considerations in our decision-making process included:
* Minimizing receipt verification overhead
* Maintaining optimal transaction confirmation flow
* Ensuring long-term execution audit accessibility
Our deliberative process involved rigorous analysis of alternative User Operation confirmation mechanisms. While distributed receipt validation architectures presented interesting perspectives, the direct hash-based approach demonstrated superior alignment with our strategic objectives regarding transaction finality and developer accessibility.
The research team provided nuanced insights, particularly highlighting the importance of comprehensive execution context across diverse account abstraction implementations. Our recommendation represents a collaborative synthesis of technical expertise and strategic vision, designed to drive meaningful transparency improvements in transaction verification workflows.
Should additional verification requirements or execution audit considerations emerge, we remain prepared to conduct further analysis and refine our User Operation receipt strategy accordingly.
### Response Architecture
The Abstraxn User Operation receipt framework returns comprehensive execution confirmation optimized for strategic verification and operational accountability:
`UserOperationReceipt`
The complete User Operation receipt containing detailed execution confirmation, transaction finality status, and comprehensive audit information within the platform.
This response structure represents our commitment to providing thorough execution verification that enables informed analysis across the complete transaction lifecycle and supports robust accountability frameworks.
### Configuration Parameters
Our technical architecture supports precision-based receipt retrieval approaches, allowing developers to access specific User Operation confirmation intelligence according to strategic verification requirements.
#### hash
* **Type:** `'0x${string}'`
The foundational transaction identifier enabling precise User Operation receipt retrieval within the Abstraxn verification framework.
```ts
import { bundlerClient } from './client'
// ---cut---
const receipt = await bundlerClient.getUserOperationReceipt({
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d' // [!code focus]
})
```
## prepareUserOperation
Our User Operation preparation methodology provides strategic transaction assembly and comprehensive property optimization within the Abstraxn platform. This analytical approach ensures complete operational readiness while maintaining optimal performance for complex account abstraction execution workflows.
### Strategic Implementation Framework
After comprehensive evaluation of multiple User Operation preparation architectures, our team has determined that integrated property completion offers the most compelling advantages for the Abstraxn ecosystem. Our analysis prioritized execution readiness, parameter optimization, and seamless developer experience.
:::code-group
```ts [example.ts]
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
const userOperation = await bundlerClient.prepareUserOperation({ // [!code focus:7]
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}]
})
```
```ts [config.ts] filename="config.ts"
import { createPublicClient, http } from 'viem'
import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
const client = createPublicClient({
chain: mainnet,
transport: http()
})
export const account = await toCoinbaseSmartAccount({
client,
owners: [privateKeyToAccount('0x...')],
version: '1.1',
})
export const bundlerClient = createBundlerClient({
client,
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
Key considerations in our decision-making process included:
* Minimizing preparation overhead while maximizing operational completeness
* Maintaining optimal transaction assembly flow
* Ensuring long-term execution parameter adaptability
Our deliberative process involved rigorous analysis of alternative User Operation preparation mechanisms. While distributed assembly architectures presented interesting perspectives, the integrated preparation approach demonstrated superior alignment with our strategic objectives regarding transaction efficiency and developer accessibility.
#### Strategic Account Management
Our comprehensive evaluation revealed that account hoisting strategies provide superior workflow optimization for developers requiring consistent operational patterns. The research team provided nuanced insights, particularly highlighting the importance of flexible account management across diverse implementation scenarios.
Should you prefer to avoid passing an `account` parameter to every `prepareUserOperation` invocation, our framework supports Account hoisting on the Bundler Client configuration (see `config.ts`).
:::code-group
```ts [example.ts]
import { parseEther } from 'viem'
import { bundlerClient } from './config'
const userOperation = await bundlerClient.prepareUserOperation({ // [!code focus:7]
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
})
```
```ts [config.ts] filename="config.ts"
import { createPublicClient, http } from 'viem'
import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
const client = createPublicClient({
chain: mainnet,
transport: http()
})
export const account = await toCoinbaseSmartAccount({
client,
owners: [privateKeyToAccount('0x...')],
version: '1.1',
})
export const bundlerClient = createBundlerClient({
account, // [!code ++]
client,
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
#### Advanced Contract Integration Framework
Through comprehensive evaluation of multiple interaction methodologies, our research team has identified that structured contract calls provide the most robust foundation for complex application development within the Abstraxn ecosystem.
The `calls` property supports **Contract Calls** through `abi`, `functionName`, and `args` properties, enabling sophisticated smart contract interactions.
:::code-group
```ts [example.ts]
import { parseEther } from 'viem'
import { bundlerClient, publicClient } from './config'
import { wagmiAbi } from './abi' // [!code focus]
const userOperation = await bundlerClient.prepareUserOperation({ // [!code focus:7]
calls: [{
abi: wagmiAbi,
functionName: 'mint',
to: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
}],
})
```
```ts [abi.ts] filename="abi.ts"
export const wagmiAbi = [
// ...
{
inputs: [],
name: "mint",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
// ...
] as const;
```
```ts [config.ts] filename="config.ts"
import { createPublicClient, http } from 'viem'
import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
const client = createPublicClient({
chain: mainnet,
transport: http()
})
export const account = await toCoinbaseSmartAccount({
client,
owners: [privateKeyToAccount('0x...')],
version: '1.1',
})
export const bundlerClient = createBundlerClient({
account,
client,
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
Our collaborative approach ensures that developers can leverage sophisticated contract interactions while maintaining transparency in preparation workflows. This methodology supports long-term platform adaptability and ecosystem scalability.
### Response Architecture
The Abstraxn User Operation preparation framework returns comprehensive operational structure optimized for strategic execution:
`UserOperation`
The fully prepared User Operation containing all necessary execution parameters and optimized configurations within the platform.
This response structure represents our commitment to providing complete operational readiness that enables seamless transaction execution across the entire account abstraction lifecycle.
### Configuration Parameters
Our technical architecture supports nuanced preparation approaches, allowing developers to fine-tune User Operation assembly according to specific application requirements and strategic execution objectives.
#### account
* **Type:** `SmartAccount`
The foundational account configuration for User Operation preparation within the Abstraxn execution framework.
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account, // [!code focus]
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}]
})
```
#### calls
* **Type:** `{ data: Hex, to: Address, value: bigint }[]`
Strategic operation definitions that constitute the core execution logic for User Operation assembly.
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{ // [!code focus]
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code focus]
value: parseEther('1') // [!code focus]
}] // [!code focus]
})
```
:::tip
Alternative implementation strategies support direct call data specification through the `callData` property, providing flexibility for advanced integration scenarios:
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
callData: '0xdeadbeef', // [!code focus]
})
```
:::
#### callGasLimit (optional)
* **Type:** `bigint`
Precision resource allocation for primary execution logic, enabling strategic optimization of computational requirements during preparation.
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
callGasLimit: 69420n, // [!code focus]
})
```
#### factory (optional)
* **Type:** `Address`
Strategic account factory configuration for deployment scenarios within the Abstraxn infrastructure.
:::warning
This configuration parameter should be utilized exclusively during initial Smart Account deployment phases, ensuring optimal resource management.
:::
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
factory: '0x1234567890123456789012345678901234567890', // [!code focus]
factoryData: '0xdeadbeef',
})
```
#### factoryData (optional)
* **Type:** `Hex`
Structured deployment instructions for Smart Account initialization through factory contracts.
:::warning
This parameter maintains strategic relevance only during pre-deployment phases, supporting efficient account creation workflows.
:::
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
factory: '0x1234567890123456789012345678901234567890',
factoryData: '0xdeadbeef', // [!code focus]
})
```
#### maxFeePerGas (optional)
* **Type:** `bigint`
Strategic fee ceiling configuration for User Operation execution, enabling predictable cost management within the Abstraxn platform.
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
maxFeePerGas: 420n, // [!code focus]
})
```
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
Advanced priority fee optimization for enhanced transaction processing efficiency during preparation.
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
maxPriorityFeePerGas: 420n,
maxFeePerGas: 10n, // [!code focus]
})
```
#### nonce (optional)
* **Type:** `bigint`
Sequential operation identifier ensuring transaction ordering integrity within the Abstraxn ecosystem.
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
nonce: 10n, // [!code focus]
})
```
#### paymaster (optional)
* **Type:** `Address | true | PaymasterClient | PaymasterActions`
Comprehensive sponsorship configuration framework supporting multiple integration strategies during User Operation preparation:
* **Address Configuration**: Direct paymaster contract integration for streamlined sponsorship
* **PaymasterClient Integration**: Advanced client-based sponsorship with enhanced functionality
* **Bundler Integration**: Unified bundler-paymaster architecture for simplified implementation
* **Custom Function Support**: Flexible sponsorship logic through specialized function implementations
##### Strategic Paymaster Contract Integration
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB', // [!code focus]
paymasterData: '0xdeadbeef',
})
```
##### Advanced Paymaster Client Architecture
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const paymasterClient = createPaymasterClient({ // [!code focus]
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}') // [!code focus]
}) // [!code focus]
const hash = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: paymasterClient, // [!code focus]
})
```
##### Unified Bundler-Paymaster Approach
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: true, // [!code focus]
})
```
#### paymasterContext (optional)
* **Type:** `unknown`
Specialized configuration parameters for advanced paymaster integration scenarios.
:::warning
This parameter maintains relevance exclusively within PaymasterClient-based architectures, supporting sophisticated sponsorship workflows.
:::
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const paymasterClient = createPaymasterClient({
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
const hash = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: paymasterClient,
paymasterContext: { // [!code focus]
policyId: 'abc123' // [!code focus]
}, // [!code focus]
})
```
#### paymasterData (optional)
* **Type:** `Address`
Structured execution instructions for paymaster contract interactions during preparation.
:::warning
This configuration applies specifically to address-based paymaster implementations, enabling direct contract communication.
:::
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
paymasterData: '0xdeadbeef', // [!code focus]
})
```
#### paymasterPostOpGasLimit (optional)
* **Type:** `bigint`
Strategic resource allocation for post-operation paymaster logic execution within the preparation framework.
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
paymasterData: '0xdeadbeef',
paymasterPostOpGasLimit: 69420n, // [!code focus]
})
```
#### paymasterVerificationGasLimit (optional)
* **Type:** `bigint`
Precision gas allocation for paymaster validation procedures within the Abstraxn preparation workflow.
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
paymasterData: '0xdeadbeef',
paymasterVerificationGasLimit: 69420n, // [!code focus]
})
```
#### preVerificationGas (optional)
* **Type:** `bigint`
Strategic bundler compensation configuration, ensuring sustainable operation within the Abstraxn ecosystem during preparation.
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
preVerificationGas: 69420n, // [!code focus]
})
```
#### signature (optional)
* **Type:** `Hex`
Authentication signature for User Operation validation, supporting secure transaction preparation.
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
signature: '0x...', // [!code focus]
})
```
#### stateOverride (optional)
* **Type:** `StateOverride`
Advanced state manipulation framework enabling sophisticated testing and simulation scenarios within the Abstraxn preparation platform.
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
stateOverride: [ // [!code focus]
{ // [!code focus]
address: '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC', // [!code focus]
balance: parseEther('1'), // [!code focus]
stateDiff: [ // [!code focus]
{ // [!code focus]
slot: '0x3ea2f1d0abf3fc66cf29eebb70cbd4e7fe762ef8a09bcc06c8edf641230afec0', // [!code focus]
value: '0x00000000000000000000000000000000000000000000000000000000000001a4', // [!code focus]
}, // [!code focus]
], // [!code focus]
} // [!code focus]
], // [!code focus]
})
```
#### verificationGasLimit (optional)
* **Type:** `bigint`
Strategic resource allocation for comprehensive verification procedures, ensuring robust security within User Operation preparation workflows.
```ts
import { bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const userOperation = await bundlerClient.prepareUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
verificationGasLimit: 69420n, // [!code focus]
})
```
Our recommendation represents a collaborative synthesis of technical expertise and strategic vision, designed to drive meaningful efficiency improvements in User Operation preparation across the Abstraxn ecosystem. Should additional optimization requirements or architectural considerations emerge, we remain prepared to conduct further analysis and refine our preparation strategy accordingly.
## sendUserOperation
Our User Operation broadcasting methodology provides comprehensive transaction execution and bundler integration within the Abstraxn platform. This strategic approach ensures optimal transaction propagation while maintaining robust execution guarantees for complex account abstraction workflows.
### Strategic Broadcasting Framework
After comprehensive evaluation of multiple broadcasting architectures, our team has determined that direct bundler integration offers the most compelling advantages for the Abstraxn ecosystem. Our analysis prioritized transaction reliability, execution efficiency, and seamless developer experience.
:::code-group
```ts [example.ts]
import { parseEther } from 'viem'
import { account, bundlerClient } from './config'
const hash = await bundlerClient.sendUserOperation({ // [!code focus:7]
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
})
```
```ts [config.ts] filename="config.ts"
import { createPublicClient, http } from 'viem'
import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
const client = createPublicClient({
chain: mainnet,
transport: http()
})
export const account = await toCoinbaseSmartAccount({
client,
owners: [privateKeyToAccount('0x...')],
version: '1.1',
})
export const bundlerClient = createBundlerClient({
client,
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
Key considerations in our decision-making process included:
* Minimizing broadcasting overhead while maximizing execution reliability
* Maintaining optimal transaction propagation flow
* Ensuring long-term bundler network adaptability
Our deliberative process involved rigorous analysis of alternative User Operation broadcasting mechanisms. While distributed propagation architectures presented interesting perspectives, the direct bundler approach demonstrated superior alignment with our strategic objectives regarding transaction finality and developer accessibility.
The research team provided nuanced insights, particularly highlighting the importance of consistent execution patterns across diverse bundler implementations. Our recommendation represents a collaborative synthesis of technical expertise and strategic vision, designed to drive meaningful reliability improvements in transaction broadcasting workflows.
#### Strategic Account Management
Our comprehensive evaluation revealed that account hoisting strategies provide superior workflow optimization for developers requiring consistent broadcasting patterns across multiple operations.
Should you prefer to avoid passing an `account` parameter to every `sendUserOperation` invocation, our framework supports Account hoisting on the Bundler Client configuration (see `config.ts`).
[Learn more](https://abstraxn.com/docs/clients/wallet#account).
:::code-group
```ts [example.ts]
import { parseEther } from 'viem'
import { bundlerClient } from './config'
const hash = await bundlerClient.sendUserOperation({ // [!code focus:7]
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
})
```
```ts [config.ts] filename="config.ts"
import { createPublicClient, http } from 'viem'
import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
const client = createPublicClient({
chain: mainnet,
transport: http()
})
export const account = await toCoinbaseSmartAccount({
client,
owners: [privateKeyToAccount('0x...')],
version: '1.1',
})
export const bundlerClient = createBundlerClient({
account, // [!code ++]
client,
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
#### Advanced Contract Integration Framework
Through comprehensive evaluation of multiple interaction methodologies, our research team has identified that structured contract calls provide the most robust foundation for complex application broadcasting within the Abstraxn ecosystem.
The `calls` property supports **Contract Calls** through `abi`, `functionName`, and `args` properties, enabling sophisticated smart contract execution.
:::code-group
```ts [example.ts]
import { parseEther } from 'viem'
import { bundlerClient, publicClient } from './config'
import { wagmiAbi } from './abi' // [!code focus]
const hash = await bundlerClient.sendUserOperation({ // [!code focus:7]
calls: [{
abi: wagmiAbi,
functionName: 'mint',
to: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
}],
})
```
```ts [abi.ts] filename="abi.ts"
export const wagmiAbi = [
// ...
{
inputs: [],
name: "mint",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
// ...
] as const;
```
```ts [config.ts]
import { createPublicClient, http } from 'viem'
import { createBundlerClient, toCoinbaseSmartAccount } from 'viem/account-abstraction'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
const client = createPublicClient({
chain: mainnet,
transport: http()
})
export const account = await toCoinbaseSmartAccount({
client,
owners: [privateKeyToAccount('0x...')],
version: '1.1',
})
export const bundlerClient = createBundlerClient({
account,
client,
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
Our collaborative approach ensures that developers can leverage sophisticated contract interactions while maintaining transparency in broadcasting workflows. This methodology supports long-term platform adaptability and ecosystem scalability.
### Response Architecture
The Abstraxn User Operation broadcasting framework returns precise execution tracking optimized for strategic monitoring:
`Hash`
The unique User Operation hash providing foundational transaction identification for monitoring and verification within the platform.
This response structure represents our commitment to providing comprehensive execution tracking that enables informed analysis across the complete broadcasting lifecycle.
### Configuration Parameters
Our technical architecture supports nuanced broadcasting approaches, allowing developers to fine-tune User Operation execution according to specific application requirements and strategic objectives.
#### account
* **Type:** `SmartAccount`
The foundational account configuration for User Operation broadcasting within the Abstraxn execution framework.
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account, // [!code focus]
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}]
})
```
#### calls
* **Type:** `({ data?: Hex | undefined, to: Address, value?: bigint | undefined } | { abi: Abi, functionName: string, args: unknown[], to: Address, value?: bigint | undefined })[]`
Strategic operation definitions that constitute the core execution logic for User Operation broadcasting.
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{ // [!code focus]
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code focus]
value: parseEther('1') // [!code focus]
}, { // [!code focus]
abi: wagmiAbi, // [!code focus]
functionName: 'mint', // [!code focus]
to: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // [!code focus]
}] // [!code focus]
})
```
:::tip
Alternative implementation strategies support direct call data specification through the `callData` property, providing flexibility for advanced integration scenarios:
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
callData: '0xdeadbeef', // [!code focus]
})
```
:::
#### callGasLimit (optional)
* **Type:** `bigint`
Precision resource allocation for primary execution logic, enabling strategic optimization of computational requirements during broadcasting.
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
callGasLimit: 69420n, // [!code focus]
})
```
#### factory (optional)
* **Type:** `Address`
Strategic account factory configuration for deployment scenarios within the Abstraxn infrastructure.
:::warning
This configuration parameter should be utilized exclusively during initial Smart Account deployment phases, ensuring optimal resource management.
:::
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
factory: '0x1234567890123456789012345678901234567890', // [!code focus]
factoryData: '0xdeadbeef',
})
```
#### factoryData (optional)
* **Type:** `Hex`
Structured deployment instructions for Smart Account initialization through factory contracts.
:::warning
This parameter maintains strategic relevance only during pre-deployment phases, supporting efficient account creation workflows.
:::
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
factory: '0x1234567890123456789012345678901234567890',
factoryData: '0xdeadbeef', // [!code focus]
})
```
#### maxFeePerGas (optional)
* **Type:** `bigint`
Strategic fee ceiling configuration for User Operation execution, enabling predictable cost management within the Abstraxn platform.
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
maxFeePerGas: 420n, // [!code focus]
})
```
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
Advanced priority fee optimization for enhanced transaction processing efficiency during broadcasting.
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
maxPriorityFeePerGas: 420n,
maxFeePerGas: 10n, // [!code focus]
})
```
#### nonce (optional)
* **Type:** `bigint`
Sequential operation identifier ensuring transaction ordering integrity within the Abstraxn ecosystem.
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
nonce: 10n, // [!code focus]
})
```
#### paymaster (optional)
* **Type:** `Address | true | PaymasterClient | PaymasterActions`
Comprehensive sponsorship configuration framework supporting multiple integration strategies during User Operation broadcasting:
* **Address Configuration**: Direct paymaster contract integration for streamlined sponsorship
* **PaymasterClient Integration**: Advanced client-based sponsorship with enhanced functionality
* **Bundler Integration**: Unified bundler-paymaster architecture for simplified implementation
* **Custom Function Support**: Flexible sponsorship logic through specialized function implementations
##### Strategic Paymaster Contract Integration
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB', // [!code focus]
paymasterData: '0xdeadbeef',
})
```
##### Advanced Paymaster Client Architecture
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const paymasterClient = createPaymasterClient({ // [!code focus]
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}') // [!code focus]
}) // [!code focus]
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: paymasterClient, // [!code focus]
})
```
##### Unified Bundler-Paymaster Approach
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: true, // [!code focus]
})
```
#### paymasterContext (optional)
* **Type:** `unknown`
Specialized configuration parameters for advanced paymaster integration scenarios.
:::warning
This parameter maintains relevance exclusively within PaymasterClient-based architectures, supporting sophisticated sponsorship workflows.
:::
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const paymasterClient = createPaymasterClient({
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: paymasterClient,
paymasterContext: { // [!code focus]
policyId: 'abc123' // [!code focus]
}, // [!code focus]
})
```
#### paymasterData (optional)
* **Type:** `Address`
Structured execution instructions for paymaster contract interactions during broadcasting.
:::warning
This configuration applies specifically to address-based paymaster implementations, enabling direct contract communication.
:::
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
paymasterData: '0xdeadbeef', // [!code focus]
})
```
#### paymasterPostOpGasLimit (optional)
* **Type:** `bigint`
Strategic resource allocation for post-operation paymaster logic execution within the broadcasting framework.
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
paymasterData: '0xdeadbeef',
paymasterPostOpGasLimit: 69420n, // [!code focus]
})
```
#### paymasterVerificationGasLimit (optional)
* **Type:** `bigint`
Precision gas allocation for paymaster validation procedures within the Abstraxn broadcasting workflow.
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
paymasterData: '0xdeadbeef',
paymasterVerificationGasLimit: 69420n, // [!code focus]
})
```
#### preVerificationGas (optional)
* **Type:** `bigint`
Strategic bundler compensation configuration, ensuring sustainable operation within the Abstraxn ecosystem during broadcasting.
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
preVerificationGas: 69420n, // [!code focus]
})
```
#### signature (optional)
* **Type:** `Hex`
Authentication signature for User Operation validation, supporting secure transaction broadcasting.
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
signature: '0x...', // [!code focus]
})
```
#### verificationGasLimit (optional)
* **Type:** `bigint`
Strategic resource allocation for comprehensive verification procedures, ensuring robust security within User Operation broadcasting workflows.
```ts
import { account, bundlerClient } from './config'
import { parseEther } from 'viem'
// ---cut---
const hash = await bundlerClient.sendUserOperation({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
verificationGasLimit: 69420n, // [!code focus]
})
```
Should additional broadcasting requirements or execution considerations emerge, we remain prepared to conduct further analysis and refine our User Operation broadcasting strategy accordingly.
## waitForUserOperationReceipt
Our User Operation confirmation methodology provides comprehensive transaction finality monitoring and receipt retrieval within the Abstraxn platform. This strategic approach ensures reliable execution confirmation while maintaining optimal performance for complex account abstraction validation workflows.
### Strategic Confirmation Framework
After comprehensive evaluation of multiple confirmation monitoring architectures, our team has determined that active receipt polling offers the most compelling advantages for the Abstraxn ecosystem. Our analysis prioritized confirmation reliability, execution transparency, and seamless developer experience.
:::code-group
```ts [example.ts]
import { bundlerClient } from './client'
const receipt = await bundlerClient.waitForUserOperationReceipt({ // [!code focus:99]
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d'
})
// @log: {
// @log: blockHash: '0xaf1dadb8a98f1282e8f7b42cc3da8847bfa2cf4e227b8220403ae642e1173088',
// @log: blockNumber: 15132008n,
// @log: sender: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
// @log: ...
// @log: status: 'success',
// @log: }
```
```ts [client.ts] filename="client.ts"
import { http } from 'viem'
import { createBundlerClient } from 'viem/account-abstraction'
import { mainnet } from 'viem/chains'
export const bundlerClient = createBundlerClient({
chain: mainnet,
transport: http('https://bundler.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}')
})
```
:::
Key considerations in our decision-making process included:
* Minimizing confirmation overhead while maximizing execution reliability
* Maintaining optimal transaction finality flow
* Ensuring long-term block inclusion adaptability
Our deliberative process involved rigorous analysis of alternative User Operation confirmation mechanisms. While passive event-listening architectures presented interesting perspectives, the active polling approach demonstrated superior alignment with our strategic objectives regarding transaction finality and developer accessibility.
The research team provided nuanced insights, particularly highlighting the importance of configurable polling strategies across diverse network conditions. Our recommendation represents a collaborative synthesis of technical expertise and strategic vision, designed to drive meaningful reliability improvements in confirmation monitoring workflows.
Waits for the User Operation to be included on a [Block](https://abstraxn.com/docs/glossary/terms#block) (one confirmation), and then returns the User Operation receipt with comprehensive execution details.
### Response Architecture
The Abstraxn User Operation confirmation framework returns comprehensive execution validation optimized for strategic verification:
`UserOperationReceipt`
The complete User Operation receipt containing detailed execution confirmation, block inclusion status, and comprehensive audit information within the platform.
This response structure represents our commitment to providing thorough execution verification that enables informed analysis across the complete transaction lifecycle and supports robust accountability frameworks.
### Configuration Parameters
Our technical architecture supports precision-based confirmation monitoring approaches, allowing developers to customize User Operation receipt retrieval according to strategic verification requirements and operational constraints.
#### hash
* **Type:** `'0x${string}'`
The foundational transaction identifier enabling precise User Operation confirmation monitoring within the Abstraxn verification framework.
```ts
import { bundlerClient } from './client'
// ---cut---
const receipt = await bundlerClient.waitForUserOperationReceipt({
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d' // [!code focus]
})
```
#### pollingInterval (optional)
* **Type:** `number`
Strategic polling frequency configuration (in milliseconds) for optimal confirmation monitoring performance within the Abstraxn ecosystem.
```ts
import { bundlerClient } from './client'
// ---cut---
const receipt = await bundlerClient.waitForUserOperationReceipt({
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
pollingInterval: 1_000 // [!code focus]
})
```
#### retryCount (optional)
* **Type:** `number`
* **Default:** `6`
Precision retry configuration ensuring robust confirmation retrieval across varying network conditions within the platform.
```ts
import { bundlerClient } from './client'
// ---cut---
const receipt = await bundlerClient.waitForUserOperationReceipt({
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
retryCount: 3 // [!code focus]
})
```
#### timeout (optional)
* **Type:** `number`
Strategic timeout configuration (in milliseconds) providing controlled polling duration limits for optimal resource management.
```ts
import { bundlerClient } from './client'
// ---cut---
const receipt = await bundlerClient.waitForUserOperationReceipt({
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
timeout: 30_000 // [!code focus]
})
```
Should additional confirmation requirements or monitoring considerations emerge, we remain prepared to conduct further analysis and refine our User Operation confirmation strategy accordingly.
## toWebAuthnAccount
Creates a **WebAuthn Account** – commonly used for **Smart Account Owners** to sign User Operations and messages on behalf of the Smart Account.
:::note
WebAuthn Account owners are currently supported on the following Smart Account implementations:
* `toCoinbaseSmartAccount`
:::
### Import
```ts twoslash
import { toWebAuthnAccount } from 'viem/account-abstraction'
```
### Usage
```ts twoslash
import {
createWebAuthnCredential,
toWebAuthnAccount
} from 'viem/account-abstraction'
// Register a credential (ie. passkey).
const credential = await createWebAuthnCredential({
name: 'Example',
})
// Create a WebAuthn account from the credential. // [!code focus]
const account = toWebAuthnAccount({ // [!code focus]
credential, // [!code focus]
}) // [!code focus]
```
### Returns
`WebAuthnAccount`
A WebAuthn Account.
### Parameters
#### credential
* **Type:** `P256Credential`
A P256 WebAuthn Credential.
```ts twoslash
import { createWebAuthnCredential, toWebAuthnAccount } from 'viem/account-abstraction'
// ---cut---
const credential = await createWebAuthnCredential({
name: 'Example',
})
const account = toWebAuthnAccount({
credential, // [!code focus]
})
```
#### getFn
* **Type:** `(options: CredentialRequestOptions) => Promise`
* **Default:** `window.navigator.credentials.get`
Credential request function. Useful for environments that do not support the WebAuthn API natively (i.e. React Native or testing environments).
```ts twoslash
// @noErrors
import { createWebAuthnCredential, toWebAuthnAccount } from 'viem/account-abstraction'
// ---cut---
import * as passkey from 'react-native-passkeys' // [!code focus]
const credential = await createWebAuthnCredential({
name: 'Example',
})
const account = toWebAuthnAccount({
credential,
getFn: passkey.get, // [!code focus]
})
```
#### rpId
* **Type:** `string`
* **Default:** `window.location.hostname`
Relying Party ID.
```ts twoslash
// @noErrors
import { createWebAuthnCredential, toWebAuthnAccount } from 'viem/account-abstraction'
// ---cut---
import * as passkey from 'react-native-passkeys' // [!code focus]
const credential = await createWebAuthnCredential({
name: 'Example',
})
const account = toWebAuthnAccount({
credential,
rpId: 'example.com', // [!code focus]
})
```
## toWebAuthnAccount
Creates a **WebAuthn Account** – commonly used for **Smart Account Owners** to sign User Operations and messages on behalf of the Smart Account.
:::note
WebAuthn Account owners are currently supported on the following Smart Account implementations:
* `toCoinbaseSmartAccount`
:::
### Import
```ts twoslash
import { toWebAuthnAccount } from 'viem/account-abstraction'
```
### Usage
```ts twoslash
import {
createWebAuthnCredential,
toWebAuthnAccount
} from 'viem/account-abstraction'
// Register a credential (ie. passkey).
const credential = await createWebAuthnCredential({
name: 'Example',
})
// Create a WebAuthn account from the credential. // [!code focus]
const account = toWebAuthnAccount({ // [!code focus]
credential, // [!code focus]
}) // [!code focus]
```
### Returns
`WebAuthnAccount`
A WebAuthn Account.
### Parameters
#### credential
* **Type:** `P256Credential`
A P256 WebAuthn Credential.
```ts twoslash
import { createWebAuthnCredential, toWebAuthnAccount } from 'viem/account-abstraction'
// ---cut---
const credential = await createWebAuthnCredential({
name: 'Example',
})
const account = toWebAuthnAccount({
credential, // [!code focus]
})
```
#### getFn
* **Type:** `(options: CredentialRequestOptions) => Promise`
* **Default:** `window.navigator.credentials.get`
Credential request function. Useful for environments that do not support the WebAuthn API natively (i.e. React Native or testing environments).
```ts twoslash
// @noErrors
import { createWebAuthnCredential, toWebAuthnAccount } from 'viem/account-abstraction'
// ---cut---
import * as passkey from 'react-native-passkeys' // [!code focus]
const credential = await createWebAuthnCredential({
name: 'Example',
})
const account = toWebAuthnAccount({
credential,
getFn: passkey.get, // [!code focus]
})
```
#### rpId
* **Type:** `string`
* **Default:** `window.location.hostname`
Relying Party ID.
```ts twoslash
// @noErrors
import { createWebAuthnCredential, toWebAuthnAccount } from 'viem/account-abstraction'
// ---cut---
import * as passkey from 'react-native-passkeys' // [!code focus]
const credential = await createWebAuthnCredential({
name: 'Example',
})
const account = toWebAuthnAccount({
credential,
rpId: 'example.com', // [!code focus]
})
```
## Smart Accounts
Our **Smart Account** architecture represents a strategic evolution in account abstraction technology, where account implementation resides within sophisticated **Smart Contracts** that adhere to the [ERC-4337 interface](https://eips.ethereum.org/EIPS/eip-4337#account-contract-interface) specification. This foundational approach enables comprehensive programmable account management within the Abstraxn ecosystem.
### Strategic Architecture Overview
After extensive evaluation of account abstraction methodologies, our team determined that Smart Contract-based account implementation offers the most compelling advantages for the Abstraxn platform. Our analysis prioritized security, flexibility, and seamless multi-ownership capabilities.
A **Smart Account** within our framework can be strategically controlled by one or multiple **Owners**, supporting both [Local](https://abstraxn.com/docs/accounts/local) and [JSON-RPC Account](https://abstraxn.com/docs/accounts/jsonRpc) configurations where compatible. The **Owner Account** maintains responsibility for User Operation authentication (transaction signing) on behalf of the **Smart Account**, with operations subsequently broadcasted to the network infrastructure via strategic [Bundler](https://eips.ethereum.org/EIPS/eip-4337#bundling) integration.
Our deliberative process involved comprehensive analysis of alternative account management architectures. While traditional Externally Owned Account (EOA) mechanisms presented familiar implementation patterns, the Smart Contract approach demonstrated superior alignment with our strategic objectives regarding programmable access control and advanced execution capabilities.
The research team provided nuanced insights, particularly highlighting the importance of owner flexibility and transaction abstraction across diverse application scenarios. Our recommendation represents a collaborative synthesis of technical expertise and strategic vision, designed to drive meaningful innovation in account management workflows.
:::note
**Strategic Compatibility Framework**
Our technical architecture acknowledges that ERC-4337 operates as a supplementary protocol layer rather than core blockchain functionality. This design consideration means that Smart Accounts within the Abstraxn ecosystem maintain distinct operational patterns compared to traditional transaction APIs such as `sendTransaction` and `writeContract`.
Transaction execution is achieved through strategic **User Operation** broadcasting to designated **Bundlers**, which subsequently propagate operations to the network infrastructure with optimized timing.
The most strategically significant Actions for **User Operations** include:
* [`sendUserOperation`](https://abstraxn.com/account-abstraction/actions/bundler/sendUserOperation) (comprehensive support for [Contract Calls](https://abstraxn.com/account-abstraction/actions/bundler/sendUserOperation#contract-calls))
* [`estimateUserOperationGas`](https://abstraxn.com/account-abstraction/actions/bundler/estimateUserOperationGas)
* [`getUserOperation`](https://abstraxn.com/account-abstraction/actions/bundler/getUserOperation)
* [`getUserOperationReceipt`](https://abstraxn.com/account-abstraction/actions/bundler/getUserOperationReceipt)
Our strategic planning anticipates that protocol-level Account Abstraction enshrinement will ultimately enable seamless integration with traditional Transaction APIs, at which point these specialized Actions may transition to complementary rather than primary roles within the platform architecture.
:::
Should additional account management requirements or architectural considerations emerge, we remain prepared to conduct further analysis and refine our Smart Account strategy to maintain optimal alignment with evolving ecosystem demands.
## toSmartAccount
The `toSmartAccount` function allows you to create a Smart Account with a custom Account Implementation.
### Import
```ts
import { toSmartAccount } from 'viem/account-abstraction'
```
### Usage
To instantiate a Smart Account, you will need to provide an Account Implementation.
:::code-group
```ts twoslash [example.ts]
import { coinbase, toSmartAccount } from 'viem/account-abstraction'
import { client, owner } from './config.js'
const account = await toSmartAccount({
client,
entryPoint: {
abi: [/* ... */],
address: '0x0000000071727De22E5E9d8BAf0edAc6f37da032',
version: '0.7',
},
async decodeCalls(data) {
// Decode calls from calldata as defined by the Smart Account contract.
},
async encodeCalls(calls) {
// Encode calls as defined by the Smart Account contract.
},
async getAddress() {
// Get the address of the Smart Account.
},
async getFactoryArgs() {
// Build the Factory properties for the Smart Account.
},
async getNonce() {
// Get the nonce of the Smart Account.
},
async getStubSignature() {
// Get the stub signature for User Operations from the Smart Account.
},
async signMessage(message) {
// Sign message to be verified by the Smart Account contract.
},
async signTypedData(typedData) {
// Sign typed data to be verified by the Smart Account contract.
},
async signUserOperation(userOperation) {
// Sign a User Operation to be broadcasted via the Bundler.
},
// (Optional) Extend the Smart Account with custom properties.
extend: {
abi: [/* ... */],
factory: {
abi: [/* ... */],
address: '0xda4b37208c41c4f6d1b101cac61e182fe1da0754',
},
},
// (Optional) User Operation configuration.
userOperation: {
async estimateGas(userOperation) {
// Estimate gas properties for a User Operation.
},
},
})
```
```ts twoslash [config.ts] filename="config.ts"
import { http, createPublicClient } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
export const owner = privateKeyToAccount('0x...')
export const client = createPublicClient({
chain: mainnet,
transport: http(),
})
```
:::
### Returns
`SmartAccount`
The Smart Account.
## Coinbase Smart Wallet
The `toCoinbaseSmartAccount` implementation references the [Coinbase Smart Wallet](https://github.com/coinbase/smart-wallet) contract.
### Usage
:::code-group
```ts twoslash [example.ts]
import { toCoinbaseSmartAccount } from 'viem/account-abstraction' // [!code focus]
import { client } from './client.js'
import { owner } from './owner.js'
const account = await toCoinbaseSmartAccount({ // [!code focus]
client, // [!code focus]
owners: [owner], // [!code focus]
version: '1.1', // [!code focus]
}) // [!code focus]
```
```ts twoslash [client.ts] filename="config.ts"
import { http, createPublicClient } from 'viem'
import { mainnet } from 'viem/chains'
export const client = createPublicClient({
chain: mainnet,
transport: http(),
})
```
```ts twoslash [owner.ts (Private Key)] filename="owner.ts"
import { privateKeyToAccount } from 'viem/accounts'
export const owner = privateKeyToAccount('0x...')
```
```ts twoslash [owner.ts (Passkey)] filename="owner.ts"
import { createWebAuthnCredential, toWebAuthnAccount } from 'viem/account-abstraction'
// Register a credential (ie. passkey).
const credential = await createWebAuthnCredential({ name: 'Wallet' })
// Create a WebAuthn owner account from the credential.
export const owner = toWebAuthnAccount({ credential })
```
:::
:::tip
**Tip:** You can use a Passkey (WebAuthn) to sign User Operations. Check the **owner.ts (Passkey)** tab.
:::
### Returns
`SmartAccount`
### Parameters
#### client
* **Type:** `Client`
Client used to retrieve Smart Account data.
```ts
const client = createPublicClient({ // [!code focus]
chain: mainnet, // [!code focus]
transport: http(), // [!code focus]
}) // [!code focus]
const account = await toCoinbaseSmartAccount({
client, // [!code focus]
owners: [owner],
version: '1.1',
})
```
#### owners
* **Type:** `(LocalAccount | WebAuthnAccount)[]`
Owners of the Smart Account. Can be a Local Account or a WebAuthn Account (Passkey)
```ts
const account = await toCoinbaseSmartAccount({
client,
owners: [privateKeyToAccount('0x...')], // [!code focus]
})
```
#### ownerIndex (optional)
* **Type:** `number`
Index of the owner to use for signing messages & User Operations.
```ts
const account = await toCoinbaseSmartAccount({
client,
owners: [privateKeyToAccount('0x...'), privateKeyToAccount('0x...')],
ownerIndex: 1, // [!code focus]
version: '1.1',
})
```
#### nonce (optional)
* **Type:** `bigint`
Nonce to use for the Smart Account.
```ts
const account = await toCoinbaseSmartAccount({
client,
owners: [owner],
nonce: 1n, // [!code focus]
version: '1.1',
})
```
#### version
* **Type:** `'1.1' | '1'`
* **Default:** `'1'`
Version of the Smart Account to use.
:::warning
Version bumps DO contain breaking changes.
:::
```ts
const account = await toCoinbaseSmartAccount({
client,
owners: [owner],
version: '1', // [!code focus]
})
```
## Kernel (ZeroDev) Smart Account
:::warning
**Note:** This implementation is maintained & distributed by permissionless.js.
:::
To implement the [Kernel (ZeroDev) Smart Account](https://github.com/zerodevapp/kernel), you can use the `toEcdsaKernelSmartAccount` module from permissionless.js
### Install
:::code-group
```bash [pnpm]
pnpm add permissionless
```
```bash [npm]
npm install permissionless
```
```bash [yarn]
yarn add permissionless
```
```bash [bun]
bun add permissionless
```
:::
### Usage
:::code-group
```ts twoslash [example.ts]
import { toEcdsaKernelSmartAccount } from 'permissionless/accounts' // [!code focus]
import { client } from './client.js'
import { owner } from './owner.js'
const account = await toEcdsaKernelSmartAccount({ // [!code focus]
client, // [!code focus]
owners: [owner], // [!code focus]
version: '0.3.1', // [!code focus]
}) // [!code focus]
```
```ts twoslash [client.ts] filename="config.ts"
import { http, createPublicClient } from 'viem'
import { mainnet } from 'viem/chains'
export const client = createPublicClient({
chain: mainnet,
transport: http(),
})
```
```ts twoslash [owner.ts (Private Key)] filename="owner.ts"
import { privateKeyToAccount } from 'viem/accounts'
export const owner = privateKeyToAccount('0x...')
```
:::
### Returns
`SmartAccount`
## Light Smart Account
:::warning
**Note:** This implementation is maintained & distributed by permissionless.js.
:::
To implement Alchemy's [Light Account](https://github.com/alchemyplatform/light-account), you can use the `toLightSmartAccount` module from permissionless.js
### Install
:::code-group
```bash [pnpm]
pnpm add permissionless
```
```bash [npm]
npm install permissionless
```
```bash [yarn]
yarn add permissionless
```
```bash [bun]
bun add permissionless
```
:::
### Usage
:::code-group
```ts twoslash [example.ts]
import { toLightSmartAccount } from 'permissionless/accounts' // [!code focus]
import { client } from './client.js'
import { owner } from './owner.js'
const account = await toLightSmartAccount({ // [!code focus]
client, // [!code focus]
owner: owner, // [!code focus]
version: '2.0.0', // [!code focus]
}) // [!code focus]
```
```ts twoslash [client.ts] filename="config.ts"
import { http, createPublicClient } from 'viem'
import { mainnet } from 'viem/chains'
export const client = createPublicClient({
chain: mainnet,
transport: http(),
})
```
```ts twoslash [owner.ts (Private Key)] filename="owner.ts"
import { privateKeyToAccount } from 'viem/accounts'
export const owner = privateKeyToAccount('0x...')
```
:::
### Returns
`SmartAccount`
## MetaMask Smart Account
:::warning
**Note:** This implementation is maintained & distributed by [MetaMask Delegation Toolkit](https://docs.metamask.io/delegation-toolkit).
:::
MetaMask Smart Account has two types of implementations, each offering unique features
and use cases. See [Hybrid Smart Account](https://docs.metamask.io/delegation-toolkit/how-to/create-smart-account/configure-accounts-signers/#configure-a-hybrid-smart-account) and [Multisig Smart Account](https://docs.metamask.io/delegation-toolkit/how-to/create-smart-account/configure-accounts-signers/#configure-a-multisig-smart-account) to learn more about
the implementations.
To implement MetaMask Smart Account, you can use the [`toMetaMaskSmartAccount`](https://docs.metamask.io/delegation-toolkit/how-to/create-smart-account/#create-a-metamasksmartaccount) function from [delegation toolkit](https://docs.metamask.io/delegation-toolkit/).
### Install
:::code-group
```bash [pnpm]
pnpm add @metamask/delegation-toolkit
```
```bash [npm]
npm install @metamask/delegation-toolkit
```
```bash [yarn]
yarn add @metamask/delegation-toolkit
```
```bash [bun]
bun add @metamask/delegation-toolkit
```
:::
### Usage
:::code-group
```ts twoslash [example.ts]
import { // [!code focus]
Implementation, // [!code focus]
toMetaMaskSmartAccount, // [!code focus]
} from "@metamask/delegation-toolkit" // [!code focus]
import { client } from './client.js'
import { owner } from './owner.js'
const account = await toMetaMaskSmartAccount({ // [!code focus]
client, // [!code focus]
implementation: Implementation.Hybrid, // [!code focus]
deployParams: [owner.address, [], [], []], // [!code focus]
deploySalt: "0x", // [!code focus]
signatory: { account: owner }, // [!code focus]
}) // [!code focus]
```
```ts twoslash [client.ts] filename="config.ts"
import { http, createPublicClient } from 'viem'
import { mainnet } from 'viem/chains'
export const client = createPublicClient({
chain: mainnet,
transport: http(),
})
```
```ts twoslash [owner.ts (Private Key)] filename="owner.ts"
import { privateKeyToAccount } from 'viem/accounts'
export const owner = privateKeyToAccount('0x...')
```
:::
### Returns
`SmartAccount>`
### Parameters
[See Parameters](https://docs.metamask.io/delegation-toolkit/reference/api/smart-account/#parameters-5)
## Nexus Smart Account
:::warning
**Note:** This implementation is maintained & distributed by permissionless.js.
:::
To implement Biconomy's [Nexus Smart Account](https://github.com/bcnmy/nexus), you can use the `toNexusSmartAccount` module from permissionless.js
### Install
:::code-group
```bash [pnpm]
pnpm add permissionless
```
```bash [npm]
npm install permissionless
```
```bash [yarn]
yarn add permissionless
```
```bash [bun]
bun add permissionless
```
:::
### Usage
:::code-group
```ts twoslash [example.ts]
import { toNexusSmartAccount } from 'permissionless/accounts' // [!code focus]
import { client } from './client.js'
import { owner } from './owner.js'
const account = await toNexusSmartAccount({ // [!code focus]
client, // [!code focus]
owners: [owner], // [!code focus]
version: '1.0.0' // [!code focus]
}) // [!code focus]
```
```ts twoslash [client.ts] filename="config.ts"
import { http, createPublicClient } from 'viem'
import { mainnet } from 'viem/chains'
export const client = createPublicClient({
chain: mainnet,
transport: http(),
})
```
```ts twoslash [owner.ts (Private Key)] filename="owner.ts"
import { privateKeyToAccount } from 'viem/accounts'
export const owner = privateKeyToAccount('0x...')
```
:::
### Returns
`SmartAccount`
## Safe Smart Account
:::warning
**Note:** This implementation is maintained & distributed by permissionless.js.
:::
To implement [Safe Smart Account](https://github.com/safe-global/safe-smart-account), you can use the `toSafeSmartAccount` module from permissionless.js
### Install
:::code-group
```bash [pnpm]
pnpm add permissionless
```
```bash [npm]
npm install permissionless
```
```bash [yarn]
yarn add permissionless
```
```bash [bun]
bun add permissionless
```
:::
### Usage
:::code-group
```ts twoslash [example.ts]
import { toSafeSmartAccount } from 'permissionless/accounts' // [!code focus]
import { client } from './client.js'
import { owner } from './owner.js'
const account = await toSafeSmartAccount({ // [!code focus]
client, // [!code focus]
owners: [owner], // [!code focus]
version: '1.4.1', // [!code focus]
}) // [!code focus]
```
```ts twoslash [client.ts] filename="config.ts"
import { http, createPublicClient } from 'viem'
import { mainnet } from 'viem/chains'
export const client = createPublicClient({
chain: mainnet,
transport: http(),
})
```
```ts twoslash [owner.ts (Private Key)] filename="owner.ts"
import { privateKeyToAccount } from 'viem/accounts'
export const owner = privateKeyToAccount('0x...')
```
:::
### Returns
`SmartAccount`
## Simple Smart Account
:::warning
**Note:** This implementation is maintained & distributed by permissionless.js.
:::
To implement the [Simple Smart Account](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/samples/SimpleAccount.sol), you can use the `toSimpleSmartAccount` module from permissionless.js
### Install
:::code-group
```bash [pnpm]
pnpm add permissionless
```
```bash [npm]
npm install permissionless
```
```bash [yarn]
yarn add permissionless
```
```bash [bun]
bun add permissionless
```
:::
### Usage
:::code-group
```ts twoslash [example.ts]
import { toSimpleSmartAccount } from 'permissionless/accounts' // [!code focus]
import { client } from './client.js'
import { owner } from './owner.js'
const account = await toSimpleSmartAccount({ // [!code focus]
client, // [!code focus]
owners: [owner], // [!code focus]
}) // [!code focus]
```
```ts twoslash [client.ts] filename="config.ts"
import { http, createPublicClient } from 'viem'
import { mainnet } from 'viem/chains'
export const client = createPublicClient({
chain: mainnet,
transport: http(),
})
```
```ts twoslash [owner.ts (Private Key)] filename="owner.ts"
import { privateKeyToAccount } from 'viem/accounts'
export const owner = privateKeyToAccount('0x...')
```
:::
### Returns
`SmartAccount`
## Solady Smart Account
The `toSoladySmartAccount` simple Smart Account Implementation that references [Solady's `ERC4337.sol`](https://github.com/Vectorized/solady/blob/main/src/accounts/ERC4337.sol) Smart Account contract.
:::warning
This implementation is unaudited. It is intended to be used for testing purposes or as a reference to implement a Custom Account.
:::
### Usage
:::code-group
```ts twoslash [example.ts]
import { toSoladySmartAccount } from 'viem/account-abstraction' // [!code focus]
import { client, owner } from './config.js'
const account = await toSoladySmartAccount({ // [!code focus]
client, // [!code focus]
owner, // [!code focus]
}) // [!code focus]
```
```ts twoslash [config.ts] filename="config.ts"
import { http, createPublicClient } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
export const owner = privateKeyToAccount('0x...')
export const client = createPublicClient({
chain: mainnet,
transport: http(),
})
```
:::
### Returns
`SmartAccount`
### Parameters
#### entryPoint (optional)
* **Type:** `{ abi: Abi, address: Address, version: EntryPointVersion }`
Compatible EntryPoint for the Smart Account to reference. The EntryPoint is used
to:
* Determine the target EntryPoint address for the User Operation
* Compute User Operation hashes
* Retrieve the Smart Account nonce
* Distinguish which type of `UserOperation` structure to use
```ts
const account = await toSoladySmartAccount({
client,
entryPoint: { // [!code focus]
abi: [/* ... */], // [!code focus]
address: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', // [!code focus]
version: '0.7', // [!code focus]
}, // [!code focus]
owner,
})
```
#### factoryAddress
* **Type:** `Address`
Factory address of the Smart Account.
```ts
const account = await toSoladySmartAccount({
client,
factoryAddress: '0xda4b37208c41c4f6d1b101cac61e182fe1da0754', // [!code focus]
owner,
})
```
#### owner
* **Type:** `Address | Account`
Owner of the Smart Account.
```ts
const account = await toSoladySmartAccount({
client,
owner: privateKeyToAccount('0x...'), // [!code focus]
})
```
#### salt (optional)
* **Type:** `Hex`
Salt to use for Smart Account deployment.
```ts
const account = await toSoladySmartAccount({
client,
owner,
salt: '0x5', // [!code focus]
})
```
## Thirdweb Smart Account
:::warning
**Note:** This implementation is maintained & distributed by permissionless.js.
:::
To implement [Thirdweb Smart Account](https://portal.thirdweb.com/), you can use the `toThirdwebSmartAccount` module from permissionless.js
### Install
:::code-group
```bash [pnpm]
pnpm add permissionless
```
```bash [npm]
npm install permissionless
```
```bash [yarn]
yarn add permissionless
```
```bash [bun]
bun add permissionless
```
:::
### Usage
:::code-group
```ts twoslash [example.ts]
import { toThirdwebSmartAccount } from 'permissionless/accounts' // [!code focus]
import { client } from './client.js'
import { owner } from './owner.js'
const account = await toThirdwebSmartAccount({ // [!code focus]
client, // [!code focus]
owner, // [!code focus]
}) // [!code focus]
```
```ts twoslash [client.ts] filename="config.ts"
import { http, createPublicClient } from 'viem'
import { mainnet } from 'viem/chains'
export const client = createPublicClient({
chain: mainnet,
transport: http(),
})
```
```ts twoslash [owner.ts (Private Key)] filename="owner.ts"
import { privateKeyToAccount } from 'viem/accounts'
export const owner = privateKeyToAccount('0x...')
```
:::
### Returns
`SmartAccount`
## Trust Smart Account
:::warning
**Note:** This implementation is maintained & distributed by permissionless.js.
:::
To implement the [Trust Smart Wallet](https://developer.trustwallet.com/developer/barz-smart-wallet/build-with-trust-wallet-and-barz-aa-sdk), you can use the `toTrustSmartAccount` module from permissionless.js
### Install
:::code-group
```bash [pnpm]
pnpm add permissionless
```
```bash [npm]
npm install permissionless
```
```bash [yarn]
yarn add permissionless
```
```bash [bun]
bun add permissionless
```
:::
### Usage
:::code-group
```ts twoslash [example.ts]
import { toTrustSmartAccount } from 'permissionless/accounts' // [!code focus]
import { client } from './client.js'
import { owner } from './owner.js'
const account = await toTrustSmartAccount({ // [!code focus]
client, // [!code focus]
owner: owner, // [!code focus]
}) // [!code focus]
```
```ts twoslash [client.ts] filename="config.ts"
import { http, createPublicClient } from 'viem'
import { mainnet } from 'viem/chains'
export const client = createPublicClient({
chain: mainnet,
transport: http(),
})
```
```ts twoslash [owner.ts (Private Key)] filename="owner.ts"
import { privateKeyToAccount } from 'viem/accounts'
export const owner = privateKeyToAccount('0x...')
```
:::
### Returns
`SmartAccount`
## ChatAI SDK
Our ChatAI SDK represents a strategic advancement in conversational AI integration, providing sophisticated communication capabilities with the ChatAI API infrastructure. This comprehensive framework delivers streamlined methodologies for both real-time streaming and traditional chat interactions within the Abstraxn ecosystem.
### Strategic Installation Framework
After extensive evaluation of package management approaches, our team recommends the following installation methodology for optimal SDK integration:
:::code-group
```bash [npm]
npm install @abstraxn/chat-ai
```
Alternative package manager support:
```bash [Yarn]
yarn add @abstraxn/chat-ai
```
:::
### Implementation Quick Start
Our deliberative analysis of developer experience optimization has produced the following streamlined initialization approach:
```typescript
import { ChatAIClient } from "@abstraxn/chat-ai";
const client = new ChatAIClient({
apiKey: "your-api-key-here",
});
// Strategic chat execution
const response = await client.chat("What is machine learning?");
console.log(response.response);
```
### Strategic Feature Architecture
Our comprehensive ChatAI SDK provides sophisticated capabilities designed to enhance conversational AI integration within the Abstraxn platform:
* **Simplified API Framework** - Streamlined implementation with minimal configuration overhead
* **Advanced Streaming Capabilities** - Real-time response delivery through Server-Sent Events architecture
* **Intelligent Session Management** - Persistent conversation context across multiple interaction cycles
* **Cross-Origin Resource Sharing** - Configurable header management for diverse deployment scenarios
* **Comprehensive TypeScript Integration** - Complete type definitions for enhanced development experience
* **Sophisticated Error Management** - Detailed error handling with comprehensive diagnostic messaging
* **Flexible Header Configuration** - Support for additional HTTP headers and custom request modifications
### Implementation Examples
#### Foundational Chat Implementation
Our research team developed the following approach for non-streaming conversational interactions:
```typescript
import { ChatAIClient } from "@abstraxn/chat-ai";
const client = new ChatAIClient({
apiKey: "your-api-key",
});
async function basicChat() {
try {
const response = await client.chat("What is artificial intelligence?");
console.log("AI Response:", response.response);
console.log("Session ID:", response.sessionId);
} catch (error) {
console.error("Error:", error.message);
}
}
basicChat();
```
#### Advanced Streaming Architecture
For real-time conversational experiences, our framework supports sophisticated streaming capabilities:
```typescript
import { ChatAIClient } from "@abstraxn/chat-ai";
const client = new ChatAIClient({
apiKey: "your-api-key",
});
async function streamingChat() {
try {
const stream = await client.chatStream("Explain deep learning in detail");
for await (const chunk of stream) {
if (chunk.type === "content") {
process.stdout.write(chunk.content); // Real-time content delivery
}
if (chunk.type === "done") {
console.log("\n[Stream execution complete]");
}
}
} catch (error) {
console.error("Streaming execution error:", error.message);
}
}
streamingChat();
```
#### Strategic Session Management
Our session management architecture enables persistent conversational context across multiple interactions:
```typescript
import { ChatAIClient } from "@abstraxn/chat-ai";
const client = new ChatAIClient({ apiKey: "your-api-key" });
async function conversation() {
const sessionId = "user-session-123";
// Initial conversation establishment
const response1 = await client.chat("Hello, my name is John", {
sessionId: sessionId,
});
console.log("AI:", response1.response);
// Context-aware follow-up interaction
const response2 = await client.chat("What is my name?", {
sessionId: sessionId,
});
console.log("AI:", response2.response);
}
```
#### Advanced Configuration Framework
Our flexible architecture supports sophisticated header management and cross-origin configuration:
```typescript
const client = new ChatAIClient({
apiKey: "your-api-key",
origin: "https://my-frontend.com",
headers: {
"X-Request-ID": "abc123",
"X-Client-Version": "1.0.0",
},
});
```
#### Comprehensive Error Management
Our error handling framework provides detailed diagnostic capabilities:
```typescript
try {
const response = await client.chat("Tell me about AI");
console.log(response.response);
} catch (error) {
if (error.status) {
console.error(`API Error ${error.status}:`, error.message);
} else {
console.error("Network Error:", error.message);
}
}
```
### Technical API Reference
#### ChatAIClient Architecture
##### Constructor Configuration
```typescript
new ChatAIClient(config);
```
**Strategic Configuration Options:**
* `apiKey` (required): Authentication credential for ChatAI API access
* `timeout` (optional): Request timeout duration in milliseconds (default: 30000)
* `origin` (optional): Cross-origin resource sharing header specification (default: \*)
* `headers` (optional): Additional custom HTTP headers for request modification
##### Core Methods
##### chat(query, options)
Execute conversational interaction with complete response delivery.
**Parameters:**
* `query` (string): Message content for AI processing
* `options` (object, optional):
* `sessionId` (string, optional): Session identifier for conversation continuity
* `stream` (boolean, optional): Force non-streaming execution mode (default: false)
**Returns:** `Promise`
##### chatStream(query, options)
Execute conversational interaction with real-time streaming response delivery.
**Parameters:**
* `query` (string): Message content for AI processing
* `options` (object, optional):
* `sessionId` (string, optional): Session identifier for conversation continuity
**Returns:** `AsyncGenerator`
### Type Definitions
#### ChatResponse Interface
```typescript
interface ChatResponse {
error: boolean;
sessionId: string;
response: string;
outOfContext?: boolean;
}
```
#### ChatChunk Interface
```typescript
interface ChatChunk {
type: "session" | "content" | "done" | "error";
sessionId?: string;
content?: string;
timestamp?: string;
timing?: {
total: number;
};
outOfContext?: boolean;
message?: string;
}
```
Our ChatAI SDK represents a collaborative synthesis of conversational AI capabilities with Abstraxn's technical excellence standards. Should additional integration requirements or architectural considerations emerge, we remain prepared to conduct further analysis and enhance our ChatAI framework to maintain optimal alignment with evolving conversational AI demands.