# Abstraxn | Documentation > Elevate your Web3 UX with AI-powered agents, supported by streamlined infra ## Abstraxn Overview Welcome to **Abstraxn**—the complete Web3 infrastructure platform that simplifies blockchain interactions for developers and users. From secure wallet integration and AI-powered agents to advanced account abstraction, we provide everything you need to build seamless Web3 experiences without the complexity. ### Wallet Integration Abstraxn provides powerful wallet infrastructure to onboard users seamlessly: * **Social Logins**: Let users sign in with Google, Twitter, Discord, GitHub, and more * **Email Authentication**: Simple email-based wallet creation and login * **Passkey Support**: Secure, passwordless authentication using WebAuthn * **Custom UI**: Fully customizable wallet interface to match your brand * **Smart Account Integration**: Built-in support for Safe, Kernel, ThirdWeb, and other smart account providers * **Multi-Chain Support**: Works across 100+ EVM-compatible chains Get started with our [Wallet Quickstart Guide](/guides/Wallets/Quickstart) to integrate wallets in minutes. ### The AI-First Experience * **Chat AI Agent**\ Embed a conversational interface that understands your documentation, whitepaper, FAQs—and lets users interact naturally.\ Answer questions, fetch context, and trigger on-chain actions—all through plain language. * **Agent Marketplace** *(coming soon)*\ Pick ready-made agents for tasks like trading, yield optimization, or NFT interactions. * **Agentic Framework** *(coming soon)*\ Build and orchestrate your own Web3 agents on top of our AI runtime. ### Supporting Web3 Infrastructure (Behind the Scenes) Our AI agents rely on a lean set of core infra capabilities—tools you used to offer, now seamlessly orchestrated: • **Wallets**\ Onboard users with secure, easy-to-use wallets that integrate seamlessly with your application. Support for social logins (Google, Twitter, Discord), email authentication, and passkeys. Customize the wallet UI to match your brand and provide a smooth onboarding experience. • **Relayer & Bundler**\ Batch and broadcast encrypted user operations securely via EIP-7702 flows. The bundler aggregates user operations into single on-chain transactions to optimize gas usage. • **Paymasters** * **Verifying Paymaster** — Sponsor transactions from an off-chain balance * **ERC-20 Paymaster** — Let users pay gas with tokens\ Sponsor transaction fees so users enjoy gas-less experiences with flexible fee models. • **Smart Accounts & Signers**\ Safe, Kernel, ThirdWeb, etc. integrated with Dynamic, Web3Auth, Privy and more. • **Plug & play SDK** for EIP-7702 / bundler / paymaster orchestration ### How It All Fits Your users see only the **AI layer**—a natural, intuitive experience. Everything else—including gas sponsorship, bundling, smart wallet logic—happens automatically behind the scenes. **Abstraxn = AI-driven UX + streamlined Web3 tooling**\ (no more forms, approvals, or juggling wallets) ### Documentation at a Glance | Section | Description | | --------------- | ---------------------------------------------------------------------- | | **Overview** | This AI-first introduction | | **Wallets** | Secure wallet integration with social logins, email auth, and passkeys | | **AI Stack** | Chat integration, Knowledge ingestion, Marketplace & Framework | | **Web3 Infra** | Bundler, Relayer, Paymasters, Smart Wallets, Signers, SDK usage | | **Quickstarts** | Code examples, sample apps, embedding guides | | **Reference** | Endpoints, SDK API, configuration details | | **Guides** | Best practices: security, performance, UX design | ### What's Next for Developers 1. **Set up Wallets**: Integrate secure wallet onboarding with social logins, email authentication, or passkeys. Customize the UI to match your brand and provide a seamless user experience. 2. **Start with AI**: Connect your docs/whitepaper → embed agent UI → test question-to-action flows. 3. **Extend with Infra**: Enable real on-chain actions—gasless swaps, staking, bridging—all powered behind the agent. 4. **Deepen with tools**: Use our SDK to customize agent behavior, account flows, or add new wallet interfaces. **Coming Soon**: Explore our Agent Marketplace and Agentic Framework to plug-and-play or build your own task-specific agents. ### Why This Matters * **Superior UX**: Users don't need to learn Web3. They express intent. * **Faster adoption**: Conversational interfaces reduce drop-offs and confusion. * **Developer-first**: One SDK. One integration. Infinite agent-driven experiences. ### Let's Build Conversational Web3 Start with the **Getting Started** guide next—and see how a single line of code + your docs can unlock intelligent, action-oriented Web3 interactions via Abstraxn. ## Pricing Plans Choose the tier that matches your use case—from testing automations to large-scale multi-agent deployments. | Plan | Price | Best For | Features Included | RPS | Monthly Requests | Support | Supported Chains | | -------------------------- | ------- | ---------------------------------------- | ----------------------------------------- | ----------- | ---------------- | ------------------------- | -------------------------- | | **Starter** | $99/mo | Test flows, small automations | Ask AI | 50 | 50,000 | Standard Support | — | | **Growth** | $399/mo | Production dApps with infra integration | Ask AI + Web3 Stack | 150 | 200,000 | Priority Support | Up to 3 chains | | **Scale** *(Most Popular)* | $699/mo | Multi-agent flows, higher throughput | Ask AI + Web3 Stack + Agentic Framework | 300 | 500,000 | Priority Support | Up to 10 chains | | **Enterprise** | Custom | High-scale, custom logic, agent training | Everything in Scale + custom enhancements | 2.5× faster | Custom | Dedicated Support Channel | Choose from 50+ EVM chains | *** ### Plan Details #### Starter – $99/month Ideal for prototyping automation flows or basic AI interactions. Includes 50 RPS with up to 50K monthly requests and standard support. #### Growth – $399/month Perfect for production-grade dApps. Adds bundled Web3 tooling alongside AI. Handles 150 RPS and 200K requests/month with priority support and coverage for up to **3 chains**. #### Scale – $699/month *(Most Popular)* Best for growing applications needing multi-agent orchestration and advanced use. Supports 300 RPS and 500K requests/month, Agentic Framework access, and up to **10 chains**. #### Enterprise – Custom Pricing Tailored for large-scale deployments, custom logic, SLAs, and agent training. Unlimited throughput, 2.5× faster execution, dedicated support, and access to **50+ EVM chains**. Contact us for a quote. *** ### Why Abstraxn Licensing Makes Sense * **Transparent quotas** by RPS and monthly volume—no hidden token meters * **Modular upgrade path**—start small on Starter → scale up to Growth, Scale, or Enterprise * **Multi-chain support** scales with your needs—from none to +50 chains * **Built for usage growth**—your investment directly maps to your live usage and user impact *** ### Ready to Get Started? Set up your plan in minutes or **upgrade seamlessly** as your traffic grows. Have custom needs or targeting large-scale adoption? Contact us to get set up with the **Enterprise** plan today: [Contact Sales](https://www.abstraxn.com/contact-us/) *** ### TL;DR * **Starter** — $99/mo → ask questions, test functionality * **Growth** — $399/mo → AI + Web3 stack in production * **Scale** — $699/mo → Multi-agent + infra orchestration * **Enterprise** → Custom → Unlimited scale, custom features, multi-chain support All plans include our AI-first UX layer. Higher tiers unlock deeper infra tooling and chaining capabilities. ## Abstraxn Relayer SDK The **@abstraxn/relayer** package empowers your dApp to transform user intent into on-chain execution—**gaslessly**, **securely**, and with **real-time updates**. *** ### Installation You can install our SDK by running the following command in your terminal: :::code-group ```bash [npm] npm install @abstraxn/relayer@latest ``` ```bash [Yarn] yarn add @abstraxn/relayer@latest ``` ::: *** ### Setup & Configuration To obtain the Relayer URL and API Key: To begin, you’ll need a Relayer URL and API Key. Retrieve these from your Abstraxn Dashboard: * 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 Relayer → click Add Relayer, assign a name, select app * Click View Details → copy your Relayer URL and API Key :::warning ⚠️ **Warning:** Keep your API key secret. Store it in environment variables on the server side — never embed it into public client code. ::: *** ### Basic Integration (Node / Server) ```js import { Relayer } from "@abstraxn/relayer"; import { ChainId } from "@abstraxn/core-types"; const relayerConfig = { relayerUrl: "https://your-relayer-url.com", chainId: ChainId.MAINNET, signer: yourSignerInstance, provider: yourProviderInstance, }; const relayer = new Relayer(relayerConfig); // Example: Building a relayer transaction const buildTxParams = { contractAddress: "0xYourContractAddress", abi: yourContractAbi, method: "yourMethodName", args: ["arg1", "arg2"], }; // Build and send transaction to relayer relayer.buildRelayerTx(buildTxParams).then(async (txData) => { console.log("Build Relayer Transaction Response: ", txData); const response = await relayer.sendRelayerTx(txData); // response = {message: string; transactionId: string; } const status = await relayer.getRelayerTxStatus(response.transactionId); return status; // status = {hash: string | null;data: string | null;status: string;receipt: TransactionReceipt | null;createdAt: string;reason?: string | null;} }); ``` *** ### Real-Time WebSocket Integration Real-time updates (v0.1.5+). Server or client can subscribe to live events for instant UX. ```js const relayer = new Relayer({ relayerUrl: process.env.Abstraxn_URL, apiKey: process.env.Abstraxn_KEY, chainId: ChainId.MAINNET, signer, provider, webSocket: { enabled: true, autoConnect: true, reconnection: true, }, }); const txData = await relayer.buildRelayerTx(params); const resp = await relayer.sendRelayerTxWithRealTimeUpdates({ ...txData, enableRealTimeUpdates: true, webSocketEvents: { onTransactionUpdate: (update) => { console.log("TX Update:", update); }, onError: (err) => console.error("WS Error:", err), }, }); console.log("Relayer transaction submitted:", resp.transactionId); ``` Status flow moves: initiated → pending → confirmed / failed / rejected *** ### Safe Transaction Execution Execute Safe transactions with multiple owners using the Relayer SDK. This example demonstrates how to build a Safe transaction, collect signatures from multiple owners, and execute it through the relayer with real-time WebSocket updates. ```tsx import { useState } from "react"; import { ethers } from "ethers"; import { Relayer } from "@abstraxn/relayer"; /* ================= CONFIG ================= */ const RPC_URL = ""; const SAFE_ADDRESS = ""; const TARGET = ""; const CHAIN_ID = 137n; const OWNER1_PK = ""; const OWNER2_PK = ""; /* ================= SAFE ABI ================= */ const SAFE_ABI = [ "function nonce() view returns (uint256)", "function execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes) payable returns (bool)", ]; /* ================= TYPES ================= */ type SafeTx = { to: string; value: bigint; data: string; operation: number; safeTxGas: bigint; baseGas: bigint; gasPrice: bigint; gasToken: string; refundReceiver: string; nonce: bigint; }; type SignatureItem = { signer: string; signature: string; }; /* ================= EIP-712 TYPE ================= */ type TypedDataField = { name: string; type: string; }; const SAFE_TX_TYPE: { SafeTx: TypedDataField[]; } = { SafeTx: [ { name: "to", type: "address" }, { name: "value", type: "uint256" }, { name: "data", type: "bytes" }, { name: "operation", type: "uint8" }, { name: "safeTxGas", type: "uint256" }, { name: "baseGas", type: "uint256" }, { name: "gasPrice", type: "uint256" }, { name: "gasToken", type: "address" }, { name: "refundReceiver", type: "address" }, { name: "nonce", type: "uint256" }, ], } as const; /* ================= HELPERS ================= */ function sortSignatures(sigs: SignatureItem[]): string[] { return sigs .sort((a, b) => a.signer.toLowerCase().localeCompare(b.signer.toLowerCase()), ) .map((s) => s.signature); } function packSignatures(signatures: string[]): string { return "0x" + signatures.map((sig) => sig.slice(2)).join(""); } /* ================= APP ================= */ export default function SafeTx() { const [txHash, setTxHash] = useState(""); const [loading, setLoading] = useState(false); const executeMetaTx = async (): Promise => { try { setLoading(true); /* ---------- Provider & wallets ---------- */ const provider = new ethers.JsonRpcProvider(RPC_URL); const owner1 = new ethers.Wallet(OWNER1_PK); const owner2 = new ethers.Wallet(OWNER2_PK); const safe = new ethers.Contract(SAFE_ADDRESS, SAFE_ABI, provider); console.log("safe", safe); /* ---------- Build SafeTx ---------- */ const nonce: bigint = await safe.nonce(); console.log("nonce", nonce); const safeTx: SafeTx = { to: TARGET, value: 0n, data: "0x", operation: 0, // CALL safeTxGas: 0n, baseGas: 0n, gasPrice: 0n, gasToken: ethers.ZeroAddress, refundReceiver: ethers.ZeroAddress, nonce, }; const domain = { chainId: CHAIN_ID, verifyingContract: SAFE_ADDRESS, }; /* ---------- Owners sign ---------- */ const sig1: string = await owner1.signTypedData( domain, SAFE_TX_TYPE, safeTx, ); const sig2: string = await owner2.signTypedData( domain, SAFE_TX_TYPE, safeTx, ); /* ---------- Sort & pack ---------- */ const ordered = sortSignatures([ { signer: await owner1.getAddress(), signature: sig1 }, { signer: await owner2.getAddress(), signature: sig2 }, ]); const signatureBytes: string = packSignatures(ordered); console.log("signatureBytes", signatureBytes); /* ---------- Execute via relayer ---------- */ const relayerConfig = { relayerUrl: "", isSafeTx: true, webSocket: { enabled: true, autoConnect: true, reconnection: true, }, }; const relayer = new Relayer(relayerConfig); await relayer.sendSafeRelayerTxWithRealTimeUpdates({ safeAddress: SAFE_ADDRESS, safeExecTxPayload: { safeTx: safeTx, signatureBytes: signatureBytes, }, chainId: Number(CHAIN_ID), enableRealTimeUpdates: true, webSocketEvents: { onTransactionUpdate: (update) => { console.log("update", update); console.log(`Transaction ${update.txId} status: ${update.status}`); // Set transaction hash when available if (update.hash) { setTxHash(update.hash); } if (update.status === "confirmed") { console.log("🎉 Transaction confirmed!", update.blockNumber); } else if ( update.status === "failed" || update.status === "rejected" ) { console.error("Transaction failed:", update.reason); alert(`Transaction failed: ${update.reason || "Unknown error"}`); } }, onError: (error) => { console.error("WebSocket error:", error); }, }, }); } catch (err) { console.error(err); alert("Meta-transaction failed — see console"); } finally { setLoading(false); } }; return (

Safe Manual Meta-Tx (2 Owners, TS)

{txHash && (

Tx Hash:
{txHash}

)}
); } ``` #### 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. Abstraxn Smart Wallets React Quickstart preview > 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: Initial app screen with 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: OTP verification code entry 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: Connected wallet interface showing status, address, and action buttons 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 Abstraxn wallet modal with full wallet management interface ### 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

Not connected

; } return (

Connected: {address}

); } ``` ### Sending Transactions For embedded-wallet EVM transactions, use: `usePrepareRawTxn` -> `useSignAndSendTxn` -> `useWaitForTxnReceipt` ```tsx "use client"; import { useState } from "react"; import { usePrepareRawTxn, useSignAndSendTxn, useWaitForTxnReceipt, usePublicClient, useEstimateGas, useGetGasPrice, useIsConnected, useAddress, } from "@abstraxn/signer-react"; import { polygonAmoy } from "viem/chains"; import { parseEther } from "viem"; export function SendTransaction() { const address = useAddress(); const isConnected = useIsConnected(); 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 [loading, setLoading] = useState(false); const [txHash, setTxHash] = useState<`0x${string}` | null>(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 { 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, }, }); setTxHash(result.hash as `0x${string}`); await waitForTxnReceipt({ hash: result.hash as `0x${string}`, confirmations: 1, }); } catch (error) { console.error("Transaction failed:", error); } finally { setLoading(false); } }; return (
{txHash &&

Last tx hash: {txHash}

}
); } ``` ### 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 Email Login customization settings page #### 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 Social Login configuration page showing Google, X (Twitter), and Discord providers #### 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 Passkey authentication settings page #### 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 Security settings page showing Origin Whitelist configuration #### 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. Apps page with Create New App button 3. Click the **Create New App** button to open the creation modal Create New App modal form 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) Wallets page with Add Wallets button 5. Click **Add Wallets** button to create a new wallet service Create New Wallet modal form 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. Wallet details page showing API Key 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 (
{address ? `Address: ${address}` : 'No address available'}
); } ``` **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
Not connected
; } return (

Connected via: {connectorMeta?.name || connectionType}

{connectionType === 'google' &&
Welcome Google user!
} {connectionType === 'metamask' &&
MetaMask wallet connected
} {connectionType === 'email' &&
Email OTP user
} {connectionType === 'passkey' &&
Passkey user
}
); } ``` **Returns:** * `connectionType`: `ConnectorType | null` - The connection method ('google', 'x', 'discord', 'metamask', 'walletconnect', 'email', 'passkey', etc.) * `connectorMeta`: Connector metadata with name, icon, etc. *** ### Authentication Hooks #### `useAuthContext()` Get current user and whoami information. ```tsx import { useAuthContext } from '@abstraxn/signer-react'; function MyComponent() { const { user, whoami } = useAuthContext(); return (
{user &&

User ID: {user.id}

} {whoami && (

EVM Address: {whoami.evmAddress}

Solana Address: {whoami.solanaAddress}

)}
); } ``` **Returns:** * `user`: User object * `whoami`: Whoami response with address information *** #### `useWhoami()` Call whoami API with loading state and refresh capability. ```tsx import { useWhoami } from '@abstraxn/signer-react'; import { useEffect } from 'react'; function MyComponent() { const { whoami, loading, error, refreshWhoami, isConnected } = useWhoami(); useEffect(() => { if (isConnected) { refreshWhoami(); } }, [isConnected, refreshWhoami]); if (loading) return
Loading...
; if (error) return
Error: {error.message}
; if (!whoami) return
No whoami data
; return (

EVM Address: {whoami.evmAddress}

Solana Address: {whoami.solanaAddress}

); } ``` **Returns:** * `whoami`: `WhoamiResponse | null` - Whoami data * `loading`: `boolean` - Loading state * `error`: `Error | null` - Error state * `refreshWhoami`: Function to refresh whoami data * `isConnected`: `boolean` - Connection status *** #### `useExportWallet()` Export wallet private key. ```tsx import { useExportWallet } from '@abstraxn/signer-react'; function MyComponent() { const { exportWallet, isConnected, address } = useExportWallet(); const handleExport = async () => { try { const result = await exportWallet( 'targetPublicKey', // Target public key for encryption 'privateKey', // Your private key for encryption 'evm' // or 'solana' ); console.log('Export successful:', result); } catch (error) { console.error('Export failed:', error); } }; if (!isConnected) { return
Please connect your wallet
; } return ( ); } ``` **Returns:** * `exportWallet`: Function to export wallet * `isConnected`: `boolean` - Connection status * `address`: `string | null` - Wallet address *** ### Transaction Hooks #### `usePublicClient(chain, rpcUrl)` Create a publicClient using viem for read operations. ```tsx import { usePublicClient } from '@abstraxn/signer-react'; import { polygonAmoy } from 'viem/chains'; function MyComponent() { const { publicClient } = usePublicClient( polygonAmoy, 'https://rpc-amoy.polygon.technology' ); const getBalance = async (address: string) => { const balance = await publicClient.getBalance({ address: address as `0x${string}` }); return balance; }; return ( ); } ``` **Parameters:** * `chain`: Viem chain object (from 'viem/chains' or custom) * `rpcUrl`: RPC URL for the chain **Returns:** * `publicClient`: PublicClient instance from viem *** #### `useWalletClient(chain, rpcUrl?)` Create a walletClient using viem for broadcasting signed transactions. ```tsx import { useWalletClient } from '@abstraxn/signer-react'; import { polygonAmoy } from 'viem/chains'; function MyComponent() { const { walletClient } = useWalletClient( polygonAmoy, 'https://rpc-amoy.polygon.technology' ); const broadcastTransaction = async (signedTx: string) => { const hash = await walletClient.sendRawTransaction({ serializedTransaction: signedTx as `0x${string}`, }); return hash; }; return ( ); } ``` **Parameters:** * `chain`: Viem chain object * `rpcUrl`: Optional RPC URL (uses chain's default if not provided) **Returns:** * `walletClient`: WalletClient instance from viem *** #### `usePrepareRawTxn(provider)` Prepare raw transaction data with encoding support. ```tsx import { usePrepareRawTxn, usePublicClient, useAddress } from '@abstraxn/signer-react'; import { polygonAmoy } from 'viem/chains'; function MyComponent() { const address = useAddress(); const { publicClient } = usePublicClient(polygonAmoy, 'https://rpc-amoy.polygon.technology'); const { prepareRawTxn } = usePrepareRawTxn(publicClient); // Native transfer const handleNativeTransfer = async () => { const nativeTx = await prepareRawTxn({ from: address!, to: '0x...', value: '0.001', // ETH amount }); // Returns: { to: '0x...', value: '0x...', data: '0x' } console.log(nativeTx); }; // Contract call with encoding const handleContractCall = async () => { const contractTx = await prepareRawTxn({ from: address!, to: '0x...', // Contract address abi: erc20Abi, functionName: 'transfer', args: ['0x...', 1000000n], // Pass values in the expected on-chain format value: '0', // Optional ETH value }); // Returns: { to: '0x...', value: '0x', data: '0xa9059cbb...' } console.log(contractTx); }; // Contract call with pre-encoded data const handlePreEncoded = async () => { const contractTx = await prepareRawTxn({ from: address!, to: '0x...', data: '0xa9059cbb...', // Already encoded }); console.log(contractTx); }; return (
); } ``` **Parameters:** * `provider`: PublicClient instance **Returns:** * `prepareRawTxn`: Function that accepts: * `from`: Address * `to`: Address * `value?`: Native token value (pass in chain/base units, e.g. wei) * `data?`: Pre-encoded function data * `abi?`: Contract ABI (for encoding) * `functionName?`: Function name (for encoding) * `args?`: Function arguments (passed through as-is; no implicit amount conversion) *** #### `useSignTxn(provider)` Sign transaction using Turnkey API. ```tsx import { useSignTxn, usePrepareRawTxn, usePublicClient, useAddress } from '@abstraxn/signer-react'; import { polygonAmoy } from 'viem/chains'; function MyComponent() { const address = useAddress(); const { publicClient } = usePublicClient(polygonAmoy, 'https://rpc-amoy.polygon.technology'); const { prepareRawTxn } = usePrepareRawTxn(publicClient); const { signTxn, isConnected } = useSignTxn(publicClient); const handleSign = async () => { // Prepare transaction const rawTx = await prepareRawTxn({ from: address!, to: '0x...', value: '0.001', }); // Sign transaction const signedTx = await signTxn({ from: address!, ...rawTx, // Spread to, value, data from prepareRawTxn }); console.log('Signed transaction:', signedTx.signedTransaction); }; if (!isConnected) { return
Please connect your wallet
; } return ; } ``` **Parameters:** * `provider`: PublicClient instance **Returns:** * `signTxn`: Function to sign transaction * `isConnected`: `boolean` - Connection status * `address`: `string | null` - Wallet address *** #### `useSignAndSendTxn(provider)` Sign and send transaction using Turnkey API. ```tsx import { useSignAndSendTxn, usePrepareRawTxn, usePublicClient, useAddress } from '@abstraxn/signer-react'; import { polygonAmoy } from 'viem/chains'; function MyComponent() { const address = useAddress(); const { publicClient } = usePublicClient(polygonAmoy, 'https://rpc-amoy.polygon.technology'); const { prepareRawTxn } = usePrepareRawTxn(publicClient); const { signAndSendTxn, isConnected } = useSignAndSendTxn(publicClient); const handleSend = async () => { // Prepare transaction const rawTx = await prepareRawTxn({ from: address!, to: '0x...', value: '0.001', }); // Sign and send transaction const result = await signAndSendTxn({ from: address!, ...rawTx, // Spread to, value, data from prepareRawTxn }); console.log('Transaction hash:', result.hash); }; if (!isConnected) { return
Please connect your wallet
; } return ; } ``` **Parameters:** * `provider`: PublicClient instance **Returns:** * `signAndSendTxn`: Function to sign and send transaction * `isConnected`: `boolean` - Connection status * `address`: `string | null` - Wallet address *** #### `useWaitForTxnReceipt(provider)` Wait for transaction receipt. ```tsx import { useWaitForTxnReceipt, usePublicClient } from '@abstraxn/signer-react'; import { polygonAmoy } from 'viem/chains'; function MyComponent() { const { publicClient } = usePublicClient(polygonAmoy, 'https://rpc-amoy.polygon.technology'); const { waitForTxnReceipt } = useWaitForTxnReceipt(publicClient); const handleWait = async (txHash: string) => { try { const receipt = await waitForTxnReceipt({ hash: txHash as `0x${string}`, confirmations: 1, // Optional: number of confirmations }); console.log('Transaction confirmed:', receipt); console.log('Status:', receipt.status); } catch (error) { console.error('Failed to wait for receipt:', error); } }; return ( ); } ``` **Parameters:** * `provider`: PublicClient instance **Returns:** * `waitForTxnReceipt`: Function that accepts: * `hash`: Transaction hash * `confirmations?`: Number of confirmations to wait for * `timeout?`: Timeout in milliseconds *** #### `useSwitchChain()` Switch chain for both internal (social) and external wallets. ```tsx import { useSwitchChain } from '@abstraxn/signer-react'; function MyComponent() { const { switchChain } = useSwitchChain(); const handleSwitch = async () => { try { await switchChain(137); // Switch to Polygon console.log('Chain switched successfully'); } catch (error) { console.error('Failed to switch chain:', error); } }; return ; } ``` **Returns:** * `switchChain`: Function to switch chain (accepts `chainId: number`) *** #### `useSignMessage()` Sign a message (for external wallets only). ```tsx import { useSignMessage } from '@abstraxn/signer-react'; function MyComponent() { const { signMessage } = useSignMessage(); const handleSign = async () => { try { const signature = await signMessage('Hello World'); console.log('Signature:', signature); } catch (error) { console.error('Failed to sign message:', error); } }; return ; } ``` :::warning **Note:** Signing messages is not supported for embedded wallets (Google, email, etc.). Only works with external wallets. ::: **Returns:** * `signMessage`: Function to sign message (accepts `message: string`) *** ### Contract Interaction Hooks #### `useContract({ address, abi, provider })` Create a contract instance using viem. ```tsx import { useContract, usePublicClient } from '@abstraxn/signer-react'; import { polygonAmoy } from 'viem/chains'; import erc20Abi from './erc20Abi.json'; function MyComponent() { const { publicClient } = usePublicClient(polygonAmoy, 'https://rpc-amoy.polygon.technology'); const { contract } = useContract({ address: '0x...', abi: erc20Abi, provider: publicClient, }); const readBalance = async (address: string) => { const balance = await contract.read.balanceOf([address]); return balance; }; return ( ); } ``` **Parameters:** * `address`: Contract address * `abi`: Contract ABI * `provider`: PublicClient instance **Returns:** * `contract`: Contract instance from viem *** #### `useReadContract(provider)` Read from contract using publicClient. ```tsx import { useReadContract, usePublicClient } from '@abstraxn/signer-react'; import { polygonAmoy } from 'viem/chains'; import erc20Abi from './erc20Abi.json'; function MyComponent() { const { publicClient } = usePublicClient(polygonAmoy, 'https://rpc-amoy.polygon.technology'); const { readContract } = useReadContract(publicClient); const readBalance = async () => { try { const balance = await readContract({ address: '0x...', abi: erc20Abi, functionName: 'balanceOf', args: ['0x...'], }); console.log('Balance:', balance); } catch (error) { console.error('Failed to read contract:', error); } }; const readName = async () => { const name = await readContract({ address: '0x...', abi: erc20Abi, functionName: 'name', }); console.log('Name:', name); }; return (
); } ``` **Parameters:** * `provider`: PublicClient instance **Returns:** * `readContract`: Function that accepts: * `address`: Contract address * `abi`: Contract ABI * `functionName`: Function name * `args?`: Function arguments *** #### `useWriteContract()` Write to contract (for external wallets like MetaMask, WalletConnect, etc.). ```tsx import { useWriteContract } from '@abstraxn/signer-react'; import { parseEther } from 'viem'; import erc20Abi from './erc20Abi.json'; function MyComponent() { const { writeContract } = useWriteContract(); const handleTransfer = async () => { try { // Write to contract const hash = await writeContract({ address: '0x...', // Contract address abi: erc20Abi, functionName: 'transfer', args: ['0x...', '1000000000000000000'], // to, amount in wei }); console.log('Transaction hash:', hash); } catch (error) { console.error('Transaction failed:', error); } }; const handleDeposit = async () => { try { // With ETH value const hash = await writeContract({ address: '0x...', abi: contractAbi, functionName: 'deposit', value: parseEther('0.1'), // Send 0.1 ETH with the transaction }); console.log('Transaction hash:', hash); } catch (error) { console.error('Transaction failed:', error); } }; return (
); } ``` **Returns:** * `writeContract`: Function that accepts: * `address`: Contract address * `abi`: Contract ABI * `functionName`: Function name * `args?`: Function arguments * `value?`: ETH value (bigint) * `account?`: Account address * `gas?`: Gas limit * `gasPrice?`: Gas price * `maxFeePerGas?`: Max fee per gas * `maxPriorityFeePerGas?`: Max priority fee per gas * `nonce?`: Nonce *** ### MFA Hooks #### `useEnableMfa()` Manage the MFA enable setup flow: * `start()` initializes setup and returns QR + secret * `verifyCode(code)` verifies TOTP setup * `verifyRecoveryCode(code)` validates backup/recovery code * `setupState` moves through `setup` -> `verify` -> `complete` ```tsx import { useEnableMfa } from '@abstraxn/signer-react'; function EnableMfaCard() { const { setupState, start, verifyCode, isLoading, error } = useEnableMfa(); return (
{setupState?.step === 'setup' && MFA QR} {setupState?.step === 'verify' && ( )} {isLoading &&

Loading...

} {error &&

Error: {error}

}
); } ``` #### `useDisableMfa()` Manage MFA disable flow: * `start()` begins auth step * `switchMethod('otp' | 'recovery')` selects auth mode * `verifyCode(code)` verifies OTP or backup code * `confirmDisable()` disables MFA after verification ```tsx import { useDisableMfa } from '@abstraxn/signer-react'; function DisableMfaCard() { const { start, verifyCode, confirmDisable, disableState } = useDisableMfa(); return (
{!disableState && } {disableState?.step === 'auth' && ( )} {disableState?.step === 'confirm' && ( )}
); } ``` #### `useEmailForOtp()` Pre-fill the onboarding email field for OTP login. ```tsx import { useEmailForOtp } from '@abstraxn/signer-react'; function PrefillEmailButton() { const { emailForOtp, setEmailForOtp } = useEmailForOtp(); return ( ); } ``` *** ### Typed Signing #### `useSignTypedTx()` Sign EIP-712 payloads with embedded wallets. ```tsx import { useAddress, useSignTypedTx } from '@abstraxn/signer-react'; function SignTypedDataExample() { const address = useAddress(); const { signTypedTx } = useSignTypedTx(); const sign = async () => { if (!address) return; const result = await signTypedTx({ from: address as `0x${string}`, domain: { name: 'Demo', version: '1', chainId: 80002 }, primaryType: 'Mail', types: { Mail: [ { name: 'to', type: 'address' }, { name: 'contents', type: 'string' }, ], }, message: { to: address, contents: 'hello', }, encoding: 'PAYLOAD_ENCODING_EIP712', hashFunction: 'HASH_FUNCTION_KECCAK256', }); console.log(result.signature); }; return ; } ``` *** ### External Wallet Client Hook #### `useExternalWalletClient()` Access the underlying external wallet client (wagmi connector client) and connection state. ```tsx import { useExternalWalletClient } from '@abstraxn/signer-react'; function ExternalWalletClientState() { const { walletClient, isConnected, isPending, isError, error } = useExternalWalletClient(); return (

Connected: {String(isConnected)}

Pending: {String(isPending)}

Error: {isError ? error?.message : 'none'}

Client ready: {String(!!walletClient)}

); } ``` *** ### Solana Hooks #### `useSolanaConnection(rpcUrl, commitment?)` Create a Solana RPC connection. #### `useSolanaPublicKey()` Get the current Solana address from authenticated user context. #### `useSolanaBalance()` Load Solana balance (`lamports`, `formattedBalance`, loading/error). #### `usePrepareSolanaTxn(connection)` Prepare transfer or instruction-based transactions. #### `useSignSolanaTxn(connection)` Sign prepared Solana transaction through SDK signing API. #### `useSignAndSendSolanaTxn(connection)` Sign and send prepared Solana transaction in one call. #### `useWaitForSolanaConfirmation(connection)` Wait for signature confirmation on Solana. #### `usePrepareSolanaProgramTxn(connection)` and `useSolanaProgramTransaction(connection)` Prepare and send program transactions with `programId`, `accounts`, and encoded instruction data. #### `useSolanaExternalWallet()` Interact with connected external Solana wallets (sign message, sign tx, send tx). ```tsx import { useSolanaConnection, usePrepareSolanaTxn, useSignAndSendSolanaTxn, useWaitForSolanaConfirmation, useSolanaPublicKey, } from '@abstraxn/signer-react'; function SolanaSendExample() { const { solanaAddress } = useSolanaPublicKey(); const { connection } = useSolanaConnection('https://api.devnet.solana.com'); const { prepareSolanaTransfer } = usePrepareSolanaTxn(connection); const { signAndSendSolanaTxn } = useSignAndSendSolanaTxn(connection); const { waitForSolanaConfirmation } = useWaitForSolanaConfirmation(connection); const send = async () => { if (!solanaAddress) return; const { transaction } = await prepareSolanaTransfer({ fromPubkey: solanaAddress, toPubkey: '9xQeWvG816bUx9EPfEZ9PzWQw4j6wVx6U6z9ER6Q9Y2Z', amountInSol: 0.01, }); const sent = await signAndSendSolanaTxn({ transaction, fromPubkey: solanaAddress }); await waitForSolanaConfirmation({ signature: sent.signature }); }; return ; } ``` *** ### Complete Transaction Flow Example Here's a complete example showing how to use multiple hooks together for a full transaction flow: ```tsx import { useAddress, usePublicClient, usePrepareRawTxn, useSignAndSendTxn, useWaitForTxnReceipt } from '@abstraxn/signer-react'; import { polygonAmoy } from 'viem/chains'; import { useState } from 'react'; function SendTransactionComponent() { const address = useAddress(); const { publicClient } = usePublicClient( polygonAmoy, 'https://rpc-amoy.polygon.technology' ); const { prepareRawTxn } = usePrepareRawTxn(publicClient); const { signAndSendTxn, isConnected } = useSignAndSendTxn(publicClient); const { waitForTxnReceipt } = useWaitForTxnReceipt(publicClient); const [loading, setLoading] = useState(false); const [txHash, setTxHash] = useState(null); const [receipt, setReceipt] = useState(null); const handleSendTransaction = async () => { if (!address || !isConnected) { alert('Please connect your wallet'); return; } setLoading(true); try { // Step 1: Prepare transaction const rawTx = await prepareRawTxn({ from: address, to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', // Example recipient value: '0.001', // Send 0.001 ETH }); // Step 2: Sign and send transaction const result = await signAndSendTxn({ from: address, ...rawTx, }); setTxHash(result.hash); // Step 3: Wait for receipt const txReceipt = await waitForTxnReceipt({ hash: result.hash, confirmations: 1, }); setReceipt(txReceipt); console.log('Transaction confirmed:', txReceipt); } catch (error) { console.error('Transaction failed:', error); alert(`Transaction failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } finally { setLoading(false); } }; if (!isConnected) { return
Please connect your wallet
; } return (
{txHash && (

Transaction Hash: {txHash}

View on PolygonScan
)} {receipt && (

Status: {receipt.status === 'success' ? 'Success' : 'Failed'}

Block Number: {receipt.blockNumber.toString()}

)}
); } ``` *** ### 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

MFA setup is not available for this session.

; } return (
{!setupState && } {setupState?.step === "setup" && (
MFA QR code

Secret: {setupState.secret}

)} {setupState?.step === "verify" && ( )} {setupState?.step === "complete" && (

MFA enabled.

Backup codes: {setupState.backupCodes.join(", ")}

)} {isLoading &&

Working...

} {error &&

Error: {error}

}
); } ``` ### Disable MFA Flow Use OTP or recovery code to authenticate, then call `confirmDisable()`. ```tsx import { useDisableMfa } from "@abstraxn/signer-react"; export function DisableMfaPanel() { const { canDisableMfaFlow, disableState, start, switchMethod, verifyCode, confirmDisable, isLoading, error, } = useDisableMfa(); if (!canDisableMfaFlow) return null; return (
{!disableState && } {disableState?.step === "auth" && ( <> )} {disableState?.step === "confirm" && ( )} {isLoading &&

Working...

} {error &&

Error: {error}

}
); } ``` ### 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)

); } ``` ### Embedded Wallet Transfer For embedded Solana wallets: `usePrepareSolanaTxn` -> `useSignAndSendSolanaTxn` -> `useWaitForSolanaConfirmation` ```tsx import { useSolanaConnection, usePrepareSolanaTxn, useSignAndSendSolanaTxn, useWaitForSolanaConfirmation, useSolanaPublicKey, } from "@abstraxn/signer-react"; export function SendSolTransfer() { const { solanaAddress } = useSolanaPublicKey(); const { connection } = useSolanaConnection("https://api.devnet.solana.com"); const { prepareSolanaTransfer } = usePrepareSolanaTxn(connection); const { signAndSendSolanaTxn } = useSignAndSendSolanaTxn(connection); const { waitForSolanaConfirmation } = useWaitForSolanaConfirmation(connection); const handleSend = async () => { if (!solanaAddress) return; const { transaction } = await prepareSolanaTransfer({ fromPubkey: solanaAddress, toPubkey: "9xQeWvG816bUx9EPfEZ9PzWQw4j6wVx6U6z9ER6Q9Y2Z", amountInSol: 0.01, }); const sent = await signAndSendSolanaTxn({ transaction, fromPubkey: solanaAddress, }); await waitForSolanaConfirmation({ signature: sent.signature, commitment: "confirmed", }); }; return ; } ``` ### Program Transaction Flow Use program hooks when you already have `programId`, `accounts`, and encoded instruction data. ```tsx import { useSolanaConnection, usePrepareSolanaProgramTxn, useSolanaProgramTransaction, useSolanaPublicKey, } from "@abstraxn/signer-react"; export function ProgramTxExample() { const { solanaAddress } = useSolanaPublicKey(); const { connection } = useSolanaConnection("https://api.devnet.solana.com"); const { prepareSolanaProgramTxn } = usePrepareSolanaProgramTxn(connection); const { sendProgramTransaction } = useSolanaProgramTransaction(connection); const run = async () => { if (!solanaAddress) return; const { transaction } = await prepareSolanaProgramTxn({ feePayer: solanaAddress, programId: "11111111111111111111111111111111", accounts: [{ pubkey: solanaAddress, isSigner: true, isWritable: true }], data: "0x01", }); console.log("Prepared tx", transaction); await sendProgramTransaction({ feePayer: solanaAddress, programId: "11111111111111111111111111111111", accounts: [{ pubkey: solanaAddress, isSigner: true, isWritable: true }], data: "0x01", }); }; return ; } ``` ### External Solana Wallet Use `useSolanaExternalWallet` for Phantom/Solflare style flows: ```tsx import { useSolanaExternalWallet } from "@abstraxn/signer-react"; export function SolanaExternalWalletPanel() { const { isConnected, publicKey, signMessage } = useSolanaExternalWallet(); const sign = async () => { const sig = await signMessage("Hello Solana"); console.log("Message signature bytes:", sig); }; if (!isConnected) return

Connect a Solana wallet first.

; return (

Connected: {publicKey}

); } ``` ### Next Steps * Add wallet UX in \[App Integration]\(./App Integration) * Learn signing hardening in [MFA](./MFA) * Review full hook signatures in [Hooks](./Hooks) ## UI Customization > Learn how to customize the login UI for smart wallets The Abstraxn SDK allows you to customize the onboarding UI through the `ui` configuration object in your `AbstraxnProvider` config. All customization is done in one place - no additional configuration files needed. ### Basic UI Configuration Customize the UI by adding a `ui` property to your `AbstraxnProvider` configuration: ```tsx // app/layout.tsx or pages/_app.tsx import { AbstraxnProvider, type AbstraxnProviderConfig, } from "@abstraxn/signer-react"; const providerConfig: AbstraxnProviderConfig = { apiKey: process.env.NEXT_PUBLIC_ABSTRAXN_API_KEY || "your-api-key-here", ui: { onboardTitle: "Sign In", // Optional (default: 'Sign In') logo: "https://your-logo-url.com/logo.png", // Optional (default: no logo) theme: "light", // Optional: 'light' | 'dark' (default: 'light') showFooter: true, // Optional (default: true) showCloseButton: true, // Optional (default: false) closeOnBackdropClick: true, // Optional (default: true) authMethods: ["otp", "passkey", "google", "twitter", "discord"], // Optional (default: ['otp', 'google']) // Custom labels labels: { emailLabel: "Enter Email", // Optional (default: 'Email Address') emailPlaceholder: "Enter your email", // Optional (default: 'Enter your email address') otpLabel: "OTP", // Optional (default: 'OTP Code') otpPlaceholder: "Enter your OTP", // Optional (default: 'Enter 6-digit code') emailButton: "Continue", // Optional (default: 'Continue') otpButton: "Verify", // Optional (default: 'Verify') googleButton: "Continue with Google", // Optional (default: 'Continue with Google') }, // Custom colors colors: { primary: "#9333ea", primaryHover: "#7e22ce", }, // Custom CSS customCSS: ` .onboarding-button-primary { background-color: rgb(234, 51, 231); } .onboarding-card { border-radius: 20px; } `, }, }; // ... rest of your setup ``` ### Available UI Options #### Basic Options ```tsx ui: { onboardTitle: 'Sign In', // Optional (default: 'Sign In') logo: 'https://...', // Optional (default: no logo) theme: 'light', // Optional: 'light' | 'dark' (default: 'light') showFooter: true, // Optional (default: true) showCloseButton: true, // Optional (default: false) closeOnBackdropClick: true, // Optional (default: true) } ``` #### Authentication Methods ```tsx ui: { authMethods: ['otp', 'passkey', 'google', 'twitter', 'discord'], // Optional (default: ['otp', 'google']) } ``` Available methods: * `'otp'` - Email OTP authentication * `'passkey'` - Passkey/WebAuthn authentication * `'google'` - Google OAuth * `'discord'` - Discord OAuth * `'twitter'` - Twitter/X OAuth #### Custom Labels ```tsx ui: { labels: { emailLabel: 'Email Address', emailPlaceholder: 'Enter your email', otpLabel: 'OTP Code', otpPlaceholder: 'Enter 6-digit code', emailButton: 'Continue', otpButton: 'Verify', googleButton: 'Continue with Google', connectButton: 'Connect Wallet', disconnectButton: 'Disconnect', }, } ``` #### Custom Colors ```tsx ui: { colors: { primary: '#9333ea', // Primary color primaryHover: '#7e22ce', // Primary hover color background: '#ffffff', // Background color text: '#000000', // Text color border: '#e5e7eb', // Border color error: '#ef4444', // Error color success: '#10b981', // Success color }, } ``` #### Custom CSS ```tsx ui: { customCSS: ` .onboarding-button-primary { background-color: rgb(234, 51, 231) !important; } .onboarding-card { border-radius: 20px !important; } `, } ``` ### External Wallets Configuration To enable external wallet connections (MetaMask, WalletConnect, etc.): ```tsx const providerConfig: AbstraxnProviderConfig = { // ... other config externalWallets: { enabled: true, walletConnectProjectId: "your-walletconnect-project-id", connectors: ["injected", "metaMask", "walletConnect"], }, }; ``` ### Complete Example Here's a complete example with all customization options: ```tsx const providerConfig: AbstraxnProviderConfig = { apiKey: import.meta.env.VITE_ABSTRAXN_API_KEY || "your-api-key-here", ui: { onboardTitle: "Sign In", logo: "https://your-logo-url.com/logo.png", theme: "light", showFooter: true, showCloseButton: true, closeOnBackdropClick: true, authMethods: ["otp", "passkey", "google", "twitter", "discord"], labels: { emailLabel: "Enter Email", emailPlaceholder: "Enter your email", otpLabel: "OTP", otpPlaceholder: "Enter your OTP", emailButton: "Continue", otpButton: "Verify", googleButton: "Continue with Google", }, colors: { primary: "#9333ea", primaryHover: "#7e22ce", }, customCSS: ` .onboarding-button-primary { background-color: rgb(234, 51, 231); } .onboarding-card { border-radius: 20px; } `, }, externalWallets: { enabled: true, walletConnectProjectId: "your-project-id", connectors: ["injected", "metaMask", "walletConnect"], }, }; ``` ## How to use a Dynamic signer with Abstraxn Our comprehensive signer integration framework enables seamless custom signer deployment within the Abstraxn ecosystem. Dynamic represents a strategic embedded wallet provider that facilitates streamlined user onboarding workflows for decentralized applications. This implementation guide demonstrates how to leverage Dynamic as a sophisticated signer mechanism with Abstraxn, enabling comprehensive smart account creation, control, and transaction execution capabilities. After extensive evaluation of multiple wallet integration architectures, our team determined that Dynamic's embedded approach offers superior user experience optimization while maintaining robust security standards within account abstraction workflows. :::steps #### Strategic Dependency Installation Our comprehensive technical analysis identified the following dependencies as essential for optimal Dynamic integration within the Abstraxn platform: ```bash npm i @dynamic-labs/sdk-react-core @dynamic-labs/wagmi-connector @dynamic-labs/ethereum permissionless viem wagmi ``` #### Dynamic Provider Architecture Implementation Following Dynamic's established integration methodology, our framework requires strategic provider configuration within your application architecture. The DynamicWagmiConnector integration enables seamless Dynamic utilization as a signer mechanism with Abstraxn's account abstraction capabilities: ```ts import { DynamicContextProvider, DynamicWidget, } from "@dynamic-labs/sdk-react-core"; import { DynamicWagmiConnector } from "@dynamic-labs/wagmi-connector"; import { EthereumWalletConnectors } from "@dynamic-labs/ethereum"; export const App = () => { return ( ); }; ``` #### SmartAccountClient Architecture Development Our deliberative process involved comprehensive analysis of smart account client configuration approaches. The Dynamic signer integration supports multiple implementation strategies including strategic account type selection and advanced paymaster logic configuration. Note: DynamicWagmiConnector provides internal WagmiConfig setup, eliminating redundant configuration requirements. This implementation phase enables selection of preferred smart account architectures (Safe, Kernel, Biconomy, TrustWallet, SimpleAccount) and strategic paymaster integration approaches: ```ts import { createSmartAccountClient } from "permissionless"; import { toSimpleSmartAccount } from "permissionless/accounts"; import { useWalletClient } from "wagmi"; import { createPublicClient, http, zeroAddress } from "viem"; import { createPaymasterClient } from 'viem/account-abstraction' import { sepolia } from "viem/chains"; import { entryPoint07Address } from "viem/account-abstraction" const { data: walletClient } = useWalletClient() const publicClient = createPublicClient({ chain: sepolia, // or whatever chain you are using transport: http() }) const Abstraxn_Bundler_URL = `<_API_KEY>` export const paymasterClient = createPaymasterClient({ transport: http('https://paymaster.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}'), }) const simpleSmartAccount = await toSimpleSmartAccount({ owner: walletClient, client: publicClient, entryPoint: { address: entryPoint07Address, version: "0.7" } }) const smartAccountClient = createSmartAccountClient({ account: simpleSmartAccount, chain: sepolia, bundlerTransport: http({Abstraxn_Bundler_URL}), paymaster: paymasterClient, userOperation: { estimateFeesPerGas: async () => { return (await bundlerClient.getUserOperationGasPrice()).fast }, } }) ``` #### Strategic Transaction Execution Our transaction execution framework provides comprehensive User Operation broadcasting with integrated custom paymaster logic. The research team developed sophisticated sponsorship mechanisms that apply strategic paymaster operations before transaction signing and network propagation: ```ts const txHash = await smartAccountClient.sendTransaction({ to: zeroAddress, data: "0x", value: BigInt(0) }) ``` ::: The collaborative synthesis of Dynamic's embedded wallet capabilities with Abstraxn's account abstraction framework represents a strategic advancement in user onboarding and transaction management. Should additional integration requirements or architectural considerations emerge, we remain prepared to conduct further analysis and refine our Dynamic signer strategy to maintain optimal alignment with evolving ecosystem demands. ## How to use a Passkey (WebAuthn) signer This comprehensive implementation guide demonstrates strategic Passkey (WebAuthn) signer integration with smart accounts within the Abstraxn ecosystem, featuring optimized user operation relay and sponsorship capabilities. Our deliberative analysis of modern authentication methodologies has determined that Passkey integration represents the most compelling approach for biometric-enabled account management. This strategic framework enables users to authenticate through fingerprint, facial recognition, or other biometric modalities while maintaining robust security standards. :::info Passkey (WebAuthn) represents a strategic evolution in authentication technology, enabling users to authenticate with websites and applications through biometric information rather than traditional credential mechanisms. For comprehensive technical documentation on Passkey functionality, consult [the authentication specification](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API). ::: ### Implementation Framework ::::steps #### Strategic Package Installation Our comprehensive evaluation identified the following dependencies as essential for optimal Passkey integration within the Abstraxn platform: ```bash npm install viem permissionless ``` #### Credential Generation Architecture The research team developed a sophisticated credential management approach that balances security with user experience optimization: ```tsx import { useState } from "react" import { createWebAuthnCredential, } from "viem/account-abstraction" export function PasskeysDemo() { const [credential, setCredential] = useState(() => JSON.parse(localStorage.getItem("credential")) ) const createCredential = async () => { const credential = await createWebAuthnCredential({ name: "Wallet" }) localStorage.setItem("credential", JSON.stringify(credential)) setCredential(credential) } if (!credential) return ( ) return (

Credential: {credential.id}

) } ``` :::warning Our strategic recommendation emphasizes utilizing dedicated passkey server infrastructure to enable seamless credential synchronization across multiple devices. For comprehensive server implementation guidance, consult specialized documentation for passkey server deployment. Advanced implementation resources include [simplewebauthn](https://simplewebauthn.dev/docs/packages/server) for custom server development. Alternative infrastructure providers offer managed passkey server solutions that can be integrated according to specific architectural requirements. ::: #### Smart Account Architecture Integration After comprehensive evaluation of multiple smart account architectures, our team determined that Kernel Smart Account integration provides superior compatibility with Passkey authentication mechanisms: ```tsx import { type SmartAccountClient, createSmartAccountClient } from "permissionless" import { type ToKernelSmartAccountReturnType, toKernelSmartAccount } from "permissionless/accounts" import { entryPoint07Address, toWebAuthnAccount } from "viem/account-abstraction" const AbstraxnUrl = `https://paymaster.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={PAYMASTER_URL}` // [!code focus] export const paymasterClient = createPaymasterClient({ // [!code focus] transport: http('https://paymaster.abstraxn.com/api/v1/{CHAIN_ID}/?apikey={API_KEY}'), // [!code focus] }) // [!code focus] export function PasskeysDemo() { const [smartAccountClient, setSmartAccountClient] =// [!code focus] React.useState// [!code focus] >// [!code focus] >()// [!code focus] ... React.useEffect(() => { // [!code focus] if (!credential) return // [!code focus] toKernelSmartAccount({ // [!code focus] client: publicClient, // [!code focus] version: "0.3.1", // [!code focus] owners: [toWebAuthnAccount({ credential })], // [!code focus] entryPoint: { // [!code focus] address: entryPoint07Address, // [!code focus] version: "0.7" // [!code focus] } // [!code focus] }).then((account: ToKernelSmartAccountReturnType<"0.7">) => { // [!code focus] setSmartAccountClient( // [!code focus] createSmartAccountClient({ // [!code focus] account, // [!code focus] paymaster: paymasterClient, // [!code focus] chain, // [!code focus] userOperation: { // [!code focus] estimateFeesPerGas: async () => // [!code focus] (await paymasterClient.getUserOperationGasPrice()) // [!code focus] .fast // [!code focus] }, // [!code focus] bundlerTransport: http(AbstraxnUrl) // [!code focus] }) // [!code focus] ) // [!code focus] }) // [!code focus] }, [credential]) // [!code focus] ... } ``` :::info Our current technical architecture supports Passkey integration exclusively through kernel account implementations, representing the most robust foundation for biometric authentication workflows. ::: #### Strategic Transaction Execution Our transaction execution framework provides comprehensive User Operation broadcasting capabilities with integrated biometric authentication: ```tsx export function PasskeysDemo() { ... const [txHash, setTxHash] = React.useState() // [!code focus] const sendUserOperation = async ( // [!code focus] event: React.FormEvent // [!code focus] ) => { // [!code focus] event.preventDefault() // [!code focus] if (!smartAccountClient) return // [!code focus] const formData = new FormData(event.currentTarget) // [!code focus] const to = formData.get("to") as `0x${string}` // [!code focus] const value = formData.get("value") as string // [!code focus] const txHash = await smartAccountClient.sendTransaction({ // [!code focus] calls: [ // [!code focus] { // [!code focus] to, // [!code focus] value: parseEther(value) // [!code focus] } // [!code focus] ], // [!code focus] }) // [!code focus] setTxHash(txHash) // [!code focus] } // [!code focus] return ( // [!code focus] <> // [!code focus]

Account

// [!code focus]

Address: {smartAccountClient?.account?.address}

// [!code focus]

Send User Operation

// [!code focus]
// [!code focus] // [!code focus] // [!code focus] // [!code focus] {txHash &&

Transaction Hash: {txHash}

} // [!code focus]
// [!code focus] // [!code focus] ) // [!code focus] } ``` #### Advanced Message Authentication The platform supports sophisticated message signing and verification workflows, enabling comprehensive cryptographic operations through biometric authentication: ```tsx export function PasskeysDemo() { const [signature, setSignature] = React.useState() // [!code focus] const [isVerified, setIsVerified] = React.useState() // [!code focus] ... const signAndVerifyMessage = async () => { // [!code focus] if (!smartAccountClient) return // [!code focus] const signature = await smartAccountClient.signTypedData(typedData) // [!code focus] const isVerified = await publicClient.verifyTypedData({ // [!code focus] ...typedData, // [!code focus] address: smartAccountClient.account.address, // [!code focus] signature // [!code focus] }) // [!code focus] setIsVerified(isVerified) // [!code focus] setSignature(signature) // [!code focus] } // [!code focus] return ( // [!code focus] <> // [!code focus]

Account

// [!code focus]

Address: {smartAccountClient?.account?.address}

// [!code focus]

Sign typed data

// [!code focus] // [!code focus] {signature && ( // [!code focus]

// [!code focus] Signature:

{signature}
// [!code focus]

// [!code focus] )} // [!code focus] {isVerified !== undefined && ( // [!code focus]

Verified: {isVerified.toString()}

// [!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.